| 1 | /* |
|---|
| 2 | * ArcEmu MMORPG Server |
|---|
| 3 | * Copyright (C) 2005-2007 Ascent Team <http://www.ascentemu.com/> |
|---|
| 4 | * Copyright (C) 2008-2009 <http://www.ArcEmu.org/> |
|---|
| 5 | * |
|---|
| 6 | * This program is free software: you can redistribute it and/or modify |
|---|
| 7 | * it under the terms of the GNU Affero General Public License as published by |
|---|
| 8 | * the Free Software Foundation, either version 3 of the License, or |
|---|
| 9 | * any later version. |
|---|
| 10 | * |
|---|
| 11 | * This program is distributed in the hope that it will be useful, |
|---|
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 14 | * GNU Affero General Public License for more details. |
|---|
| 15 | * |
|---|
| 16 | * You should have received a copy of the GNU Affero General Public License |
|---|
| 17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 18 | * |
|---|
| 19 | */ |
|---|
| 20 | |
|---|
| 21 | #include "StdAfx.h" |
|---|
| 22 | |
|---|
| 23 | #ifndef UNIX |
|---|
| 24 | #include <cmath> |
|---|
| 25 | #endif |
|---|
| 26 | |
|---|
| 27 | |
|---|
| 28 | #ifdef WIN32 |
|---|
| 29 | #define HACKY_CRASH_FIXES 1 // SEH stuff |
|---|
| 30 | #endif |
|---|
| 31 | |
|---|
| 32 | AIInterface::AIInterface() |
|---|
| 33 | : |
|---|
| 34 | m_waypoints(NULL), |
|---|
| 35 | m_canMove(true), |
|---|
| 36 | m_destinationX(0), |
|---|
| 37 | m_destinationY(0), |
|---|
| 38 | m_destinationZ(0), |
|---|
| 39 | m_nextPosX(0), |
|---|
| 40 | m_nextPosY(0), |
|---|
| 41 | m_nextPosZ(0), |
|---|
| 42 | UnitToFollow(NULL), |
|---|
| 43 | FollowDistance(0.0f), |
|---|
| 44 | m_fallowAngle(float(M_PI/2)), |
|---|
| 45 | m_timeToMove(0), |
|---|
| 46 | m_timeMoved(0), |
|---|
| 47 | m_moveTimer(0), |
|---|
| 48 | m_WayPointsShowing(false), |
|---|
| 49 | m_WayPointsShowBackwards(false), |
|---|
| 50 | m_currentWaypoint(0), |
|---|
| 51 | m_moveBackward(false), |
|---|
| 52 | m_moveType(0), |
|---|
| 53 | m_moveRun(false), |
|---|
| 54 | m_moveSprint(false), |
|---|
| 55 | m_moveFly(false), |
|---|
| 56 | m_creatureState(STOPPED), |
|---|
| 57 | m_canCallForHelp(false), |
|---|
| 58 | m_hasCalledForHelp(false), |
|---|
| 59 | m_fleeTimer(0), |
|---|
| 60 | m_FleeDuration(0), |
|---|
| 61 | m_canFlee(false), |
|---|
| 62 | m_hasFleed(false), |
|---|
| 63 | m_canRangedAttack(false), |
|---|
| 64 | m_FleeHealth(0.0f), |
|---|
| 65 | m_CallForHelpHealth(0.0f), |
|---|
| 66 | m_AIState(STATE_IDLE), |
|---|
| 67 | |
|---|
| 68 | m_updateAssist(false), |
|---|
| 69 | m_updateTargets(false), |
|---|
| 70 | m_updateAssistTimer(1), |
|---|
| 71 | m_updateTargetsTimer(TARGET_UPDATE_INTERVAL_ON_PLAYER), |
|---|
| 72 | m_updateTargetsTimer2(0), |
|---|
| 73 | |
|---|
| 74 | m_nextSpell(NULL), |
|---|
| 75 | m_nextTarget(0), |
|---|
| 76 | totemspell(NULL), |
|---|
| 77 | m_Unit(NULL), |
|---|
| 78 | m_PetOwner(NULL), |
|---|
| 79 | m_aiCurrentAgent(AGENT_NULL), |
|---|
| 80 | m_runSpeed(0.0f), |
|---|
| 81 | m_flySpeed(0.0f), |
|---|
| 82 | UnitToFear(NULL), |
|---|
| 83 | m_outOfCombatRange(2500), // Where did u get this value? |
|---|
| 84 | |
|---|
| 85 | tauntedBy(NULL), |
|---|
| 86 | isTaunted(false), |
|---|
| 87 | soullinkedWith(NULL), |
|---|
| 88 | isSoulLinked(false), |
|---|
| 89 | m_AllowedToEnterCombat(true), |
|---|
| 90 | m_totalMoveTime(0), |
|---|
| 91 | m_lastFollowX(0), |
|---|
| 92 | m_lastFollowY(0), |
|---|
| 93 | m_FearTimer(0), |
|---|
| 94 | m_WanderTimer(0), |
|---|
| 95 | m_totemspelltime(0), |
|---|
| 96 | m_totemspelltimer(0), |
|---|
| 97 | m_formationFollowAngle(0.0f), |
|---|
| 98 | m_formationFollowDistance(0.0f), |
|---|
| 99 | m_formationLinkTarget(0), |
|---|
| 100 | m_formationLinkSqlId(0), |
|---|
| 101 | m_currentHighestThreat(0), |
|---|
| 102 | |
|---|
| 103 | disable_combat(false), |
|---|
| 104 | |
|---|
| 105 | disable_melee(false), |
|---|
| 106 | disable_ranged(false), |
|---|
| 107 | disable_spell(false), |
|---|
| 108 | |
|---|
| 109 | disable_targeting(false), |
|---|
| 110 | |
|---|
| 111 | next_spell_time(0), |
|---|
| 112 | waiting_for_cooldown(false), |
|---|
| 113 | UnitToFollow_backup(NULL), |
|---|
| 114 | m_isGuard(false), |
|---|
| 115 | m_isNeutralGuard(false), |
|---|
| 116 | m_is_in_instance(false), |
|---|
| 117 | skip_reset_hp(false), |
|---|
| 118 | timed_emotes(NULL), |
|---|
| 119 | timed_emote_expire(0xFFFFFFFF), |
|---|
| 120 | m_MovementState(MOVEMENTSTATE_STOP) |
|---|
| 121 | |
|---|
| 122 | #ifdef HACKY_SERVER_CLIENT_POS_SYNC |
|---|
| 123 | , |
|---|
| 124 | moved_for_attack(false) |
|---|
| 125 | #endif |
|---|
| 126 | { |
|---|
| 127 | m_aiTargets.clear(); |
|---|
| 128 | m_assistTargets.clear(); |
|---|
| 129 | m_spells.clear(); |
|---|
| 130 | } |
|---|
| 131 | |
|---|
| 132 | void AIInterface::EventAiInterfaceParamsetFinish() |
|---|
| 133 | { |
|---|
| 134 | if( timed_emotes && timed_emotes->begin() != timed_emotes->end() ) |
|---|
| 135 | { |
|---|
| 136 | next_timed_emote = timed_emotes->begin(); |
|---|
| 137 | timed_emote_expire = (*next_timed_emote)->expire_after; |
|---|
| 138 | } |
|---|
| 139 | } |
|---|
| 140 | |
|---|
| 141 | void AIInterface::Init(Unit *un, AIType at, MovementType mt) |
|---|
| 142 | { |
|---|
| 143 | ASSERT(at != AITYPE_PET); |
|---|
| 144 | |
|---|
| 145 | m_AIType = at; |
|---|
| 146 | m_MovementType = mt; |
|---|
| 147 | |
|---|
| 148 | m_AIState = STATE_IDLE; |
|---|
| 149 | m_MovementState = MOVEMENTSTATE_STOP; |
|---|
| 150 | |
|---|
| 151 | m_Unit = un; |
|---|
| 152 | |
|---|
| 153 | m_walkSpeed = m_Unit->m_walkSpeed*0.001f;//move distance per ms time |
|---|
| 154 | m_runSpeed = m_Unit->m_runSpeed*0.001f;//move distance per ms time |
|---|
| 155 | m_flySpeed = m_Unit->m_flySpeed * 0.001f; |
|---|
| 156 | /*if(!m_DefaultMeleeSpell) |
|---|
| 157 | { |
|---|
| 158 | m_DefaultMeleeSpell = new AI_Spell; |
|---|
| 159 | m_DefaultMeleeSpell->entryId = 0; |
|---|
| 160 | m_DefaultMeleeSpell->spellType = 0; |
|---|
| 161 | m_DefaultMeleeSpell->agent = AGENT_MELEE; |
|---|
| 162 | m_DefaultSpell = m_DefaultMeleeSpell; |
|---|
| 163 | }*/ |
|---|
| 164 | m_sourceX = un->GetPositionX(); |
|---|
| 165 | m_sourceY = un->GetPositionY(); |
|---|
| 166 | m_sourceZ = un->GetPositionZ(); |
|---|
| 167 | m_guardTimer = getMSTime(); |
|---|
| 168 | } |
|---|
| 169 | |
|---|
| 170 | AIInterface::~AIInterface() |
|---|
| 171 | { |
|---|
| 172 | for(list<AI_Spell*>::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) |
|---|
| 173 | delete (*itr); |
|---|
| 174 | m_spells.clear(); |
|---|
| 175 | } |
|---|
| 176 | |
|---|
| 177 | void AIInterface::Init(Unit *un, AIType at, MovementType mt, Unit *owner) |
|---|
| 178 | { |
|---|
| 179 | ASSERT(at == AITYPE_PET || at == AITYPE_TOTEM); |
|---|
| 180 | |
|---|
| 181 | m_AIType = at; |
|---|
| 182 | m_MovementType = mt; |
|---|
| 183 | |
|---|
| 184 | m_AIState = STATE_IDLE; |
|---|
| 185 | m_MovementState = MOVEMENTSTATE_STOP; |
|---|
| 186 | |
|---|
| 187 | m_Unit = un; |
|---|
| 188 | m_PetOwner = owner; |
|---|
| 189 | |
|---|
| 190 | m_walkSpeed = m_Unit->m_walkSpeed*0.001f;//move distance per ms time |
|---|
| 191 | m_runSpeed = m_Unit->m_runSpeed*0.001f;//move/ms |
|---|
| 192 | m_flySpeed = m_Unit->m_flySpeed*0.001f; |
|---|
| 193 | m_sourceX = un->GetPositionX(); |
|---|
| 194 | m_sourceY = un->GetPositionY(); |
|---|
| 195 | m_sourceZ = un->GetPositionZ(); |
|---|
| 196 | } |
|---|
| 197 | |
|---|
| 198 | void AIInterface::HandleEvent(uint32 event, Unit* pUnit, uint32 misc1) |
|---|
| 199 | { |
|---|
| 200 | if( m_Unit == NULL ) return; |
|---|
| 201 | |
|---|
| 202 | if(m_AIState != STATE_EVADE) |
|---|
| 203 | { |
|---|
| 204 | switch(event) |
|---|
| 205 | { |
|---|
| 206 | case EVENT_ENTERCOMBAT: |
|---|
| 207 | { |
|---|
| 208 | if( pUnit == NULL || pUnit->IsDead() || m_Unit->IsDead() ) return; |
|---|
| 209 | |
|---|
| 210 | // set the target first |
|---|
| 211 | if(pUnit && pUnit->GetInstanceID() == m_Unit->GetInstanceID()) |
|---|
| 212 | { |
|---|
| 213 | m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, pUnit->GetGUID()); |
|---|
| 214 | } |
|---|
| 215 | /* send the message */ |
|---|
| 216 | if( m_Unit->GetTypeId() == TYPEID_UNIT ) |
|---|
| 217 | { |
|---|
| 218 | if( static_cast< Creature* >( m_Unit )->has_combat_text ) |
|---|
| 219 | objmgr.HandleMonsterSayEvent( static_cast< Creature* >( m_Unit ), MONSTER_SAY_EVENT_ENTER_COMBAT ); |
|---|
| 220 | |
|---|
| 221 | CALL_SCRIPT_EVENT(m_Unit, OnCombatStart)(pUnit); |
|---|
| 222 | |
|---|
| 223 | if( static_cast< Creature* >( m_Unit )->m_spawn && ( static_cast< Creature* >( m_Unit )->m_spawn->channel_target_go || static_cast< Creature* >( m_Unit )->m_spawn->channel_target_creature)) |
|---|
| 224 | { |
|---|
| 225 | m_Unit->SetUInt32Value(UNIT_CHANNEL_SPELL, 0); |
|---|
| 226 | m_Unit->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, 0); |
|---|
| 227 | } |
|---|
| 228 | } |
|---|
| 229 | |
|---|
| 230 | // Stop the emote - change to fight emote |
|---|
| 231 | m_Unit->SetUInt32Value( UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY1H ); |
|---|
| 232 | m_returnX = m_Unit->GetPositionX(); |
|---|
| 233 | m_returnY = m_Unit->GetPositionY(); |
|---|
| 234 | m_returnZ = m_Unit->GetPositionZ(); |
|---|
| 235 | |
|---|
| 236 | m_moveRun = true; //run to the target |
|---|
| 237 | |
|---|
| 238 | // dismount if mounted |
|---|
| 239 | m_Unit->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0); |
|---|
| 240 | |
|---|
| 241 | if(m_AIState != STATE_ATTACKING) |
|---|
| 242 | StopMovement(0); |
|---|
| 243 | |
|---|
| 244 | m_AIState = STATE_ATTACKING; |
|---|
| 245 | if(m_Unit->GetMapMgr() && m_Unit->GetMapMgr()->GetMapInfo() && m_Unit->GetMapMgr()->GetMapInfo()->type == INSTANCE_RAID) |
|---|
| 246 | { |
|---|
| 247 | if(m_Unit->GetTypeId() == TYPEID_UNIT) |
|---|
| 248 | { |
|---|
| 249 | if(static_cast<Creature*>(m_Unit)->GetCreatureInfo() && static_cast<Creature*>(m_Unit)->GetCreatureInfo()->Rank == 3) |
|---|
| 250 | { |
|---|
| 251 | m_Unit->GetMapMgr()->AddCombatInProgress(m_Unit->GetGUID()); |
|---|
| 252 | } |
|---|
| 253 | } |
|---|
| 254 | } |
|---|
| 255 | if (pUnit->IsPlayer() && static_cast<Player*>(pUnit)->InGroup()) |
|---|
| 256 | { |
|---|
| 257 | m_Unit->GetAIInterface()->modThreatByPtr(pUnit, 1); |
|---|
| 258 | Group *pGroup = static_cast<Player*>(pUnit)->GetGroup(); |
|---|
| 259 | |
|---|
| 260 | Player *pGroupGuy; |
|---|
| 261 | GroupMembersSet::iterator itr; |
|---|
| 262 | pGroup->Lock(); |
|---|
| 263 | for(uint32 i = 0; i < pGroup->GetSubGroupCount(); i++) { |
|---|
| 264 | for(itr = pGroup->GetSubGroup(i)->GetGroupMembersBegin(); itr != pGroup->GetSubGroup(i)->GetGroupMembersEnd(); ++itr) |
|---|
| 265 | { |
|---|
| 266 | pGroupGuy = (*itr)->m_loggedInPlayer; |
|---|
| 267 | if( pGroupGuy && pGroupGuy->isAlive() && m_Unit->GetMapMgr() == pGroupGuy->GetMapMgr() && pGroupGuy->GetDistanceSq(pUnit) <= 40*40) //50 yards for now. lets see if it works |
|---|
| 268 | { |
|---|
| 269 | m_Unit->GetAIInterface()->AttackReaction(pGroupGuy, 1, 0); |
|---|
| 270 | } |
|---|
| 271 | } |
|---|
| 272 | } |
|---|
| 273 | pGroup->Unlock(); |
|---|
| 274 | } |
|---|
| 275 | //Zack : Put mob into combat animation. Take out weapons and start to look serious :P |
|---|
| 276 | m_Unit->smsg_AttackStart( pUnit ); |
|---|
| 277 | }break; |
|---|
| 278 | case EVENT_LEAVECOMBAT: |
|---|
| 279 | { |
|---|
| 280 | if( pUnit == NULL ) return; |
|---|
| 281 | |
|---|
| 282 | if( pUnit->IsCreature() ) |
|---|
| 283 | { |
|---|
| 284 | if( pUnit->IsDead() ) |
|---|
| 285 | pUnit->RemoveAllAuras(); |
|---|
| 286 | else |
|---|
| 287 | pUnit->RemoveNegativeAuras(); |
|---|
| 288 | } |
|---|
| 289 | |
|---|
| 290 | Unit* target = NULL; |
|---|
| 291 | if (m_Unit->GetMapMgr() && m_Unit->GetMapMgr()->GetMapInfo()) |
|---|
| 292 | { |
|---|
| 293 | switch (m_Unit->GetMapMgr()->GetMapInfo()->type) |
|---|
| 294 | { |
|---|
| 295 | case INSTANCE_NULL: |
|---|
| 296 | case INSTANCE_BATTLEGROUND: |
|---|
| 297 | if (m_outOfCombatRange && _CalcDistanceFromHome() < m_outOfCombatRange) |
|---|
| 298 | target = FindTarget(); |
|---|
| 299 | break; |
|---|
| 300 | |
|---|
| 301 | case INSTANCE_RAID: |
|---|
| 302 | case INSTANCE_NONRAID: |
|---|
| 303 | case INSTANCE_ARENA: |
|---|
| 304 | target = FindTarget(); |
|---|
| 305 | break; |
|---|
| 306 | } |
|---|
| 307 | |
|---|
| 308 | if(target != NULL) |
|---|
| 309 | { |
|---|
| 310 | AttackReaction(target, 1, 0); |
|---|
| 311 | return; |
|---|
| 312 | } |
|---|
| 313 | } |
|---|
| 314 | |
|---|
| 315 | //cancel spells that we are casting. Should remove bug where creatures cast a spell after they died |
|---|
| 316 | // CancelSpellCast(); |
|---|
| 317 | // restart emote |
|---|
| 318 | if(m_Unit->GetTypeId() == TYPEID_UNIT) |
|---|
| 319 | { |
|---|
| 320 | if( static_cast< Creature* >( m_Unit )->has_combat_text ) |
|---|
| 321 | objmgr.HandleMonsterSayEvent( static_cast< Creature* >( m_Unit ), MONSTER_SAY_EVENT_ON_COMBAT_STOP ); |
|---|
| 322 | |
|---|
| 323 | if( static_cast< Creature* >( m_Unit )->original_emotestate ) |
|---|
| 324 | m_Unit->SetUInt32Value( UNIT_NPC_EMOTESTATE, static_cast< Creature* >( m_Unit )->original_emotestate ); |
|---|
| 325 | else |
|---|
| 326 | m_Unit->SetUInt32Value( UNIT_NPC_EMOTESTATE, 0 ); |
|---|
| 327 | |
|---|
| 328 | if(static_cast<Creature*>(m_Unit)->m_spawn && (static_cast< Creature* >( m_Unit )->m_spawn->channel_target_go || static_cast< Creature* >( m_Unit )->m_spawn->channel_target_creature ) ) |
|---|
| 329 | { |
|---|
| 330 | if(static_cast<Creature*>(m_Unit)->m_spawn->channel_target_go) |
|---|
| 331 | sEventMgr.AddEvent( static_cast< Creature* >( m_Unit ), &Creature::ChannelLinkUpGO, static_cast< Creature* >( m_Unit )->m_spawn->channel_target_go, EVENT_CREATURE_CHANNEL_LINKUP, 1000, 5, 0 ); |
|---|
| 332 | |
|---|
| 333 | if(static_cast<Creature*>(m_Unit)->m_spawn->channel_target_creature) |
|---|
| 334 | sEventMgr.AddEvent( static_cast< Creature* >( m_Unit ), &Creature::ChannelLinkUpCreature, static_cast< Creature* >( m_Unit )->m_spawn->channel_target_creature, EVENT_CREATURE_CHANNEL_LINKUP, 1000, 5, 0 ); |
|---|
| 335 | } |
|---|
| 336 | } |
|---|
| 337 | |
|---|
| 338 | //reset ProcCount |
|---|
| 339 | //ResetProcCounts(); |
|---|
| 340 | m_moveSprint = true; |
|---|
| 341 | LockAITargets(true); |
|---|
| 342 | m_aiTargets.clear(); |
|---|
| 343 | LockAITargets(false); |
|---|
| 344 | m_fleeTimer = 0; |
|---|
| 345 | m_hasFleed = false; |
|---|
| 346 | m_hasCalledForHelp = false; |
|---|
| 347 | m_nextSpell = NULL; |
|---|
| 348 | SetNextTarget( (Unit*)NULL ); |
|---|
| 349 | m_Unit->CombatStatus.Vanished(); |
|---|
| 350 | |
|---|
| 351 | if(m_AIType == AITYPE_PET) |
|---|
| 352 | { |
|---|
| 353 | m_AIState = STATE_FOLLOWING; |
|---|
| 354 | UnitToFollow = m_PetOwner; |
|---|
| 355 | FollowDistance = 3.0f; |
|---|
| 356 | m_lastFollowX = m_lastFollowY = 0; |
|---|
| 357 | if( m_Unit->IsPet() ) |
|---|
| 358 | { |
|---|
| 359 | static_cast< Pet* >( m_Unit )->SetPetAction( PET_ACTION_FOLLOW ); |
|---|
| 360 | if( m_Unit->isAlive() && m_Unit->IsInWorld() ) |
|---|
| 361 | { |
|---|
| 362 | static_cast< Pet* >( m_Unit )->HandleAutoCastEvent( AUTOCAST_EVENT_LEAVE_COMBAT ); |
|---|
| 363 | } |
|---|
| 364 | } |
|---|
| 365 | HandleEvent(EVENT_FOLLOWOWNER, 0, 0); |
|---|
| 366 | } |
|---|
| 367 | else |
|---|
| 368 | { |
|---|
| 369 | m_AIState = STATE_EVADE; |
|---|
| 370 | |
|---|
| 371 | Unit* SavedFollow = UnitToFollow; |
|---|
| 372 | UnitToFollow = NULL; |
|---|
| 373 | FollowDistance = 0.0f; |
|---|
| 374 | m_lastFollowX = m_lastFollowY = 0; |
|---|
| 375 | |
|---|
| 376 | if(m_Unit->isAlive()) |
|---|
| 377 | { |
|---|
| 378 | if(m_returnX != 0.0f && m_returnY != 0.0f && m_returnZ != 0.0f) |
|---|
| 379 | MoveTo(m_returnX,m_returnY,m_returnZ,m_Unit->GetSpawnO()); |
|---|
| 380 | else |
|---|
| 381 | { |
|---|
| 382 | MoveTo(m_Unit->GetSpawnX(),m_Unit->GetSpawnY(),m_Unit->GetSpawnZ(),m_Unit->GetSpawnO()); |
|---|
| 383 | m_returnX=m_Unit->GetSpawnX(); |
|---|
| 384 | m_returnY=m_Unit->GetSpawnY(); |
|---|
| 385 | m_returnZ=m_Unit->GetSpawnZ(); |
|---|
| 386 | } |
|---|
| 387 | |
|---|
| 388 | Creature *aiowner = static_cast<Creature*>(m_Unit); |
|---|
| 389 | //clear tagger. |
|---|
| 390 | aiowner->Tagged = false; |
|---|
| 391 | aiowner->TaggerGuid = 0; |
|---|
| 392 | aiowner->SetUInt32Value(UNIT_DYNAMIC_FLAGS,aiowner->GetUInt32Value(UNIT_DYNAMIC_FLAGS) & ~(U_DYN_FLAG_TAGGED_BY_OTHER |U_DYN_FLAG_LOOTABLE)); |
|---|
| 393 | aiowner->m_lootMethod = -1; |
|---|
| 394 | } |
|---|
| 395 | CALL_SCRIPT_EVENT(m_Unit, OnCombatStop)(SavedFollow); |
|---|
| 396 | } |
|---|
| 397 | |
|---|
| 398 | if(m_Unit->GetMapMgr() && m_Unit->GetMapMgr()->GetMapInfo() && m_Unit->GetMapMgr()->GetMapInfo()->type == INSTANCE_RAID) |
|---|
| 399 | { |
|---|
| 400 | if(m_Unit->GetTypeId() == TYPEID_UNIT) |
|---|
| 401 | { |
|---|
| 402 | if(static_cast<Creature*>(m_Unit)->GetCreatureInfo() && static_cast<Creature*>(m_Unit)->GetCreatureInfo()->Rank == 3) |
|---|
| 403 | { |
|---|
| 404 | m_Unit->GetMapMgr()->RemoveCombatInProgress(m_Unit->GetGUID()); |
|---|
| 405 | } |
|---|
| 406 | } |
|---|
| 407 | } |
|---|
| 408 | |
|---|
| 409 | // Remount if mounted |
|---|
| 410 | if(m_Unit->GetTypeId() == TYPEID_UNIT) |
|---|
| 411 | { |
|---|
| 412 | Creature *creature = static_cast< Creature* >( m_Unit ); |
|---|
| 413 | if( creature->GetProto() && creature->m_spawn ) |
|---|
| 414 | m_Unit->SetUInt32Value( UNIT_FIELD_MOUNTDISPLAYID, creature->m_spawn->MountedDisplayID ); |
|---|
| 415 | //m_Unit->SetUInt32Value( UNIT_FIELD_MOUNTDISPLAYID, static_cast< Creature* >( m_Unit )->GetSpawnO->MountedDisplayID ); |
|---|
| 416 | } |
|---|
| 417 | //Zack : not sure we need to send this. Did not see it in the dumps since mob died eventually but it seems logical to make this |
|---|
| 418 | m_Unit->smsg_AttackStop( pUnit ); |
|---|
| 419 | }break; |
|---|
| 420 | case EVENT_DAMAGETAKEN: |
|---|
| 421 | { |
|---|
| 422 | if( pUnit == NULL ) return; |
|---|
| 423 | |
|---|
| 424 | if( static_cast< Creature* >( m_Unit )->has_combat_text ) |
|---|
| 425 | objmgr.HandleMonsterSayEvent( static_cast< Creature* >( m_Unit ), MONSTER_SAY_EVENT_ON_DAMAGE_TAKEN ); |
|---|
| 426 | |
|---|
| 427 | if( pUnit->HasAura(24575) ) |
|---|
| 428 | pUnit->RemoveAura(24575); |
|---|
| 429 | |
|---|
| 430 | CALL_SCRIPT_EVENT(m_Unit, OnDamageTaken)(pUnit, float(misc1)); |
|---|
| 431 | if(!modThreatByPtr(pUnit, misc1)) |
|---|
| 432 | { |
|---|
| 433 | m_aiTargets.insert(TargetMap::value_type(pUnit->GetGUID(), misc1)); |
|---|
| 434 | } |
|---|
| 435 | pUnit->CombatStatus.OnDamageDealt( m_Unit ); |
|---|
| 436 | }break; |
|---|
| 437 | case EVENT_FOLLOWOWNER: |
|---|
| 438 | { |
|---|
| 439 | m_AIState = STATE_FOLLOWING; |
|---|
| 440 | if(m_Unit->IsPet()) |
|---|
| 441 | ((Pet*)m_Unit)->SetPetAction(PET_ACTION_FOLLOW); |
|---|
| 442 | UnitToFollow = m_PetOwner; |
|---|
| 443 | m_lastFollowX = m_lastFollowY = 0; |
|---|
| 444 | FollowDistance = 4.0f; |
|---|
| 445 | |
|---|
| 446 | LockAITargets(true); |
|---|
| 447 | m_aiTargets.clear(); |
|---|
| 448 | LockAITargets(false); |
|---|
| 449 | m_fleeTimer = 0; |
|---|
| 450 | m_hasFleed = false; |
|---|
| 451 | m_hasCalledForHelp = false; |
|---|
| 452 | m_nextSpell = NULL; |
|---|
| 453 | SetNextTarget( (Unit*)NULL ); |
|---|
| 454 | m_moveRun = true; |
|---|
| 455 | }break; |
|---|
| 456 | |
|---|
| 457 | case EVENT_FEAR: |
|---|
| 458 | { |
|---|
| 459 | if( pUnit == NULL ) return; |
|---|
| 460 | |
|---|
| 461 | m_FearTimer = 0; |
|---|
| 462 | SetUnitToFear(pUnit); |
|---|
| 463 | |
|---|
| 464 | CALL_SCRIPT_EVENT(m_Unit, OnFear)(pUnit, 0); |
|---|
| 465 | m_AIState = STATE_FEAR; |
|---|
| 466 | StopMovement(1); |
|---|
| 467 | |
|---|
| 468 | UnitToFollow_backup = UnitToFollow; |
|---|
| 469 | UnitToFollow = NULL; |
|---|
| 470 | m_lastFollowX = m_lastFollowY = 0; |
|---|
| 471 | FollowDistance_backup = FollowDistance; |
|---|
| 472 | FollowDistance = 0.0f; |
|---|
| 473 | |
|---|
| 474 | LockAITargets(true); |
|---|
| 475 | m_aiTargets.clear(); // we'll get a new target after we are unfeared |
|---|
| 476 | LockAITargets(false); |
|---|
| 477 | m_fleeTimer = 0; |
|---|
| 478 | m_hasFleed = false; |
|---|
| 479 | m_hasCalledForHelp = false; |
|---|
| 480 | |
|---|
| 481 | // update speed |
|---|
| 482 | m_moveRun = true; |
|---|
| 483 | getMoveFlags(); |
|---|
| 484 | |
|---|
| 485 | SetNextSpell( NULL ); |
|---|
| 486 | SetNextTarget( (Unit*)NULL); |
|---|
| 487 | }break; |
|---|
| 488 | |
|---|
| 489 | case EVENT_UNFEAR: |
|---|
| 490 | { |
|---|
| 491 | UnitToFollow = UnitToFollow_backup; |
|---|
| 492 | FollowDistance = FollowDistance_backup; |
|---|
| 493 | m_AIState = STATE_IDLE; // we need this to prevent permanent fear, wander, and other problems |
|---|
| 494 | |
|---|
| 495 | SetUnitToFear(NULL); |
|---|
| 496 | StopMovement(1); |
|---|
| 497 | }break; |
|---|
| 498 | |
|---|
| 499 | case EVENT_WANDER: |
|---|
| 500 | { |
|---|
| 501 | if( pUnit == NULL ) return; |
|---|
| 502 | |
|---|
| 503 | m_WanderTimer = 0; |
|---|
| 504 | |
|---|
| 505 | //CALL_SCRIPT_EVENT(m_Unit, OnWander)(pUnit, 0); FIX ME |
|---|
| 506 | m_AIState = STATE_WANDER; |
|---|
| 507 | StopMovement(1); |
|---|
| 508 | |
|---|
| 509 | UnitToFollow_backup = UnitToFollow; |
|---|
| 510 | UnitToFollow = NULL; |
|---|
| 511 | m_lastFollowX = m_lastFollowY = 0; |
|---|
| 512 | FollowDistance_backup = FollowDistance; |
|---|
| 513 | FollowDistance = 0.0f; |
|---|
| 514 | |
|---|
| 515 | LockAITargets(true); |
|---|
| 516 | m_aiTargets.clear(); // we'll get a new target after we are unwandered |
|---|
| 517 | LockAITargets(false); |
|---|
| 518 | m_fleeTimer = 0; |
|---|
| 519 | m_hasFleed = false; |
|---|
| 520 | m_hasCalledForHelp = false; |
|---|
| 521 | |
|---|
| 522 | // update speed |
|---|
| 523 | m_moveRun = true; |
|---|
| 524 | getMoveFlags(); |
|---|
| 525 | |
|---|
| 526 | SetNextSpell(NULL); |
|---|
| 527 | SetNextTarget( (Unit*)NULL ); |
|---|
| 528 | }break; |
|---|
| 529 | |
|---|
| 530 | case EVENT_UNWANDER: |
|---|
| 531 | { |
|---|
| 532 | UnitToFollow = UnitToFollow_backup; |
|---|
| 533 | FollowDistance = FollowDistance_backup; |
|---|
| 534 | m_AIState = STATE_IDLE; // we need this to prevent permanent fear, wander, and other problems |
|---|
| 535 | |
|---|
| 536 | StopMovement(1); |
|---|
| 537 | }break; |
|---|
| 538 | |
|---|
| 539 | default: |
|---|
| 540 | { |
|---|
| 541 | }break; |
|---|
| 542 | } |
|---|
| 543 | } |
|---|
| 544 | |
|---|
| 545 | //Should be able to do this stuff even when evading |
|---|
| 546 | switch(event) |
|---|
| 547 | { |
|---|
| 548 | case EVENT_UNITDIED: |
|---|
| 549 | { |
|---|
| 550 | if( pUnit == NULL ) return; |
|---|
| 551 | |
|---|
| 552 | if( static_cast< Creature* >( m_Unit )->has_combat_text ) |
|---|
| 553 | objmgr.HandleMonsterSayEvent( static_cast< Creature* >( m_Unit ), MONSTER_SAY_EVENT_ON_DIED ); |
|---|
| 554 | |
|---|
| 555 | CALL_SCRIPT_EVENT(m_Unit, OnDied)(pUnit); |
|---|
| 556 | if ( m_Unit->IsCreature() ) |
|---|
| 557 | CALL_INSTANCE_SCRIPT_EVENT( m_Unit->GetMapMgr(), OnCreatureDeath )( TO_CREATURE( m_Unit ), pUnit ); |
|---|
| 558 | |
|---|
| 559 | m_AIState = STATE_IDLE; |
|---|
| 560 | |
|---|
| 561 | StopMovement(0); |
|---|
| 562 | LockAITargets(true); |
|---|
| 563 | m_aiTargets.clear(); |
|---|
| 564 | LockAITargets(false); |
|---|
| 565 | UnitToFollow = NULL; |
|---|
| 566 | m_lastFollowX = m_lastFollowY = 0; |
|---|
| 567 | UnitToFear = NULL; |
|---|
| 568 | FollowDistance = 0.0f; |
|---|
| 569 | m_fleeTimer = 0; |
|---|
| 570 | m_hasFleed = false; |
|---|
| 571 | m_hasCalledForHelp = false; |
|---|
| 572 | m_nextSpell = NULL; |
|---|
| 573 | |
|---|
| 574 | SetNextTarget( (Unit*)NULL ); |
|---|
| 575 | //reset ProcCount |
|---|
| 576 | //ResetProcCounts(); |
|---|
| 577 | |
|---|
| 578 | //reset waypoint to 0 |
|---|
| 579 | m_currentWaypoint = 0; |
|---|
| 580 | |
|---|
| 581 | // There isn't any need to do any attacker checks here, as |
|---|
| 582 | // they should all be taken care of in DealDamage |
|---|
| 583 | |
|---|
| 584 | //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 |
|---|
| 585 | /* if(m_AIType == AITYPE_PET) |
|---|
| 586 | { |
|---|
| 587 | SetUnitToFollow(m_PetOwner); |
|---|
| 588 | SetFollowDistance(3.0f); |
|---|
| 589 | HandleEvent(EVENT_FOLLOWOWNER, m_Unit, 0); |
|---|
| 590 | }*/ |
|---|
| 591 | |
|---|
| 592 | Instance *pInstance = NULL; |
|---|
| 593 | if( m_Unit->GetMapMgr() ) |
|---|
| 594 | pInstance = m_Unit->GetMapMgr()->pInstance; |
|---|
| 595 | |
|---|
| 596 | if(m_Unit->GetMapMgr() |
|---|
| 597 | && m_Unit->GetTypeId() == TYPEID_UNIT |
|---|
| 598 | && !m_Unit->IsPet() |
|---|
| 599 | && pInstance |
|---|
| 600 | && (pInstance->m_mapInfo->type == INSTANCE_RAID |
|---|
| 601 | || pInstance->m_mapInfo->type == INSTANCE_NONRAID |
|---|
| 602 | || pInstance->m_mapInfo->type == INSTANCE_ARENA )) |
|---|
| 603 | { |
|---|
| 604 | InstanceBossInfoMap *bossInfoMap = objmgr.m_InstanceBossInfoMap[m_Unit->GetMapMgr()->GetMapId()]; |
|---|
| 605 | Creature *pCreature = static_cast< Creature* >( m_Unit ); |
|---|
| 606 | bool found = false; |
|---|
| 607 | |
|---|
| 608 | if(IS_PERSISTENT_INSTANCE(pInstance) && bossInfoMap != NULL && pCreature->GetProto()) |
|---|
| 609 | { |
|---|
| 610 | uint32 npcGuid = pCreature->GetProto()->Id; |
|---|
| 611 | InstanceBossInfoMap::const_iterator bossInfo = bossInfoMap->find(npcGuid); |
|---|
| 612 | if(bossInfo != bossInfoMap->end()) |
|---|
| 613 | { |
|---|
| 614 | found = true; |
|---|
| 615 | m_Unit->GetMapMgr()->pInstance->m_killedNpcs.insert( npcGuid ); |
|---|
| 616 | m_Unit->GetMapMgr()->pInstance->SaveToDB(); |
|---|
| 617 | for(InstanceBossTrashList::iterator trash = bossInfo->second->trash.begin(); trash != bossInfo->second->trash.end(); ++trash) |
|---|
| 618 | { |
|---|
| 619 | Creature *c = m_Unit->GetMapMgr()->GetSqlIdCreature((*trash)); |
|---|
| 620 | if(c != NULL) |
|---|
| 621 | c->m_noRespawn = true; |
|---|
| 622 | } |
|---|
| 623 | if(!pInstance->m_persistent) |
|---|
| 624 | { |
|---|
| 625 | pInstance->m_persistent = true; |
|---|
| 626 | pInstance->SaveToDB(); |
|---|
| 627 | for(PlayerStorageMap::iterator itr = m_Unit->GetMapMgr()->m_PlayerStorage.begin(); itr != m_Unit->GetMapMgr()->m_PlayerStorage.end(); ++itr) |
|---|
| 628 | { |
|---|
| 629 | (*itr).second->SetPersistentInstanceId(pInstance); |
|---|
| 630 | } |
|---|
| 631 | } |
|---|
| 632 | } |
|---|
| 633 | } |
|---|
| 634 | |
|---|
| 635 | if (found == false) { |
|---|
| 636 | // No instance boss information ... so fallback ... |
|---|
| 637 | uint32 npcGuid = pCreature->GetSQL_id(); |
|---|
| 638 | m_Unit->GetMapMgr()->pInstance->m_killedNpcs.insert( npcGuid ); |
|---|
| 639 | m_Unit->GetMapMgr()->pInstance->SaveToDB(); |
|---|
| 640 | } |
|---|
| 641 | } |
|---|
| 642 | if(m_Unit->GetMapMgr() && m_Unit->GetMapMgr()->GetMapInfo() && m_Unit->GetMapMgr()->GetMapInfo()->type == INSTANCE_RAID) |
|---|
| 643 | { |
|---|
| 644 | if(m_Unit->GetTypeId() == TYPEID_UNIT) |
|---|
| 645 | { |
|---|
| 646 | if(static_cast<Creature*>(m_Unit)->GetCreatureInfo() && static_cast<Creature*>(m_Unit)->GetCreatureInfo()->Rank == 3) |
|---|
| 647 | { |
|---|
| 648 | m_Unit->GetMapMgr()->RemoveCombatInProgress(m_Unit->GetGUID()); |
|---|
| 649 | } |
|---|
| 650 | } |
|---|
| 651 | } |
|---|
| 652 | |
|---|
| 653 | |
|---|
| 654 | //remove negative auras |
|---|
| 655 | //if( m_Unit->IsCreature() ) |
|---|
| 656 | // m_Unit->RemoveNegativeAuras(); |
|---|
| 657 | |
|---|
| 658 | }break; |
|---|
| 659 | } |
|---|
| 660 | } |
|---|
| 661 | |
|---|
| 662 | void AIInterface::Update(uint32 p_time) |
|---|
| 663 | { |
|---|
| 664 | float tdist; |
|---|
| 665 | if(m_AIType == AITYPE_TOTEM) |
|---|
| 666 | { |
|---|
| 667 | assert(totemspell != 0); |
|---|
| 668 | if(p_time >= m_totemspelltimer) |
|---|
| 669 | { |
|---|
| 670 | Spell *pSpell = new Spell(m_Unit, totemspell, true, 0); |
|---|
| 671 | if (!pSpell) |
|---|
| 672 | return; |
|---|
| 673 | SpellCastTargets targets(0); |
|---|
| 674 | if(!GetNextTarget() || |
|---|
| 675 | (GetNextTarget() && |
|---|
| 676 | (!m_Unit->GetMapMgr()->GetUnit(GetNextTarget()->GetGUID()) || |
|---|
| 677 | !GetNextTarget()->isAlive() || |
|---|
| 678 | !IsInrange(m_Unit,GetNextTarget(),pSpell->GetProto()->base_range_or_radius_sqr) || |
|---|
| 679 | !isAttackable(m_Unit, GetNextTarget(),!(pSpell->GetProto()->c_is_flags & SPELL_FLAG_IS_TARGETINGSTEALTHED)) |
|---|
| 680 | ) |
|---|
| 681 | ) |
|---|
| 682 | ) |
|---|
| 683 | { |
|---|
| 684 | //we set no target and see if we managed to fid a new one |
|---|
| 685 | SetNextTarget( (Unit*)NULL ); |
|---|
| 686 | //something happened to our target, pick another one |
|---|
| 687 | pSpell->GenerateTargets(&targets); |
|---|
| 688 | if(targets.m_targetMask & TARGET_FLAG_UNIT) |
|---|
| 689 | SetNextTarget( targets.m_unitTarget ); |
|---|
| 690 | } |
|---|
| 691 | if(GetNextTarget()) |
|---|
| 692 | { |
|---|
| 693 | SpellCastTargets targets(GetNextTarget()->GetGUID()); |
|---|
| 694 | pSpell->prepare(&targets); |
|---|
| 695 | // need proper cooldown time! |
|---|
| 696 | m_totemspelltimer = m_totemspelltime; |
|---|
| 697 | } |
|---|
| 698 | else |
|---|
| 699 | { |
|---|
| 700 | delete pSpell; |
|---|
| 701 | pSpell = NULL; |
|---|
| 702 | } |
|---|
| 703 | // these will *almost always* be AoE, so no need to find a target here. |
|---|
| 704 | // SpellCastTargets targets(m_Unit->GetGUID()); |
|---|
| 705 | // Spell * pSpell = new Spell(m_Unit, totemspell, true, 0); |
|---|
| 706 | // pSpell->prepare(&targets); |
|---|
| 707 | // need proper cooldown time! |
|---|
| 708 | // m_totemspelltimer = m_totemspelltime; |
|---|
| 709 | } |
|---|
| 710 | else |
|---|
| 711 | { |
|---|
| 712 | m_totemspelltimer -= p_time; |
|---|
| 713 | } |
|---|
| 714 | return; |
|---|
| 715 | } |
|---|
| 716 | |
|---|
| 717 | _UpdateTimer(p_time); |
|---|
| 718 | _UpdateTargets(); |
|---|
| 719 | if(m_Unit->isAlive() && m_AIState != STATE_IDLE |
|---|
| 720 | && m_AIState != STATE_FOLLOWING && m_AIState != STATE_FEAR |
|---|
| 721 | && m_AIState != STATE_WANDER && m_AIState != STATE_SCRIPTMOVE) |
|---|
| 722 | { |
|---|
| 723 | if(m_AIType == AITYPE_PET ) |
|---|
| 724 | { |
|---|
| 725 | if(!m_Unit->bInvincible && m_Unit->IsPet()) |
|---|
| 726 | { |
|---|
| 727 | Pet * pPet = static_cast<Pet*>(m_Unit); |
|---|
| 728 | |
|---|
| 729 | if(pPet->GetPetAction() == PET_ACTION_ATTACK || pPet->GetPetState() != PET_STATE_PASSIVE) |
|---|
| 730 | { |
|---|
| 731 | _UpdateCombat(p_time); |
|---|
| 732 | } |
|---|
| 733 | } |
|---|
| 734 | //we just use any creature as a pet guardian |
|---|
| 735 | else if(!m_Unit->IsPet()) |
|---|
| 736 | { |
|---|
| 737 | _UpdateCombat(p_time); |
|---|
| 738 | } |
|---|
| 739 | } |
|---|
| 740 | else |
|---|
| 741 | { |
|---|
| 742 | _UpdateCombat(p_time); |
|---|
| 743 | } |
|---|
| 744 | } |
|---|
| 745 | |
|---|
| 746 | _UpdateMovement(p_time); |
|---|
| 747 | if(m_AIState==STATE_EVADE) |
|---|
| 748 | { |
|---|
| 749 | tdist = m_Unit->GetDistanceSq(m_returnX,m_returnY,m_returnZ); |
|---|
| 750 | if(tdist <= 4.0f/*2.0*/) |
|---|
| 751 | { |
|---|
| 752 | m_AIState = STATE_IDLE; |
|---|
| 753 | m_returnX = m_returnY = m_returnZ = 0.0f; |
|---|
| 754 | m_moveRun = false; |
|---|
| 755 | //removed by zack : in scripted events if we keep reducing this it will bug the world out ! |
|---|
| 756 | //On Blizz it will return to previous wp but we can accept the fact that it will move on to next one |
|---|
| 757 | /* |
|---|
| 758 | if(hasWaypoints()) |
|---|
| 759 | { |
|---|
| 760 | if(m_moveBackward) |
|---|
| 761 | { |
|---|
| 762 | if(m_currentWaypoint != GetWayPointsCount()-1) |
|---|
| 763 | m_currentWaypoint++; |
|---|
| 764 | } |
|---|
| 765 | else |
|---|
| 766 | { |
|---|
| 767 | if(m_currentWaypoint != 0) |
|---|
| 768 | m_currentWaypoint--; |
|---|
| 769 | } |
|---|
| 770 | } |
|---|
| 771 | */ |
|---|
| 772 | // Set health to full if they at there last location before attacking |
|---|
| 773 | if(m_AIType != AITYPE_PET&&!skip_reset_hp) |
|---|
| 774 | m_Unit->SetUInt32Value(UNIT_FIELD_HEALTH,m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH)); |
|---|
| 775 | } |
|---|
| 776 | else |
|---|
| 777 | { |
|---|
| 778 | if( m_creatureState == STOPPED ) |
|---|
| 779 | { |
|---|
| 780 | // return to the home |
|---|
| 781 | if( m_returnX == 0.0f && m_returnY == 0.0f ) |
|---|
| 782 | { |
|---|
| 783 | m_returnX = m_Unit->GetSpawnX(); |
|---|
| 784 | m_returnY = m_Unit->GetSpawnY(); |
|---|
| 785 | m_returnZ = m_Unit->GetSpawnZ(); |
|---|
| 786 | } |
|---|
| 787 | |
|---|
| 788 | MoveTo(m_returnX, m_returnY, m_returnZ, m_Unit->GetSpawnO()); |
|---|
| 789 | } |
|---|
| 790 | } |
|---|
| 791 | } |
|---|
| 792 | |
|---|
| 793 | if(m_fleeTimer) |
|---|
| 794 | { |
|---|
| 795 | if(m_fleeTimer > p_time) |
|---|
| 796 | { |
|---|
| 797 | m_fleeTimer -= p_time; |
|---|
| 798 | _CalcDestinationAndMove(GetNextTarget(), 5.0f); |
|---|
| 799 | } |
|---|
| 800 | else |
|---|
| 801 | { |
|---|
| 802 | m_fleeTimer = 0; |
|---|
| 803 | SetNextTarget(FindTargetForSpell(m_nextSpell)); |
|---|
| 804 | } |
|---|
| 805 | } |
|---|
| 806 | |
|---|
| 807 | //Pet Dismiss after a certain distance away |
|---|
| 808 | /*if(m_AIType == AITYPE_PET && m_PetOwner != NULL) |
|---|
| 809 | { |
|---|
| 810 | float dist = (m_Unit->GetInstanceID() == m_PetOwner->GetInstanceID()) ? |
|---|
| 811 | m_Unit->GetDistanceSq(m_PetOwner) : 99999.0f; |
|---|
| 812 | |
|---|
| 813 | if(dist > 8100.0f) //90 yard away we Dismissed |
|---|
| 814 | { |
|---|
| 815 | DismissPet(); |
|---|
| 816 | return; |
|---|
| 817 | } |
|---|
| 818 | }*/ |
|---|
| 819 | |
|---|
| 820 | if ( !GetNextTarget() && !m_fleeTimer && m_creatureState == STOPPED && m_AIState == STATE_IDLE && m_Unit->isAlive() ) |
|---|
| 821 | { |
|---|
| 822 | if ( timed_emote_expire <= p_time ) // note that creature might go idle and p_time might get big next time ...We do not skip emotes because of lost time |
|---|
| 823 | { |
|---|
| 824 | if ( (*next_timed_emote)->type == 1) //standstate |
|---|
| 825 | { |
|---|
| 826 | m_Unit->SetStandState( static_cast<uint8>( (*next_timed_emote)->value )); |
|---|
| 827 | m_Unit->SetUInt32Value ( UNIT_NPC_EMOTESTATE, 0 ); |
|---|
| 828 | } |
|---|
| 829 | else if ( (*next_timed_emote)->type == 2) //emotestate |
|---|
| 830 | { |
|---|
| 831 | m_Unit->SetUInt32Value ( UNIT_NPC_EMOTESTATE, (*next_timed_emote)->value ); |
|---|
| 832 | m_Unit->SetStandState( 0 ); |
|---|
| 833 | } |
|---|
| 834 | else if ( (*next_timed_emote)->type == 3) //oneshot emote |
|---|
| 835 | { |
|---|
| 836 | m_Unit->SetUInt32Value ( UNIT_NPC_EMOTESTATE, 0 ); |
|---|
| 837 | m_Unit->SetStandState( 0 ); |
|---|
| 838 | m_Unit->Emote( (EmoteType)(*next_timed_emote)->value ); // Animation |
|---|
| 839 | } |
|---|
| 840 | if ( (*next_timed_emote)->msg ) |
|---|
| 841 | m_Unit->SendChatMessage((*next_timed_emote)->msg_type, (*next_timed_emote)->msg_lang, (*next_timed_emote)->msg); |
|---|
| 842 | |
|---|
| 843 | timed_emote_expire = (*next_timed_emote)->expire_after; //should we keep lost time ? I think not |
|---|
| 844 | next_timed_emote++; |
|---|
| 845 | if ( next_timed_emote == timed_emotes->end() ) |
|---|
| 846 | next_timed_emote = timed_emotes->begin(); |
|---|
| 847 | } |
|---|
| 848 | else |
|---|
| 849 | timed_emote_expire -= p_time; |
|---|
| 850 | } |
|---|
| 851 | } |
|---|
| 852 | |
|---|
| 853 | void AIInterface::_UpdateTimer(uint32 p_time) |
|---|
| 854 | { |
|---|
| 855 | if(m_updateAssistTimer > p_time) |
|---|
| 856 | { |
|---|
| 857 | m_updateAssistTimer -= p_time; |
|---|
| 858 | }else |
|---|
| 859 | { |
|---|
| 860 | m_updateAssist = true; |
|---|
| 861 | m_updateAssistTimer = TARGET_UPDATE_INTERVAL_ON_PLAYER * 2 - m_updateAssistTimer - p_time; |
|---|
| 862 | } |
|---|
| 863 | |
|---|
| 864 | if(m_updateTargetsTimer > p_time) |
|---|
| 865 | { |
|---|
| 866 | m_updateTargetsTimer -= p_time; |
|---|
| 867 | }else |
|---|
| 868 | { |
|---|
| 869 | m_updateTargets = true; |
|---|
| 870 | m_updateTargetsTimer = TARGET_UPDATE_INTERVAL_ON_PLAYER * 2 - m_updateTargetsTimer - p_time; |
|---|
| 871 | } |
|---|
| 872 | } |
|---|
| 873 | |
|---|
| 874 | void AIInterface::_UpdateTargets() |
|---|
| 875 | { |
|---|
| 876 | if( m_Unit->IsPlayer() || (m_AIType != AITYPE_PET && disable_targeting )) |
|---|
| 877 | return; |
|---|
| 878 | if( ( ( Creature* )m_Unit )->GetCreatureInfo() && ( ( Creature* )m_Unit )->GetCreatureInfo()->Type == UNIT_TYPE_CRITTER ) |
|---|
| 879 | return; |
|---|
| 880 | |
|---|
| 881 | if( m_Unit->GetMapMgr() == NULL ) |
|---|
| 882 | return; |
|---|
| 883 | |
|---|
| 884 | AssistTargetSet::iterator i, i2; |
|---|
| 885 | TargetMap::iterator itr, it2; |
|---|
| 886 | |
|---|
| 887 | // Find new Assist Targets and remove old ones |
|---|
| 888 | if(m_AIState == STATE_FLEEING) |
|---|
| 889 | { |
|---|
| 890 | FindFriends(100.0f/*10.0*/); |
|---|
| 891 | } |
|---|
| 892 | else if(m_AIState != STATE_IDLE && m_AIState != STATE_SCRIPTIDLE) |
|---|
| 893 | { |
|---|
| 894 | FindFriends(64.0f/*8.0f*/); |
|---|
| 895 | } |
|---|
| 896 | |
|---|
| 897 | if( m_updateAssist ) |
|---|
| 898 | { |
|---|
| 899 | m_updateAssist = false; |
|---|
| 900 | /* deque<Unit*> tokill; |
|---|
| 901 | |
|---|
| 902 | //modified for vs2005 compatibility |
|---|
| 903 | for(i = m_assistTargets.begin(); i != m_assistTargets.end(); ++i) |
|---|
| 904 | { |
|---|
| 905 | if(m_Unit->GetDistanceSq((*i)) > 2500.0f|| !(*i)->isAlive() || !(*i)->CombatStatus.IsInCombat()) |
|---|
| 906 | { |
|---|
| 907 | tokill.push_back(*i); |
|---|
| 908 | } |
|---|
| 909 | } |
|---|
| 910 | |
|---|
| 911 | for(deque<Unit*>::iterator i2 = tokill.begin(); i2 != tokill.end(); ++i2) |
|---|
| 912 | m_assistTargets.erase(*i2);*/ |
|---|
| 913 | |
|---|
| 914 | for(i = m_assistTargets.begin(); i != m_assistTargets.end();) |
|---|
| 915 | { |
|---|
| 916 | i2 = i++; |
|---|
| 917 | if((*i2) == NULL || (*i2)->event_GetCurrentInstanceId() != m_Unit->event_GetCurrentInstanceId() || |
|---|
| 918 | !(*i2)->isAlive() || m_Unit->GetDistanceSq((*i2)) >= 2500.0f || !(*i2)->CombatStatus.IsInCombat() || !((*i2)->m_phase & m_Unit->m_phase) ) |
|---|
| 919 | { |
|---|
| 920 | m_assistTargets.erase( i2 ); |
|---|
| 921 | } |
|---|
| 922 | } |
|---|
| 923 | } |
|---|
| 924 | |
|---|
| 925 | if( m_updateTargets ) |
|---|
| 926 | { |
|---|
| 927 | m_updateTargets = false; |
|---|
| 928 | /*deque<Unit*> tokill; |
|---|
| 929 | |
|---|
| 930 | //modified for vs2005 compatibility |
|---|
| 931 | for(itr = m_aiTargets.begin(); itr != m_aiTargets.end();++itr) |
|---|
| 932 | { |
|---|
| 933 | if(!itr->first->isAlive() || m_Unit->GetDistanceSq(itr->first) >= 6400.0f) |
|---|
| 934 | { |
|---|
| 935 | tokill.push_back(itr->first); |
|---|
| 936 | } |
|---|
| 937 | } |
|---|
| 938 | for(deque<Unit*>::iterator itr = tokill.begin(); itr != tokill.end(); ++itr) |
|---|
| 939 | m_aiTargets.erase((*itr)); |
|---|
| 940 | tokill.clear();*/ |
|---|
| 941 | |
|---|
| 942 | LockAITargets(true); |
|---|
| 943 | |
|---|
| 944 | for(itr = m_aiTargets.begin(); itr != m_aiTargets.end();) |
|---|
| 945 | { |
|---|
| 946 | it2 = itr++; |
|---|
| 947 | |
|---|
| 948 | Unit *ai_t = m_Unit->GetMapMgr()->GetUnit( it2->first ); |
|---|
| 949 | if (ai_t == NULL) { |
|---|
| 950 | m_aiTargets.erase( it2 ); |
|---|
| 951 | } else { |
|---|
| 952 | bool instance = false; |
|---|
| 953 | if (m_Unit->GetMapMgr() && m_Unit->GetMapMgr()->GetMapInfo()) |
|---|
| 954 | { |
|---|
| 955 | switch (m_Unit->GetMapMgr()->GetMapInfo()->type) |
|---|
| 956 | { |
|---|
| 957 | case INSTANCE_RAID: |
|---|
| 958 | case INSTANCE_NONRAID: |
|---|
| 959 | case INSTANCE_ARENA: |
|---|
| 960 | instance = true; |
|---|
| 961 | break; |
|---|
| 962 | } |
|---|
| 963 | } |
|---|
| 964 | |
|---|
| 965 | if( ai_t->event_GetCurrentInstanceId() != m_Unit->event_GetCurrentInstanceId() || !ai_t->isAlive() || (!instance && m_Unit->GetDistanceSq(ai_t) >= 6400.0f || !(ai_t->m_phase & m_Unit->m_phase))) { |
|---|
| 966 | m_aiTargets.erase( it2 ); |
|---|
| 967 | } |
|---|
| 968 | } |
|---|
| 969 | } |
|---|
| 970 | |
|---|
| 971 | LockAITargets(false); |
|---|
| 972 | |
|---|
| 973 | if(m_aiTargets.size() == 0 |
|---|
| 974 | && m_AIState != STATE_IDLE && m_AIState != STATE_FOLLOWING |
|---|
| 975 | && m_AIState != STATE_EVADE && m_AIState != STATE_FEAR |
|---|
| 976 | && m_AIState != STATE_WANDER && m_AIState != STATE_SCRIPTIDLE) |
|---|
| 977 | { |
|---|
| 978 | if (m_Unit->GetMapMgr() && m_Unit->GetMapMgr()->GetMapInfo()) |
|---|
| 979 | { |
|---|
| 980 | Unit* target = NULL; |
|---|
| 981 | switch (m_Unit->GetMapMgr()->GetMapInfo()->type) |
|---|
| 982 | { |
|---|
| 983 | case INSTANCE_RAID: |
|---|
| 984 | case INSTANCE_NONRAID: |
|---|
| 985 | case INSTANCE_ARENA: |
|---|
| 986 | target = FindTarget(); |
|---|
| 987 | break; |
|---|
| 988 | |
|---|
| 989 | default: |
|---|
| 990 | if (m_outOfCombatRange && _CalcDistanceFromHome() < m_outOfCombatRange) |
|---|
| 991 | target = FindTarget(); |
|---|
| 992 | break; |
|---|
| 993 | } |
|---|
| 994 | |
|---|
| 995 | if(target != NULL) |
|---|
| 996 | AttackReaction(target, 1, 0); |
|---|
| 997 | } |
|---|
| 998 | } |
|---|
| 999 | else if( m_aiTargets.size() == 0 && (m_AIType == AITYPE_PET && (m_Unit->IsPet() && static_cast<Pet*>(m_Unit)->GetPetState() == PET_STATE_AGGRESSIVE) || (!m_Unit->IsPet() && disable_melee == false ) ) ) |
|---|
| 1000 | { |
|---|
| 1001 | Unit* target = FindTarget(); |
|---|
| 1002 | if( target ) |
|---|
| 1003 | { |
|---|
| 1004 | AttackReaction(target, 1, 0); |
|---|
| 1005 | } |
|---|
| 1006 | } |
|---|
| 1007 | } |
|---|
| 1008 | // Find new Targets when we are ooc |
|---|
| 1009 | if((m_AIState == STATE_IDLE || m_AIState == STATE_SCRIPTIDLE) && m_assistTargets.size() == 0) |
|---|
| 1010 | { |
|---|
| 1011 | Unit* target = FindTarget(); |
|---|
| 1012 | if(target) |
|---|
| 1013 | { |
|---|
| 1014 | AttackReaction(target, 1, 0); |
|---|
| 1015 | } |
|---|
| 1016 | } |
|---|
| 1017 | } |
|---|
| 1018 | |
|---|
| 1019 | ///==================================================================== |
|---|
| 1020 | /// Desc: Updates Combat Status of m_Unit |
|---|
| 1021 | ///==================================================================== |
|---|
| 1022 | void AIInterface::_UpdateCombat(uint32 p_time) |
|---|
| 1023 | { |
|---|
| 1024 | if( m_AIType != AITYPE_PET && disable_combat ) |
|---|
| 1025 | return; |
|---|
| 1026 | |
|---|
| 1027 | //just make sure we are not hitting self. |
|---|
| 1028 | // This was reported as an exploit.Should never occur anyway |
|---|
| 1029 | if( GetNextTarget() == m_Unit ) |
|---|
| 1030 | SetNextTarget( GetMostHated() ); |
|---|
| 1031 | |
|---|
| 1032 | uint16 agent = static_cast<uint16>( m_aiCurrentAgent ); |
|---|
| 1033 | |
|---|
| 1034 | // If creature is very far from spawn point return to spawnpoint |
|---|
| 1035 | // If at instance don't return -- this is wrong ... instance creatures always returns to spawnpoint, dunno how do you got this idea. |
|---|
| 1036 | // If at instance returns to spawnpoint after empty agrolist |
|---|
| 1037 | if( m_AIType != AITYPE_PET |
|---|
| 1038 | && m_AIState != STATE_EVADE |
|---|
| 1039 | && m_AIState != STATE_SCRIPTMOVE |
|---|
| 1040 | && !m_is_in_instance |
|---|
| 1041 | && (m_outOfCombatRange && m_Unit->GetDistanceSq(m_returnX,m_returnY,m_returnZ) > m_outOfCombatRange) ) |
|---|
| 1042 | { |
|---|
| 1043 | HandleEvent( EVENT_LEAVECOMBAT, m_Unit, 0 ); |
|---|
| 1044 | } |
|---|
| 1045 | else if( GetNextTarget() == NULL && m_AIState != STATE_FOLLOWING && m_AIState != STATE_SCRIPTMOVE ) |
|---|
| 1046 | { |
|---|
| 1047 | // SetNextTarget(FindTargetForSpell(m_nextSpell)); |
|---|
| 1048 | if( m_is_in_instance ) |
|---|
| 1049 | SetNextTarget( FindTarget() ); |
|---|
| 1050 | else |
|---|
| 1051 | SetNextTarget( GetMostHated() ); |
|---|
| 1052 | |
|---|
| 1053 | if( GetNextTarget() == NULL ) |
|---|
| 1054 | { |
|---|
| 1055 | HandleEvent( EVENT_LEAVECOMBAT, m_Unit, 0 ); |
|---|
| 1056 | } |
|---|
| 1057 | } else if( GetNextTarget() && !(GetNextTarget()->m_phase & m_Unit->m_phase) ) // the target or we changed phase, stop attacking |
|---|
| 1058 | { |
|---|
| 1059 | if( m_is_in_instance ) |
|---|
| 1060 | SetNextTarget( FindTarget() ); |
|---|
| 1061 | else |
|---|
| 1062 | SetNextTarget( GetMostHated() ); |
|---|
| 1063 | if( GetNextTarget() == NULL ) |
|---|
| 1064 | { |
|---|
| 1065 | HandleEvent( EVENT_LEAVECOMBAT, m_Unit, 0 ); |
|---|
| 1066 | } |
|---|
| 1067 | } |
|---|
| 1068 | |
|---|
| 1069 | #ifdef HACKY_SERVER_CLIENT_POS_SYNC |
|---|
| 1070 | if( moved_for_attack && GetNextTarget() && m_creatureState != MOVING ) |
|---|
| 1071 | { |
|---|
| 1072 | //make sure we reached the exact desired location. |
|---|
| 1073 | // due to combat updates creature might interrupt moving and start attacking and does not get to destination making us get out of range errors |
|---|
| 1074 | if( m_destinationX ) |
|---|
| 1075 | { |
|---|
| 1076 | m_Unit->m_position.x = m_destinationX; |
|---|
| 1077 | m_Unit->m_position.y = m_destinationY; |
|---|
| 1078 | } |
|---|
| 1079 | //send a forced update position to client |
|---|
| 1080 | StopMovement(0); |
|---|
| 1081 | //no need to update position until mob moves to nev target |
|---|
| 1082 | moved_for_attack = false; |
|---|
| 1083 | } |
|---|
| 1084 | #endif |
|---|
| 1085 | |
|---|
| 1086 | if (sWorld.Collision) { |
|---|
| 1087 | |
|---|
| 1088 | if ( m_Unit->GetMapMgr() != NULL && GetNextTarget() != NULL ) |
|---|
| 1089 | { |
|---|
| 1090 | if (!m_moveFly) |
|---|
| 1091 | { |
|---|
| 1092 | float target_land_z = CollideInterface.GetHeight(m_Unit->GetMapId(), GetNextTarget()->GetPositionX(), GetNextTarget()->GetPositionY(), GetNextTarget()->GetPositionZ() + 2.0f); |
|---|
| 1093 | if ( target_land_z == NO_WMO_HEIGHT ) |
|---|
| 1094 | target_land_z = m_Unit->GetMapMgr()->GetLandHeight(GetNextTarget()->GetPositionX(), GetNextTarget()->GetPositionY()); |
|---|
| 1095 | |
|---|
| 1096 | if (fabs(GetNextTarget()->GetPositionZ() - target_land_z) > _CalcCombatRange(GetNextTarget(), false)) |
|---|
| 1097 | { |
|---|
| 1098 | if ( GetNextTarget()->GetTypeId() != TYPEID_PLAYER ) |
|---|
| 1099 | { |
|---|
| 1100 | if ( target_land_z > m_Unit->GetMapMgr()->GetWaterHeight(GetNextTarget()->GetPositionX(), GetNextTarget()->GetPositionY()) ) |
|---|
| 1101 | HandleEvent( EVENT_LEAVECOMBAT, m_Unit, 0); //bugged npcs, probably db fault |
|---|
| 1102 | } |
|---|
| 1103 | else if (static_cast<Player*>(GetNextTarget())->GetSession() != NULL) |
|---|
| 1104 | { |
|---|
| 1105 | MovementInfo* mi=static_cast<Player*>(GetNextTarget())->GetSession()->GetMovementInfo(); |
|---|
| 1106 | |
|---|
| 1107 | if ( mi != NULL && !(mi->flags & MOVEFLAG_FALLING) && !(mi->flags & MOVEFLAG_SWIMMING) && !(mi->flags & MOVEFLAG_LEVITATE)) |
|---|
| 1108 | HandleEvent( EVENT_LEAVECOMBAT, m_Unit, 0); |
|---|
| 1109 | } |
|---|
| 1110 | } |
|---|
| 1111 | } |
|---|
| 1112 | } |
|---|
| 1113 | } |
|---|
| 1114 | |
|---|
| 1115 | if ( GetNextTarget() != NULL && GetNextTarget()->GetTypeId() == TYPEID_UNIT && m_AIState == STATE_EVADE) |
|---|
| 1116 | HandleEvent( EVENT_LEAVECOMBAT, m_Unit, 0); |
|---|
| 1117 | #ifdef HACKY_CRASH_FIXES |
|---|
| 1118 | bool cansee = (GetNextTarget() != NULL) ? CheckCurrentTarget() : NULL; |
|---|
| 1119 | |
|---|
| 1120 | #else |
|---|
| 1121 | bool cansee; |
|---|
| 1122 | if(GetNextTarget() && GetNextTarget()->event_GetCurrentInstanceId() == m_Unit->event_GetCurrentInstanceId()) |
|---|
| 1123 | { |
|---|
| 1124 | if( m_Unit->GetTypeId() == TYPEID_UNIT ) |
|---|
| 1125 | cansee = static_cast< Creature* >( m_Unit )->CanSee( GetNextTarget() ); |
|---|
| 1126 | else |
|---|
| 1127 | cansee = static_cast< Player* >( m_Unit )->CanSee( GetNextTarget() ); |
|---|
| 1128 | } |
|---|
| 1129 | else |
|---|
| 1130 | { |
|---|
| 1131 | if( GetNextTarget() ) |
|---|
| 1132 | SetNextTarget( (Unit*)NULL ); // corrupt pointer |
|---|
| 1133 | |
|---|
| 1134 | cansee = false; |
|---|
| 1135 | } |
|---|
| 1136 | #endif |
|---|
| 1137 | if( cansee && GetNextTarget() && GetNextTarget()->isAlive() && m_AIState != STATE_EVADE && !m_Unit->IsCasting() ) |
|---|
| 1138 | { |
|---|
| 1139 | if( agent == AGENT_NULL || ( m_AIType == AITYPE_PET && !m_nextSpell ) ) // allow pets autocast |
|---|
| 1140 | { |
|---|
| 1141 | if( !m_nextSpell ) |
|---|
| 1142 | m_nextSpell = this->getSpell(); |
|---|
| 1143 | |
|---|
| 1144 | /* |
|---|
| 1145 | if(!m_nextSpell && waiting_for_cooldown) |
|---|
| 1146 | { |
|---|
| 1147 | // don't start running to the target for melee if we're waiting for a cooldown. |
|---|
| 1148 | return; |
|---|
| 1149 | } |
|---|
| 1150 | */ |
|---|
| 1151 | |
|---|
| 1152 | if(m_canFlee && !m_hasFleed |
|---|
| 1153 | && ((m_Unit->GetUInt32Value(UNIT_FIELD_HEALTH) / m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH)) < m_FleeHealth )) |
|---|
| 1154 | agent = AGENT_FLEE; |
|---|
| 1155 | else if(m_canCallForHelp |
|---|
| 1156 | && !m_hasCalledForHelp |
|---|
| 1157 | /*&& (m_CallForHelpHealth > (m_Unit->GetUInt32Value(UNIT_FIELD_HEALTH) / (m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH) > 0 ? m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH) : 1)))*/) |
|---|
| 1158 | agent = AGENT_CALLFORHELP; |
|---|
| 1159 | else if(m_nextSpell) |
|---|
| 1160 | { |
|---|
| 1161 | if(m_nextSpell->agent != AGENT_NULL) |
|---|
| 1162 | { |
|---|
| 1163 | agent = m_nextSpell->agent; |
|---|
| 1164 | } |
|---|
| 1165 | else |
|---|
| 1166 | { |
|---|
| 1167 | agent = AGENT_MELEE; |
|---|
| 1168 | } |
|---|
| 1169 | } |
|---|
| 1170 | else |
|---|
| 1171 | { |
|---|
| 1172 | agent = AGENT_MELEE; |
|---|
| 1173 | } |
|---|
| 1174 | } |
|---|
| 1175 | if(agent == AGENT_RANGED || agent == AGENT_MELEE) |
|---|
| 1176 | { |
|---|
| 1177 | if(m_canRangedAttack) |
|---|
| 1178 | { |
|---|
| 1179 | agent = AGENT_MELEE; |
|---|
| 1180 | if(GetNextTarget()->GetTypeId() == TYPEID_PLAYER) |
|---|
| 1181 | { |
|---|
| 1182 | float dist = m_Unit->GetDistanceSq(GetNextTarget()); |
|---|
| 1183 | if( static_cast< Player* >( GetNextTarget() )->m_currentMovement == MOVE_ROOT || dist >= 64.0f ) |
|---|
| 1184 | { |
|---|
| 1185 | agent = AGENT_RANGED; |
|---|
| 1186 | } |
|---|
| 1187 | } |
|---|
| 1188 | else if( GetNextTarget()->m_canMove == false || m_Unit->GetDistanceSq(GetNextTarget()) >= 64.0f ) |
|---|
| 1189 | { |
|---|
| 1190 | agent = AGENT_RANGED; |
|---|
| 1191 | } |
|---|
| 1192 | } |
|---|
| 1193 | else |
|---|
| 1194 | { |
|---|
| 1195 | agent = AGENT_MELEE; |
|---|
| 1196 | } |
|---|
| 1197 | } |
|---|
| 1198 | |
|---|
| 1199 | if(this->disable_melee && agent == AGENT_MELEE) |
|---|
| 1200 | agent = AGENT_NULL; |
|---|
| 1201 | |
|---|
| 1202 | if(this->disable_ranged && agent == AGENT_RANGED) |
|---|
| 1203 | agent = AGENT_NULL; |
|---|
| 1204 | |
|---|
| 1205 | if(this->disable_spell && agent == AGENT_SPELL) |
|---|
| 1206 | agent = AGENT_NULL; |
|---|
| 1207 | |
|---|
| 1208 | switch(agent) |
|---|
| 1209 | { |
|---|
| 1210 | case AGENT_MELEE: |
|---|
| 1211 | { |
|---|
| 1212 | float combatReach[2]; // Calculate Combat Reach |
|---|
| 1213 | float distance = m_Unit->CalcDistance(GetNextTarget()); |
|---|
| 1214 | |
|---|
| 1215 | combatReach[0] = GetNextTarget()->GetModelHalfSize(); |
|---|
| 1216 | combatReach[1] = _CalcCombatRange(GetNextTarget(), false); |
|---|
| 1217 | |
|---|
| 1218 | if( ( |
|---|
| 1219 | // distance >= combatReach[0] && //removed by Zack. You can create an exploit with this that creature will never attack |
|---|
| 1220 | distance <= combatReach[1] + DISTANCE_TO_SMALL_TO_WALK ) |
|---|
| 1221 | // || gracefull_hit_on_target == GetNextTarget() |
|---|
| 1222 | ) // Target is in Range -> Attack |
|---|
| 1223 | { |
|---|
| 1224 | // gracefull_hit_on_target = NULL; |
|---|
| 1225 | if(UnitToFollow != NULL) |
|---|
| 1226 | { |
|---|
| 1227 | UnitToFollow = NULL; //we shouldn't be following any one |
|---|
| 1228 | m_lastFollowX = m_lastFollowY = 0; |
|---|
| 1229 | //m_Unit->setAttackTarget(NULL); // remove ourselves from any target that might have been followed |
|---|
| 1230 | } |
|---|
| 1231 | |
|---|
| 1232 | FollowDistance = 0.0f; |
|---|
| 1233 | // m_moveRun = false; |
|---|
| 1234 | //FIX ME: offhand shit |
|---|
| 1235 | if(m_Unit->isAttackReady(false) && !m_fleeTimer) |
|---|
| 1236 | { |
|---|
| 1237 | m_creatureState = ATTACKING; |
|---|
| 1238 | bool infront = m_Unit->isInFront(GetNextTarget()); |
|---|
| 1239 | |
|---|
| 1240 | if(!infront) // set InFront |
|---|
| 1241 | { |
|---|
| 1242 | //prevent mob from rotating while stunned |
|---|
| 1243 | if(!m_Unit->IsStunned ()) |
|---|
| 1244 | { |
|---|
| 1245 | setInFront(GetNextTarget()); |
|---|
| 1246 | infront = true; |
|---|
| 1247 | } |
|---|
| 1248 | } |
|---|
| 1249 | if(infront) |
|---|
| 1250 | { |
|---|
| 1251 | m_Unit->setAttackTimer(0, false); |
|---|
| 1252 | #ifdef ENABLE_CREATURE_DAZE |
|---|
| 1253 | //we require to know if strike was successful. If there was no dmg then target cannot be dazed by it |
|---|
| 1254 | Unit *t_unit = GetNextTarget(); |
|---|
| 1255 | if( !t_unit ) |
|---|
| 1256 | return; //omg lol, in seconds we lost target. This might be possible due to the Eventrelocated |
|---|
| 1257 | uint32 health_before_strike = t_unit->GetUInt32Value(UNIT_FIELD_HEALTH); |
|---|
| 1258 | #endif |
|---|
| 1259 | m_Unit->Strike( GetNextTarget(), ( agent == AGENT_MELEE ? MELEE : RANGED ), NULL, 0, 0, 0, false, false ); |
|---|
| 1260 | #ifdef ENABLE_CREATURE_DAZE |
|---|
| 1261 | //now if the target is facing his back to us then we could just cast dazed on him :P |
|---|
| 1262 | //as far as i know dazed is casted by most of the creatures but feel free to remove this code if you think otherwise |
|---|
| 1263 | if(GetNextTarget() && m_Unit->m_factionDBC && |
|---|
| 1264 | !(m_Unit->m_factionDBC->RepListId == -1 && m_Unit->m_faction->FriendlyMask==0 && m_Unit->m_faction->HostileMask==0) /* neutral creature */ |
|---|
| 1265 | && GetNextTarget()->IsPlayer() && !m_Unit->IsPet() && health_before_strike>GetNextTarget()->GetUInt32Value(UNIT_FIELD_HEALTH) |
|---|
| 1266 | && Rand(m_Unit->get_chance_to_daze(GetNextTarget()))) |
|---|
| 1267 | { |
|---|
| 1268 | float our_facing=m_Unit->calcRadAngle(m_Unit->GetPositionX(),m_Unit->GetPositionY(),GetNextTarget()->GetPositionX(),GetNextTarget()->GetPositionY()); |
|---|
| 1269 | float his_facing=GetNextTarget()->GetOrientation(); |
|---|
| 1270 | if(fabs(our_facing-his_facing)<CREATURE_DAZE_TRIGGER_ANGLE && !GetNextTarget()->HasAura(CREATURE_SPELL_TO_DAZE)) |
|---|
| 1271 | { |
|---|
| 1272 | SpellEntry *info = dbcSpell.LookupEntry(CREATURE_SPELL_TO_DAZE); |
|---|
| 1273 | Spell *sp = new Spell(m_Unit, info, false, NULL); |
|---|
| 1274 | if (!sp) |
|---|
| 1275 | return; |
|---|
| 1276 | SpellCastTargets targets; |
|---|
| 1277 | targets.m_unitTarget = GetNextTarget()->GetGUID(); |
|---|
| 1278 | sp->prepare(&targets); |
|---|
| 1279 | } |
|---|
| 1280 | } |
|---|
| 1281 | #endif |
|---|
| 1282 | } |
|---|
| 1283 | } |
|---|
| 1284 | } |
|---|
| 1285 | else // Target out of Range -> Run to it |
|---|
| 1286 | { |
|---|
| 1287 | //calculate next move |
|---|
| 1288 | // float dist = combatReach[1]; //this is theoretically right but annoying formula in game |
|---|
| 1289 | // float dist = combatReach[1] - m_Unit->GetFloatValue( UNIT_FIELD_COMBATREACH ); //ignore our combat reach, make sure target (player) can reach us first. |
|---|
| 1290 | |
|---|
| 1291 | //practical tests show that we really should try to jump on target to get good results :S |
|---|
| 1292 | //simply ignore combat reach and move as close as visually not annoying |
|---|
| 1293 | float dist; |
|---|
| 1294 | if( m_Unit->GetModelHalfSize() > GetNextTarget()->GetModelHalfSize() ) |
|---|
| 1295 | dist = m_Unit->GetModelHalfSize(); |
|---|
| 1296 | else |
|---|
| 1297 | dist = GetNextTarget()->GetModelHalfSize(); |
|---|
| 1298 | |
|---|
| 1299 | //removed by Zack. You can create an exploit with this that creature will never attack |
|---|
| 1300 | // if (distance<combatReach[0]) //if we are inside one each other |
|---|
| 1301 | // dist = -(combatReach[1] - distance); |
|---|
| 1302 | // gracefull_hit_on_target = GetNextTarget(); // this is an exploit where you manage to move the exact speed that mob will reposition itself all the time |
|---|
| 1303 | |
|---|
| 1304 | m_moveRun = true; |
|---|
| 1305 | _CalcDestinationAndMove(GetNextTarget(), dist); |
|---|
| 1306 | } |
|---|
| 1307 | }break; |
|---|
| 1308 | case AGENT_RANGED: |
|---|
| 1309 | { |
|---|
| 1310 | float combatReach[2]; // Calculate Combat Reach |
|---|
| 1311 | float distance = m_Unit->CalcDistance(GetNextTarget()); |
|---|
| 1312 | |
|---|
| 1313 | combatReach[0] = 8.0f; |
|---|
| 1314 | combatReach[1] = 30.0f; |
|---|
| 1315 | |
|---|
| 1316 | if(distance >= combatReach[0] && distance <= combatReach[1]) // Target is in Range -> Attack |
|---|
| 1317 | { |
|---|
| 1318 | if(UnitToFollow != NULL) |
|---|
| 1319 | { |
|---|
| 1320 | UnitToFollow = NULL; //we shouldn't be following any one |
|---|
| 1321 | m_lastFollowX = m_lastFollowY = 0; |
|---|
| 1322 | //m_Unit->setAttackTarget(NULL); // remove ourselves from any target that might have been followed |
|---|
| 1323 | } |
|---|
| 1324 | |
|---|
| 1325 | FollowDistance = 0.0f; |
|---|
| 1326 | // m_moveRun = false; |
|---|
| 1327 | //FIX ME: offhand shit |
|---|
| 1328 | if(m_Unit->isAttackReady(false) && !m_fleeTimer) |
|---|
| 1329 | { |
|---|
| 1330 | m_creatureState = ATTACKING; |
|---|
| 1331 | bool infront = m_Unit->isInFront(GetNextTarget()); |
|---|
| 1332 | |
|---|
| 1333 | if(!infront) // set InFront |
|---|
| 1334 | { |
|---|
| 1335 | //prevent mob from rotating while stunned |
|---|
| 1336 | if(!m_Unit->IsStunned ()) |
|---|
| 1337 | { |
|---|
| 1338 | setInFront(GetNextTarget()); |
|---|
| 1339 | infront = true; |
|---|
| 1340 | } |
|---|
| 1341 | } |
|---|
| 1342 | |
|---|
| 1343 | if(infront) |
|---|
| 1344 | { |
|---|
| 1345 | m_Unit->setAttackTimer(0, false); |
|---|
| 1346 | SpellEntry *info = dbcSpell.LookupEntry(SPELL_RANGED_GENERAL); |
|---|
| 1347 | if(info) |
|---|
| 1348 | { |
|---|
| 1349 | Spell *sp = new Spell(m_Unit, info, false, NULL); |
|---|
| 1350 | if (!sp) |
|---|
| 1351 | return; |
|---|
| 1352 | SpellCastTargets targets; |
|---|
| 1353 | targets.m_unitTarget = GetNextTarget()->GetGUID(); |
|---|
| 1354 | sp->prepare(&targets); |
|---|
| 1355 | //Lets make spell handle this |
|---|
| 1356 | //m_Unit->Strike( GetNextTarget(), ( agent == AGENT_MELEE ? MELEE : RANGED ), NULL, 0, 0, 0 ); |
|---|
| 1357 | } |
|---|
| 1358 | } |
|---|
| 1359 | } |
|---|
| 1360 | } |
|---|
| 1361 | else // Target out of Range -> Run to it |
|---|
| 1362 | { |
|---|
| 1363 | //calculate next move |
|---|
| 1364 | float dist; |
|---|
| 1365 | |
|---|
| 1366 | if(distance < combatReach[0])// Target is too near |
|---|
| 1367 | dist = 9.0f; |
|---|
| 1368 | else |
|---|
| 1369 | dist = 20.0f; |
|---|
| 1370 | |
|---|
| 1371 | m_moveRun = true; |
|---|
| 1372 | _CalcDestinationAndMove(GetNextTarget(), dist); |
|---|
| 1373 | } |
|---|
| 1374 | }break; |
|---|
| 1375 | case AGENT_SPELL: |
|---|
| 1376 | { |
|---|
| 1377 | if(!m_nextSpell || !GetNextTarget()) |
|---|
| 1378 | return; // this shouldn't happen |
|---|
| 1379 | |
|---|
| 1380 | SpellCastTime *sd = dbcSpellCastTime.LookupEntry(m_nextSpell->spell->CastingTimeIndex); |
|---|
| 1381 | |
|---|
| 1382 | float distance = m_Unit->CalcDistance(GetNextTarget()); |
|---|
| 1383 | bool los = true; |
|---|
| 1384 | |
|---|
| 1385 | if (sWorld.Collision) { |
|---|
| 1386 | los = CollideInterface.CheckLOS(m_Unit->GetMapId(), m_Unit->GetPositionNC(),GetNextTarget()->GetPositionNC()); |
|---|
| 1387 | } |
|---|
| 1388 | if(los |
|---|
| 1389 | && ( ( distance <= m_nextSpell->maxrange + m_Unit->GetModelHalfSize() |
|---|
| 1390 | // && distance >= m_nextSpell->minrange |
|---|
| 1391 | ) |
|---|
| 1392 | || m_nextSpell->maxrange == 0) ) // Target is in Range -> Attack |
|---|
| 1393 | { |
|---|
| 1394 | SpellEntry* spellInfo = m_nextSpell->spell; |
|---|
| 1395 | |
|---|
| 1396 | /* if in range stop moving so we don't interrupt the spell */ |
|---|
| 1397 | //do not stop for instant spells |
|---|
| 1398 | if(sd && GetCastTime(sd) != 0) |
|---|
| 1399 | StopMovement(0); |
|---|
| 1400 | |
|---|
| 1401 | /* if(m_nextSpell->procCount) |
|---|
| 1402 | m_nextSpell->procCount--;*/ |
|---|
| 1403 | |
|---|
| 1404 | SpellCastTargets targets = setSpellTargets(spellInfo, GetNextTarget()); |
|---|
| 1405 | uint32 targettype = m_nextSpell->spelltargetType; |
|---|
| 1406 | switch(targettype) |
|---|
| 1407 | { |
|---|
| 1408 | case TTYPE_CASTER: |
|---|
| 1409 | case TTYPE_SINGLETARGET: |
|---|
| 1410 | { |
|---|
| 1411 | CastSpell(m_Unit, spellInfo, targets); |
|---|
| 1412 | break; |
|---|
| 1413 | } |
|---|
| 1414 | case TTYPE_SOURCE: |
|---|
| 1415 | { |
|---|
| 1416 | m_Unit->CastSpellAoF(targets.m_srcX,targets.m_srcY,targets.m_srcZ, spellInfo, true); |
|---|
| 1417 | break; |
|---|
| 1418 | } |
|---|
| 1419 | case TTYPE_DESTINATION: |
|---|
| 1420 | { |
|---|
| 1421 | m_Unit->CastSpellAoF(targets.m_destX,targets.m_destY,targets.m_destZ, spellInfo, true); |
|---|
| 1422 | break; |
|---|
| 1423 | } |
|---|
| 1424 | default: |
|---|
| 1425 | sLog.outError("AI Agents: Targettype of AI agent spell %u for creature %u not set", spellInfo->Id, static_cast< Creature* >( m_Unit )->GetCreatureInfo()->Id ); |
|---|
| 1426 | } |
|---|
| 1427 | // CastSpell(m_Unit, spellInfo, targets); |
|---|
| 1428 | if(m_nextSpell&&m_nextSpell->cooldown) |
|---|
| 1429 | m_nextSpell->cooldowntime = getMSTime() + m_nextSpell->cooldown; |
|---|
| 1430 | |
|---|
| 1431 | next_spell_time = (uint32)UNIXTIME + MOB_SPELLCAST_GLOBAL_COOLDOWN; |
|---|
| 1432 | |
|---|
| 1433 | m_nextSpell = NULL; |
|---|
| 1434 | } |
|---|
| 1435 | else // Target out of Range -> Run to it |
|---|
| 1436 | { |
|---|
| 1437 | //calculate next move |
|---|
| 1438 | m_moveRun = true; |
|---|
| 1439 | float close_to_enemy = 0.0f; |
|---|
| 1440 | if( distance > m_nextSpell->maxrange ) |
|---|
| 1441 | close_to_enemy = m_nextSpell->maxrange - DISTANCE_TO_SMALL_TO_WALK ; |
|---|
| 1442 | else if( distance < m_nextSpell->minrange ) |
|---|
| 1443 | close_to_enemy = m_nextSpell->minrange + DISTANCE_TO_SMALL_TO_WALK ; |
|---|
| 1444 | |
|---|
| 1445 | if( close_to_enemy < 0 ) |
|---|
| 1446 | close_to_enemy = 0; |
|---|
| 1447 | |
|---|
| 1448 | _CalcDestinationAndMove(GetNextTarget(), close_to_enemy ); //if we make exact movement we will never position perfectly |
|---|
| 1449 | /*Destination* dst = _CalcDestination(GetNextTarget(), dist); |
|---|
| 1450 | MoveTo(dst->x, dst->y, dst->z,0); |
|---|
| 1451 | delete dst;*/ |
|---|
| 1452 | } |
|---|
| 1453 | }break; |
|---|
| 1454 | case AGENT_FLEE: |
|---|
| 1455 | { |
|---|
| 1456 | //float dist = 5.0f; |
|---|
| 1457 | |
|---|
| 1458 | m_moveRun = false; |
|---|
| 1459 | if(m_fleeTimer == 0) |
|---|
| 1460 | m_fleeTimer = m_FleeDuration; |
|---|
| 1461 | |
|---|
| 1462 | /*Destination* dst = _CalcDestination(GetNextTarget(), dist); |
|---|
| 1463 | MoveTo(dst->x, dst->y, dst->z,0); |
|---|
| 1464 | delete dst;*/ |
|---|
| 1465 | _CalcDestinationAndMove(GetNextTarget(), 5.0f); |
|---|
| 1466 | if(!m_hasFleed) |
|---|
| 1467 | CALL_SCRIPT_EVENT(m_Unit, OnFlee)(GetNextTarget()); |
|---|
| 1468 | |
|---|
| 1469 | m_AIState = STATE_FLEEING; |
|---|
| 1470 | //removed by Zack : somehow creature starts to attack self. Just making sure it is not this one |
|---|
| 1471 | // m_nextTarget = m_Unit; |
|---|
| 1472 | // m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, 0); |
|---|
| 1473 | SetNextTarget( (Unit*)NULL ); |
|---|
| 1474 | |
|---|
| 1475 | WorldPacket data( SMSG_MESSAGECHAT, 100 ); |
|---|
| 1476 | string msg = "%s attempts to run away in fear!"; |
|---|
| 1477 | data << (uint8)CHAT_MSG_CHANNEL; |
|---|
| 1478 | data << (uint32)LANG_UNIVERSAL; |
|---|
| 1479 | data << (uint32)( strlen( static_cast< Creature* >( m_Unit )->GetCreatureInfo()->Name ) + 1 ); |
|---|
| 1480 | data << static_cast< Creature* >( m_Unit )->GetCreatureInfo()->Name; |
|---|
| 1481 | data << (uint64)0; |
|---|
| 1482 | data << (uint32)(msg.size() + 1); |
|---|
| 1483 | data << msg; |
|---|
| 1484 | data << uint8(0); |
|---|
| 1485 | |
|---|
| 1486 | m_Unit->SendMessageToSet(&data, false); |
|---|
| 1487 | |
|---|
| 1488 | //m_Unit->SendChatMessage(CHAT_MSG_MONSTER_EMOTE, LANG_UNIVERSAL, msg); |
|---|
| 1489 | //sChatHandler.FillMessageData(&data, CHAT_MSG_MONSTER_EMOTE, LANG_UNIVERSAL, msg, m_Unit->GetGUID()); |
|---|
| 1490 | |
|---|
| 1491 | m_hasFleed = true; |
|---|
| 1492 | }break; |
|---|
| 1493 | case AGENT_CALLFORHELP: |
|---|
| 1494 | { |
|---|
| 1495 | FindFriends( 64.0f /*8.0f*/ ); |
|---|
| 1496 | m_hasCalledForHelp = true; // We only want to call for Help once in a Fight. |
|---|
| 1497 | if( m_Unit->GetTypeId() == TYPEID_UNIT ) |
|---|
| 1498 | objmgr.HandleMonsterSayEvent( static_cast< Creature* >( m_Unit ), MONSTER_SAY_EVENT_CALL_HELP ); |
|---|
| 1499 | CALL_SCRIPT_EVENT( m_Unit, OnCallForHelp )(); |
|---|
| 1500 | }break; |
|---|
| 1501 | } |
|---|
| 1502 | } |
|---|
| 1503 | else if( !GetNextTarget() || GetNextTarget()->GetInstanceID() != m_Unit->GetInstanceID() || !GetNextTarget()->isAlive() || !cansee ) |
|---|
| 1504 | { |
|---|
| 1505 | SetNextTarget( (Unit*)NULL ); |
|---|
| 1506 | // no more target |
|---|
| 1507 | //m_Unit->setAttackTarget(NULL); |
|---|
| 1508 | } |
|---|
| 1509 | } |
|---|
| 1510 | |
|---|
| 1511 | void AIInterface::DismissPet() |
|---|
| 1512 | { |
|---|
| 1513 | /* |
|---|
| 1514 | if(m_AIType != AITYPE_PET) |
|---|
| 1515 | return; |
|---|
| 1516 | |
|---|
| 1517 | if(!m_PetOwner) |
|---|
| 1518 | return; |
|---|
| 1519 | |
|---|
| 1520 | if(m_PetOwner->GetTypeId() != TYPEID_PLAYER) |
|---|
| 1521 | return; |
|---|
| 1522 | |
|---|
| 1523 | if(m_Unit->GetUInt32Value(UNIT_CREATED_BY_SPELL) == 0) |
|---|
| 1524 | static_cast< Player* >( m_PetOwner )->SetFreePetNo(false, (int)m_Unit->GetUInt32Value(UNIT_FIELD_PETNUMBER)); |
|---|
| 1525 | static_cast< Player* >( m_PetOwner )->SetPet(NULL); |
|---|
| 1526 | static_cast< Player* >( m_PetOwner )->SetPetName(""); |
|---|
| 1527 | |
|---|
| 1528 | //FIXME:Check hunter pet or not |
|---|
| 1529 | //FIXME:Check enslaved creature |
|---|
| 1530 | m_PetOwner->SetUInt64Value(UNIT_FIELD_SUMMON, 0); |
|---|
| 1531 | |
|---|
| 1532 | WorldPacket data; |
|---|
| 1533 | data.Initialize(SMSG_PET_SPELLS); |
|---|
| 1534 | data << (uint64)0; |
|---|
| 1535 | static_cast< Player* >( m_PetOwner )->GetSession()->SendPacket(&data); |
|---|
| 1536 | |
|---|
| 1537 | sEventMgr.RemoveEvents(((Creature*)m_Unit)); |
|---|
| 1538 | if(m_Unit->IsInWorld()) |
|---|
| 1539 | { |
|---|
| 1540 | m_Unit->RemoveFromWorld(); |
|---|
| 1541 | } |
|---|
| 1542 | //setup an event to delete the Creature |
|---|
| 1543 | sEventMgr.AddEvent(((Creature*)this->m_Unit), &Creature::DeleteMe, EVENT_DELETE_TIMER, 1, 1);*/ |
|---|
| 1544 | } |
|---|
| 1545 | |
|---|
| 1546 | void AIInterface::AttackReaction(Unit* pUnit, uint32 damage_dealt, uint32 spellId) |
|---|
| 1547 | { |
|---|
| 1548 | if( m_AIState == STATE_EVADE || !pUnit || !pUnit->isAlive() || m_Unit->IsDead() || m_Unit == pUnit ) |
|---|
| 1549 | return; |
|---|
| 1550 | |
|---|
| 1551 | if( sWorld.Collision && pUnit->IsPlayer() ) |
|---|
| 1552 | { |
|---|
| 1553 | if ( m_Unit->GetMapMgr() != NULL ) |
|---|
| 1554 | { |
|---|
| 1555 | if (!m_moveFly) |
|---|
| 1556 | { |
|---|
| 1557 | float target_land_z = CollideInterface.GetHeight(m_Unit->GetMapId(), pUnit->GetPositionX(), pUnit->GetPositionY(), pUnit->GetPositionZ() + 2.0f); |
|---|
| 1558 | if ( target_land_z == NO_WMO_HEIGHT ) |
|---|
| 1559 | target_land_z = m_Unit->GetMapMgr()->GetLandHeight(pUnit->GetPositionX(), pUnit->GetPositionY()); |
|---|
| 1560 | |
|---|
| 1561 | if (fabs(pUnit->GetPositionZ() - target_land_z) > _CalcCombatRange(pUnit, false) ) |
|---|
| 1562 | { |
|---|
| 1563 | if ( pUnit->GetTypeId()!=TYPEID_PLAYER && target_land_z > m_Unit->GetMapMgr()->GetWaterHeight(pUnit->GetPositionX(), pUnit->GetPositionY()) ) |
|---|
| 1564 | return; |
|---|
| 1565 | else if( static_cast<Player*>(pUnit)->GetSession() != NULL ) |
|---|
| 1566 | { |
|---|
| 1567 | MovementInfo* mi=static_cast<Player*>(pUnit)->GetSession()->GetMovementInfo(); |
|---|
| 1568 | |
|---|
| 1569 | if ( mi != NULL && !(mi->flags & MOVEFLAG_FALLING) && !(mi->flags & MOVEFLAG_SWIMMING) && !(mi->flags & MOVEFLAG_LEVITATE)) |
|---|
| 1570 | return; |
|---|
| 1571 | } |
|---|
| 1572 | } |
|---|
| 1573 | } |
|---|
| 1574 | } |
|---|
| 1575 | } |
|---|
| 1576 | |
|---|
| 1577 | if (pUnit->GetTypeId() == TYPEID_PLAYER && static_cast<Player *>(pUnit)->GetMisdirectionTarget() != 0) |
|---|
| 1578 | { |
|---|
| 1579 | Unit *mTarget = m_Unit->GetMapMgr()->GetUnit(static_cast<Player *>(pUnit)->GetMisdirectionTarget()); |
|---|
| 1580 | if (mTarget != NULL && mTarget->isAlive()) |
|---|
| 1581 | pUnit = mTarget; |
|---|
| 1582 | } |
|---|
| 1583 | |
|---|
| 1584 | if( (m_AIState == STATE_IDLE || m_AIState == STATE_FOLLOWING) && m_Unit->GetAIInterface()->GetAllowedToEnterCombat()) |
|---|
| 1585 | { |
|---|
| 1586 | WipeTargetList(); |
|---|
| 1587 | |
|---|
| 1588 | HandleEvent(EVENT_ENTERCOMBAT, pUnit, 0); |
|---|
| 1589 | } |
|---|
| 1590 | |
|---|
| 1591 | HandleEvent(EVENT_DAMAGETAKEN, pUnit, _CalcThreat(damage_dealt, spellId ? dbcSpell.LookupEntryForced(spellId) : NULL, pUnit)); |
|---|
| 1592 | } |
|---|
| 1593 | |
|---|
| 1594 | void AIInterface::HealReaction(Unit* caster, Unit* victim, SpellEntry* sp, uint32 amount) |
|---|
| 1595 | { |
|---|
| 1596 | if(!caster || !victim) |
|---|
| 1597 | return; |
|---|
| 1598 | |
|---|
| 1599 | bool casterInList = false, victimInList = false; |
|---|
| 1600 | |
|---|
| 1601 | if(m_aiTargets.find(caster->GetGUID()) != m_aiTargets.end()) |
|---|
| 1602 | casterInList = true; |
|---|
| 1603 | |
|---|
| 1604 | if(m_aiTargets.find(victim->GetGUID()) != m_aiTargets.end()) |
|---|
| 1605 | victimInList = true; |
|---|
| 1606 | |
|---|
| 1607 | if(!victimInList && !casterInList) // none of the Casters is in the Creatures Threat list |
|---|
| 1608 | return; |
|---|
| 1609 | |
|---|
| 1610 | int32 threat = int32(amount / 2); |
|---|
| 1611 | if (caster->getClass() == PALADIN) |
|---|
| 1612 | threat = threat / 2; //Paladins only get 50% threat per heal than other classes |
|---|
| 1613 | |
|---|
| 1614 | if (sp != NULL) |
|---|
| 1615 | threat += (threat * caster->GetGeneratedThreatModifyer(sp->School) / 100); |
|---|
| 1616 | |
|---|
| 1617 | if (threat < 1) |
|---|
| 1618 | threat = 1; |
|---|
| 1619 | |
|---|
| 1620 | if(!casterInList && victimInList) // caster is not yet in Combat but victim is |
|---|
| 1621 | { |
|---|
| 1622 | // get caster into combat if he's hostile |
|---|
| 1623 | if(isHostile(m_Unit, caster)) |
|---|
| 1624 | m_aiTargets.insert(TargetMap::value_type(caster->GetGUID(), threat)); |
|---|
| 1625 | } |
|---|
| 1626 | else if(casterInList && victimInList) // both are in combat already |
|---|
| 1627 | modThreatByPtr(caster, threat); |
|---|
| 1628 | |
|---|
| 1629 | else // caster is in Combat already but victim is not |
|---|
| 1630 | { |
|---|
| 1631 | modThreatByPtr(caster, threat); |
|---|
| 1632 | // both are players so they might be in the same group |
|---|
| 1633 | if( caster->GetTypeId() == TYPEID_PLAYER && victim->GetTypeId() == TYPEID_PLAYER ) |
|---|
| 1634 | { |
|---|
| 1635 | if( static_cast< Player* >( caster )->GetGroup() == static_cast< Player* >( victim )->GetGroup() ) |
|---|
| 1636 | { |
|---|
| 1637 | // get victim into combat since they are both |
|---|
| 1638 | // in the same party |
|---|
| 1639 | if( isHostile( m_Unit, victim ) ) |
|---|
| 1640 | m_aiTargets.insert( TargetMap::value_type( victim->GetGUID(), 1 ) ); |
|---|
| 1641 | } |
|---|
| 1642 | } |
|---|
| 1643 | } |
|---|
| 1644 | } |
|---|
| 1645 | |
|---|
| 1646 | void AIInterface::OnDeath(Object* pKiller) |
|---|
| 1647 | { |
|---|
| 1648 | if(pKiller->GetTypeId() == TYPEID_PLAYER || pKiller->GetTypeId() == TYPEID_UNIT) |
|---|
| 1649 | HandleEvent(EVENT_UNITDIED, static_cast<Unit*>(pKiller), 0); |
|---|
| 1650 | else |
|---|
| 1651 | HandleEvent(EVENT_UNITDIED, m_Unit, 0); |
|---|
| 1652 | } |
|---|
| 1653 | |
|---|
| 1654 | //function is designed to make a quick check on target to decide if we can attack it |
|---|
| 1655 | bool AIInterface::UnsafeCanOwnerAttackUnit(Unit *pUnit) |
|---|
| 1656 | { |
|---|
| 1657 | if( !isHostile(m_Unit,pUnit ) ) |
|---|
| 1658 | return false; |
|---|
| 1659 | |
|---|
| 1660 | if( !pUnit->isAlive() ) |
|---|
| 1661 | return false; |
|---|
| 1662 | |
|---|
| 1663 | if( !(pUnit->m_phase & m_Unit->m_phase) ) //Not in the same phase |
|---|
| 1664 | return false; |
|---|
| 1665 | |
|---|
| 1666 | //do not agro units that are faking death. Should this be based on chance ? |
|---|
| 1667 | if( pUnit->HasFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_FEIGN_DEATH ) ) |
|---|
| 1668 | return false; |
|---|
| 1669 | |
|---|
| 1670 | //don't attack owner |
|---|
| 1671 | if( m_Unit->GetUInt64Value(UNIT_FIELD_CREATEDBY) == pUnit->GetGUID() ) |
|---|
| 1672 | return false; |
|---|
| 1673 | |
|---|
| 1674 | //don't agro neutrals |
|---|
| 1675 | if( ( pUnit->IsPlayer() || pUnit->IsPet() ) |
|---|
| 1676 | && m_Unit->m_factionDBC |
|---|
| 1677 | && m_Unit->m_factionDBC->RepListId == -1 |
|---|
| 1678 | && m_Unit->m_faction->HostileMask == 0 |
|---|
| 1679 | && m_Unit->m_faction->FriendlyMask == 0 |
|---|
| 1680 | ) |
|---|
| 1681 | return false; |
|---|
| 1682 | else if( ( m_Unit->IsPlayer() || m_Unit->IsPet() ) |
|---|
| 1683 | && pUnit->m_factionDBC |
|---|
| 1684 | && pUnit->m_factionDBC->RepListId == -1 |
|---|
| 1685 | && pUnit->m_faction->HostileMask == 0 |
|---|
| 1686 | && pUnit->m_faction->FriendlyMask == 0 |
|---|
| 1687 | ) |
|---|
| 1688 | return false; |
|---|
| 1689 | |
|---|
| 1690 | //make sure we do not agro flying stuff |
|---|
| 1691 | if( abs( pUnit->GetPositionZ() - m_Unit->GetPositionZ() ) > _CalcCombatRange( pUnit, false ) ) |
|---|
| 1692 | return false; //blizz has this set to 250 but uses pathfinding |
|---|
| 1693 | |
|---|
| 1694 | return true; |
|---|
| 1695 | } |
|---|
| 1696 | |
|---|
| 1697 | //this function might be slow but so it should not be spammed |
|---|
| 1698 | //!!!this function has been reported the biggest bottleneck on emu in 2008 07 04 |
|---|
| 1699 | Unit* AIInterface::FindTarget() |
|---|
| 1700 | {// find nearest hostile Target to attack |
|---|
| 1701 | if( !m_AllowedToEnterCombat ) |
|---|
| 1702 | return NULL; |
|---|
| 1703 | |
|---|
| 1704 | if( m_Unit->GetMapMgr() == NULL ) |
|---|
| 1705 | return NULL; |
|---|
| 1706 | |
|---|
| 1707 | Unit* target = NULL; |
|---|
| 1708 | Unit* critterTarget = NULL; |
|---|
| 1709 | float distance = 999999.0f; // that should do it.. :p |
|---|
| 1710 | // float crange; |
|---|
| 1711 | // float z_diff; |
|---|
| 1712 | |
|---|
| 1713 | std::set<Object*>::iterator itr, itr2; |
|---|
| 1714 | std::set<Player *>::iterator pitr, pitr2; |
|---|
| 1715 | // Object *pObj; |
|---|
| 1716 | Unit *pUnit; |
|---|
| 1717 | float dist; |
|---|
| 1718 | |
|---|
| 1719 | // Don't remove this please! - dfighter |
|---|
| 1720 | /* |
|---|
| 1721 | if( m_AIType == AITYPE_PET ){ |
|---|
| 1722 | printf("I'm a pet and I'm looking for targets, RAWR!\n"); |
|---|
| 1723 | } |
|---|
| 1724 | */ |
|---|
| 1725 | |
|---|
| 1726 | |
|---|
| 1727 | |
|---|
| 1728 | /* Commented due to no use |
|---|
| 1729 | bool pvp=true; |
|---|
| 1730 | if(m_Unit->GetTypeId()==TYPEID_UNIT&&((Creature*)m_Unit)->GetCreatureInfo()&&((Creature*)m_Unit)->GetCreatureInfo()->Civilian) |
|---|
| 1731 | pvp=false;*/ |
|---|
| 1732 | |
|---|
| 1733 | //target is immune to all form of attacks, cant attack either. |
|---|
| 1734 | // not attackable creatures sometimes fight enemies in scripted fights though |
|---|
| 1735 | if(m_Unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NOT_ATTACKABLE_2)) |
|---|
| 1736 | { |
|---|
| 1737 | return 0; |
|---|
| 1738 | } |
|---|
| 1739 | |
|---|
| 1740 | // Start of neutralguard snippet |
|---|
| 1741 | if (m_isNeutralGuard) |
|---|
| 1742 | { |
|---|
| 1743 | Player *tmpPlr; |
|---|
| 1744 | for (std::set<Player*>::iterator itrPlr = m_Unit->GetInRangePlayerSetBegin(); itrPlr != m_Unit->GetInRangePlayerSetEnd(); ++itrPlr) |
|---|
| 1745 | { |
|---|
| 1746 | tmpPlr = (*itrPlr); |
|---|
| 1747 | if (tmpPlr == NULL) |
|---|
| 1748 | continue; |
|---|
| 1749 | if (tmpPlr->IsDead()) |
|---|
| 1750 | continue; |
|---|
| 1751 | if (tmpPlr->GetTaxiState()) |
|---|
| 1752 | continue; |
|---|
| 1753 | if (tmpPlr->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FEIGN_DEATH)) |
|---|
| 1754 | continue; |
|---|
| 1755 | if (tmpPlr->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_ATTACKABLE_9)) |
|---|
| 1756 | continue; |
|---|
| 1757 | if (tmpPlr->m_invisible) |
|---|
| 1758 | continue; |
|---|
| 1759 | if( !tmpPlr->HasFlag( PLAYER_FLAGS, PLAYER_FLAG_UNKNOWN2 ) )//PvP Guard Attackable. |
|---|
| 1760 | continue; |
|---|
| 1761 | if( !(tmpPlr->m_phase & m_Unit->m_phase) ) //Not in the same phase, skip this target |
|---|
| 1762 | continue; |
|---|
| 1763 | |
|---|
| 1764 | dist = m_Unit->GetDistanceSq(tmpPlr); |
|---|
| 1765 | |
|---|
| 1766 | if (dist > 2500.0f) |
|---|
| 1767 | continue; |
|---|
| 1768 | if (distance > dist) |
|---|
| 1769 | { |
|---|
| 1770 | if (sWorld.Collision) { |
|---|
| 1771 | if( CollideInterface.CheckLOS( m_Unit->GetMapId(), m_Unit->GetPositionNC(), tmpPlr->GetPositionNC() ) ) |
|---|
| 1772 | { |
|---|
| 1773 | distance = dist; |
|---|
| 1774 | target = static_cast<Unit*>(tmpPlr); |
|---|
| 1775 | } |
|---|
| 1776 | } else { |
|---|
| 1777 | distance = dist; |
|---|
| 1778 | target = static_cast<Unit*>(tmpPlr); |
|---|
| 1779 | } |
|---|
| 1780 | } |
|---|
| 1781 | } |
|---|
| 1782 | if (target) |
|---|
| 1783 | { |
|---|
| 1784 | m_Unit->m_runSpeed = m_Unit->m_base_runSpeed * 2.0f; |
|---|
| 1785 | AttackReaction(target, 1, 0); |
|---|
| 1786 | |
|---|
| 1787 | WorldPacket data(SMSG_AI_REACTION, 12); |
|---|
| 1788 | data << m_Unit->GetGUID() << uint32(2); // Aggro sound |
|---|
| 1789 | static_cast< Player* >( target )->GetSession()->SendPacket( &data ); |
|---|
| 1790 | |
|---|
| 1791 | return target; |
|---|
| 1792 | } |
|---|
| 1793 | distance = 999999.0f; //Reset Distance for normal check |
|---|
| 1794 | } |
|---|
| 1795 | // End of neutralguard snippet |
|---|
| 1796 | |
|---|
| 1797 | //we have a high chance that we will agro a player |
|---|
| 1798 | //this is slower then oppfaction list BUT it has a lower chance that contains invalid pointers |
|---|
| 1799 | for( pitr2 = m_Unit->GetInRangePlayerSetBegin(); pitr2 != m_Unit->GetInRangePlayerSetEnd(); ) |
|---|
| 1800 | { |
|---|
| 1801 | pitr = pitr2; |
|---|
| 1802 | ++pitr2; |
|---|
| 1803 | |
|---|
| 1804 | pUnit = *pitr; |
|---|
| 1805 | |
|---|
| 1806 | if( UnsafeCanOwnerAttackUnit( pUnit ) == false ) |
|---|
| 1807 | continue; |
|---|
| 1808 | |
|---|
| 1809 | //on blizz there is no Z limit check |
|---|
| 1810 | dist = m_Unit->GetDistance2dSq(pUnit); |
|---|
| 1811 | |
|---|
| 1812 | if(dist > distance) // we want to find the CLOSEST target |
|---|
| 1813 | continue; |
|---|
| 1814 | |
|---|
| 1815 | if(dist <= _CalcAggroRange(pUnit) ) |
|---|
| 1816 | { |
|---|
| 1817 | if (sWorld.Collision) { |
|---|
| 1818 | if( CollideInterface.CheckLOS( m_Unit->GetMapId( ), m_Unit->GetPositionNC( ), pUnit->GetPositionNC( ) ) ) |
|---|
| 1819 | { |
|---|
| 1820 | distance = dist; |
|---|
| 1821 | target = pUnit; |
|---|
| 1822 | } |
|---|
| 1823 | } else { |
|---|
| 1824 | distance = dist; |
|---|
| 1825 | target = pUnit; |
|---|
| 1826 | } |
|---|
| 1827 | } |
|---|
| 1828 | } |
|---|
| 1829 | |
|---|
| 1830 | //a lot less times are check inter faction mob wars :) |
|---|
| 1831 | if( m_updateTargetsTimer2 < getMSTime() ) |
|---|
| 1832 | { |
|---|
| 1833 | m_updateTargetsTimer2 = getMSTime() + TARGET_UPDATE_INTERVAL; |
|---|
| 1834 | m_Unit->AquireInrangeLock(); //make sure to release lock before exit function ! |
|---|
| 1835 | for( itr2 = m_Unit->GetInRangeSetBegin(); itr2 != m_Unit->GetInRangeSetEnd(); ) |
|---|
| 1836 | { |
|---|
| 1837 | itr = itr2; |
|---|
| 1838 | ++itr2; |
|---|
| 1839 | |
|---|
| 1840 | if( !(*itr)->IsUnit() ) |
|---|
| 1841 | continue; |
|---|
| 1842 | |
|---|
| 1843 | // We checked for player targets before, so this shouldn't happen |
|---|
| 1844 | // [14:45] <+burlex> but andy |
|---|
| 1845 | // [14:45] <+burlex> dey have dead pointerz |
|---|
| 1846 | if( (*itr)->IsPlayer() ) |
|---|
| 1847 | continue; |
|---|
| 1848 | |
|---|
| 1849 | pUnit = static_cast< Unit* >( (*itr) ); |
|---|
| 1850 | |
|---|
| 1851 | if( UnsafeCanOwnerAttackUnit( pUnit ) == false ) |
|---|
| 1852 | continue; |
|---|
| 1853 | |
|---|
| 1854 | //on blizz there is no Z limit check |
|---|
| 1855 | dist = m_Unit->GetDistance2dSq(pUnit); |
|---|
| 1856 | |
|---|
| 1857 | if(pUnit->m_faction && pUnit->m_faction->Faction == 28)// only Attack a critter if there is no other Enemy in range |
|---|
| 1858 | { |
|---|
| 1859 | if(dist < 225.0f) // was 10 |
|---|
| 1860 | critterTarget = pUnit; |
|---|
| 1861 | continue; |
|---|
| 1862 | } |
|---|
| 1863 | |
|---|
| 1864 | if(dist > distance) // we want to find the CLOSEST target |
|---|
| 1865 | continue; |
|---|
| 1866 | |
|---|
| 1867 | if(dist <= _CalcAggroRange(pUnit) ) |
|---|
| 1868 | { |
|---|
| 1869 | if (sWorld.Collision) { |
|---|
| 1870 | if( CollideInterface.CheckLOS( m_Unit->GetMapId( ), m_Unit->GetPositionNC( ), pUnit->GetPositionNC( ) ) ) |
|---|
| 1871 | { |
|---|
| 1872 | distance = dist; |
|---|
| 1873 | target = pUnit; |
|---|
| 1874 | } |
|---|
| 1875 | } else { |
|---|
| 1876 | distance = dist; |
|---|
| 1877 | target = pUnit; |
|---|
| 1878 | } |
|---|
| 1879 | } |
|---|
| 1880 | } |
|---|
| 1881 | m_Unit->ReleaseInrangeLock(); |
|---|
| 1882 | } |
|---|
| 1883 | |
|---|
| 1884 | if( !target ) |
|---|
| 1885 | { |
|---|
| 1886 | target = critterTarget; |
|---|
| 1887 | } |
|---|
| 1888 | |
|---|
| 1889 | if( target ) |
|---|
| 1890 | { |
|---|
| 1891 | /* if(m_isGuard) |
|---|
| 1892 | { |
|---|
| 1893 | m_Unit->m_runSpeed = m_Unit->m_base_runSpeed * 2.0f; |
|---|
| 1894 | m_fastMove = true; |
|---|
| 1895 | }*/ |
|---|
| 1896 | |
|---|
| 1897 | AttackReaction(target, 1, 0); |
|---|
| 1898 | if(target->IsPlayer()) |
|---|
| 1899 | { |
|---|
| 1900 | WorldPacket data(SMSG_AI_REACTION, 12); |
|---|
| 1901 | data << m_Unit->GetGUID() << uint32(2); // Aggro sound |
|---|
| 1902 | static_cast< Player* >( target )->GetSession()->SendPacket( &data ); |
|---|
| 1903 | } |
|---|
| 1904 | if(target->GetUInt32Value(UNIT_FIELD_CREATEDBY) != 0) |
|---|
| 1905 | { |
|---|
| 1906 | Unit* target2 = m_Unit->GetMapMgr()->GetPlayer(target->GetUInt32Value(UNIT_FIELD_CREATEDBY)); |
|---|
| 1907 | /*if(!target2) |
|---|
| 1908 | { |
|---|
| 1909 | target2 = sObjHolder.GetObject<Player>(target->GetUInt32Value(UNIT_FIELD_CREATEDBY)); |
|---|
| 1910 | }*/ |
|---|
| 1911 | if(target2) |
|---|
| 1912 | { |
|---|
| 1913 | AttackReaction(target2, 1, 0); |
|---|
| 1914 | } |
|---|
| 1915 | } |
|---|
| 1916 | } |
|---|
| 1917 | return target; |
|---|
| 1918 | } |
|---|
| 1919 | |
|---|
| 1920 | Unit* AIInterface::FindTargetForSpell(AI_Spell *sp) |
|---|
| 1921 | { |
|---|
| 1922 | /*if(!m_Unit) return NULL;*/ |
|---|
| 1923 | |
|---|
| 1924 | /*if(!sp) |
|---|
| 1925 | { |
|---|
| 1926 | m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, 0); |
|---|
| 1927 | return NULL; |
|---|
| 1928 | }*/ |
|---|
| 1929 | |
|---|
| 1930 | TargetMap::iterator itr, itr2; |
|---|
| 1931 | |
|---|
| 1932 | if(sp) |
|---|
| 1933 | { |
|---|
| 1934 | if(sp->spellType == STYPE_HEAL) |
|---|
| 1935 | { |
|---|
| 1936 | uint32 cur = m_Unit->GetUInt32Value(UNIT_FIELD_HEALTH) + 1; |
|---|
| 1937 | uint32 max = m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH) + 1; |
|---|
| 1938 | float healthPercent = float(cur) / float(max); |
|---|
| 1939 | if(healthPercent <= sp->floatMisc1) // Heal ourselves cause we got too low HP |
|---|
| 1940 | { |
|---|
| 1941 | m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, 0); |
|---|
| 1942 | return m_Unit; |
|---|
| 1943 | } |
|---|
| 1944 | for(AssistTargetSet::iterator i = m_assistTargets.begin(); i != m_assistTargets.end(); i++) |
|---|
| 1945 | { |
|---|
| 1946 | if(!(*i)->isAlive()) |
|---|
| 1947 | { |
|---|
| 1948 | continue; |
|---|
| 1949 | } |
|---|
| 1950 | cur = (*i)->GetUInt32Value(UNIT_FIELD_HEALTH); |
|---|
| 1951 | max = (*i)->GetUInt32Value(UNIT_FIELD_MAXHEALTH); |
|---|
| 1952 | healthPercent = float(cur) / float(max); |
|---|
| 1953 | if(healthPercent <= sp->floatMisc1) // Heal ourselves cause we got too low HP |
|---|
| 1954 | { |
|---|
| 1955 | m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, (*i)->GetGUID()); |
|---|
| 1956 | return (*i); // heal Assist Target which has low HP |
|---|
| 1957 | } |
|---|
| 1958 | } |
|---|
| 1959 | } |
|---|
| 1960 | if(sp->spellType == STYPE_BUFF) |
|---|
| 1961 | { |
|---|
| 1962 | m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, 0); |
|---|
| 1963 | return m_Unit; |
|---|
| 1964 | } |
|---|
| 1965 | } |
|---|
| 1966 | |
|---|
| 1967 | return GetMostHated(); |
|---|
| 1968 | } |
|---|
| 1969 | |
|---|
| 1970 | bool AIInterface::FindFriends(float dist) |
|---|
| 1971 | { |
|---|
| 1972 | |
|---|
| 1973 | if( m_Unit->GetMapMgr() == NULL ) |
|---|
| 1974 | return false; |
|---|
| 1975 | |
|---|
| 1976 | bool result = false; |
|---|
| 1977 | TargetMap::iterator it; |
|---|
| 1978 | |
|---|
| 1979 | std::set<Object*>::iterator itr; |
|---|
| 1980 | Unit *pUnit; |
|---|
| 1981 | |
|---|
| 1982 | m_Unit->AquireInrangeLock(); //make sure to release lock before exit function ! |
|---|
| 1983 | for( itr = m_Unit->GetInRangeSetBegin(); itr != m_Unit->GetInRangeSetEnd(); itr++ ) |
|---|
| 1984 | { |
|---|
| 1985 | if(!(*itr) || (*itr)->GetTypeId() != TYPEID_UNIT) |
|---|
| 1986 | continue; |
|---|
| 1987 | |
|---|
| 1988 | pUnit = static_cast<Unit*>((*itr)); |
|---|
| 1989 | if(!pUnit->isAlive()) |
|---|
| 1990 | continue; |
|---|
| 1991 | |
|---|
| 1992 | if(pUnit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) |
|---|
| 1993 | { |
|---|
| 1994 | continue; |
|---|
| 1995 | } |
|---|
| 1996 | if(pUnit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_ATTACKABLE_9)) |
|---|
| 1997 | { |
|---|
| 1998 | continue; |
|---|
| 1999 | } |
|---|
| 2000 | |
|---|
| 2001 | if( !(pUnit->m_phase & m_Unit->m_phase) ) //We can't help a friendly unit if it is not in our phase |
|---|
| 2002 | continue; |
|---|
| 2003 | |
|---|
| 2004 | if( isCombatSupport( m_Unit, pUnit ) && ( pUnit->GetAIInterface()->getAIState() == STATE_IDLE || pUnit->GetAIInterface()->getAIState() == STATE_SCRIPTIDLE ) )//Not sure |
|---|
| 2005 | { |
|---|
| 2006 | if( m_Unit->GetDistanceSq(pUnit) < dist) |
|---|
| 2007 | { |
|---|
| 2008 | if( m_assistTargets.count( pUnit ) == 0 ) |
|---|
| 2009 | { |
|---|
| 2010 | result = true; |
|---|
| 2011 | m_assistTargets.insert(pUnit); |
|---|
| 2012 | } |
|---|
| 2013 | |
|---|
| 2014 | LockAITargets(true); |
|---|
| 2015 | |
|---|
| 2016 | for(it = m_aiTargets.begin(); it != m_aiTargets.end(); ++it) |
|---|
| 2017 | { |
|---|
| 2018 | Unit *ai_t = m_Unit->GetMapMgr()->GetUnit( it->first ); |
|---|
| 2019 | if( ai_t && pUnit->GetAIInterface() && isHostile((Object*)ai_t,(Object*)pUnit) ) |
|---|
| 2020 | pUnit->GetAIInterface()->AttackReaction( ai_t, 1, 0 ); |
|---|
| 2021 | } |
|---|
| 2022 | |
|---|
| 2023 | LockAITargets(false); |
|---|
| 2024 | } |
|---|
| 2025 | } |
|---|
| 2026 | } |
|---|
| 2027 | m_Unit->ReleaseInrangeLock(); |
|---|
| 2028 | |
|---|
| 2029 | // check if we're a civilian, in which case summon guards on a despawn timer |
|---|
| 2030 | uint8 civilian = (((Creature*)m_Unit)->GetCreatureInfo()) ? (((Creature*)m_Unit)->GetCreatureInfo()->Civilian) : 0; |
|---|
| 2031 | uint32 family = (((Creature*)m_Unit)->GetCreatureInfo()) ? (((Creature*)m_Unit)->GetCreatureInfo()->Type) : 0; |
|---|
| 2032 | if(family == UNIT_TYPE_HUMANOID && civilian && getMSTime() > m_guardTimer && !IS_INSTANCE(m_Unit->GetMapId())) |
|---|
| 2033 | { |
|---|
| 2034 | m_guardTimer = getMSTime() + 15000; |
|---|
| 2035 | uint16 AreaId = m_Unit->GetMapMgr()->GetAreaID(m_Unit->GetPositionX(),m_Unit->GetPositionY()); |
|---|
| 2036 | AreaTable * at = dbcArea.LookupEntry(AreaId); |
|---|
| 2037 | if(!at) |
|---|
| 2038 | return result; |
|---|
| 2039 | |
|---|
| 2040 | ZoneGuardEntry * zoneSpawn = ZoneGuardStorage.LookupEntry(at->ZoneId); |
|---|
| 2041 | if(!zoneSpawn) return result; |
|---|
| 2042 | |
|---|
| 2043 | uint32 team = 1; // horde default |
|---|
| 2044 | if(isAlliance(m_Unit)) |
|---|
| 2045 | team = 0; |
|---|
| 2046 | |
|---|
| 2047 | uint32 guardid = zoneSpawn->AllianceEntry; |
|---|
| 2048 | if(team == 1) guardid = zoneSpawn->HordeEntry; |
|---|
| 2049 | if(!guardid) return result; |
|---|
| 2050 | |
|---|
| 2051 | CreatureInfo * ci = CreatureNameStorage.LookupEntry(guardid); |
|---|
| 2052 | if(!ci) |
|---|
| 2053 | return result; |
|---|
| 2054 | |
|---|
| 2055 | float x = m_Unit->GetPositionX() + (float)( (float)(rand() % 150 + 100) / 1000.0f ); |
|---|
| 2056 | float y = m_Unit->GetPositionY() + (float)( (float)(rand() % 150 + 100) / 1000.0f ); |
|---|
| 2057 | float z; |
|---|
| 2058 | |
|---|
| 2059 | if (sWorld.Collision) { |
|---|
| 2060 | z = CollideInterface.GetHeight(m_Unit->GetMapId(), x, y, m_Unit->GetPositionZ() + 2.0f); |
|---|
| 2061 | if( z == NO_WMO_HEIGHT ) |
|---|
| 2062 | z = m_Unit->GetMapMgr()->GetLandHeight(x, y); |
|---|
| 2063 | |
|---|
| 2064 | if( fabs( z - m_Unit->GetPositionZ() ) > 10.0f ) |
|---|
| 2065 | z = m_Unit->GetPositionZ(); |
|---|
| 2066 | } else { |
|---|
| 2067 | z = m_Unit->GetPositionZ(); |
|---|
| 2068 | float adt_z = m_Unit->GetMapMgr()->GetLandHeight(x, y); |
|---|
| 2069 | if(fabs(z - adt_z) < 3) |
|---|
| 2070 | z = adt_z; |
|---|
| 2071 | } |
|---|
| 2072 | |
|---|
| 2073 | CreatureProto * cp = CreatureProtoStorage.LookupEntry(guardid); |
|---|
| 2074 | if(!cp) return result; |
|---|
| 2075 | |
|---|
| 2076 | uint8 spawned = 0; |
|---|
| 2077 | |
|---|
| 2078 | std::set<Player*>::iterator hostileItr = m_Unit->GetInRangePlayerSetBegin(); |
|---|
| 2079 | for(; hostileItr != m_Unit->GetInRangePlayerSetEnd(); hostileItr++) |
|---|
| 2080 | { |
|---|
| 2081 | if(spawned >= 3) |
|---|
| 2082 | break; |
|---|
| 2083 | |
|---|
| 2084 | if(!isHostile(*hostileItr, m_Unit)) |
|---|
| 2085 | continue; |
|---|
| 2086 | |
|---|
| 2087 | if (spawned == 0) |
|---|
| 2088 | { |
|---|
| 2089 | uint32 languageid = (team == 0) ? LANG_COMMON : LANG_ORCISH; |
|---|
| 2090 | m_Unit->SendChatMessage(CHAT_MSG_MONSTER_SAY, languageid, "Guards!"); |
|---|
| 2091 | } |
|---|
| 2092 | |
|---|
| 2093 | Creature * guard = m_Unit->GetMapMgr()->CreateCreature(guardid); |
|---|
| 2094 | guard->Load(cp, x, y, z); |
|---|
| 2095 | guard->SetInstanceID(m_Unit->GetInstanceID()); |
|---|
| 2096 | guard->SetZoneId(m_Unit->GetZoneId()); |
|---|
| 2097 | guard->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP); /* shitty DBs */ |
|---|
| 2098 | guard->m_noRespawn=true; |
|---|
| 2099 | |
|---|
| 2100 | if(guard->CanAddToWorld()) |
|---|
| 2101 | { |
|---|
| 2102 | uint32 t = RandomUInt(8)*1000; |
|---|
| 2103 | if(t==0) |
|---|
| 2104 | guard->PushToWorld(m_Unit->GetMapMgr()); |
|---|
| 2105 | else |
|---|
| 2106 | sEventMgr.AddEvent(guard,&Creature::AddToWorld, m_Unit->GetMapMgr(), EVENT_UNK, t, 1, 0); |
|---|
| 2107 | } |
|---|
| 2108 | else |
|---|
| 2109 | { |
|---|
| 2110 | guard->SafeDelete(); |
|---|
| 2111 | return result; |
|---|
| 2112 | } |
|---|
| 2113 | |
|---|
| 2114 | sEventMgr.AddEvent(guard, &Creature::SetGuardWaypoints, EVENT_UNK, 10000, 1,0); |
|---|
| 2115 | sEventMgr.AddEvent(guard, &Creature::SafeDelete, EVENT_CREATURE_SAFE_DELETE, 60*5*1000, 1,EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT); |
|---|
| 2116 | spawned++; |
|---|
| 2117 | } |
|---|
| 2118 | } |
|---|
| 2119 | |
|---|
| 2120 | return result; |
|---|
| 2121 | } |
|---|
| 2122 | |
|---|
| 2123 | float AIInterface::_CalcAggroRange(Unit* target) |
|---|
| 2124 | { |
|---|
| 2125 | //float baseAR = 15.0f; // Base Aggro Range |
|---|
| 2126 | // -8 -7 -6 -5 -4 -3 -2 -1 0 +1 +2 +3 +4 +5 +6 +7 +8 |
|---|
| 2127 | //float baseAR[17] = {29.0f, 27.5f, 26.0f, 24.5f, 23.0f, 21.5f, 20.0f, 18.5f, 17.0f, 15.5f, 14.0f, 12.5f, 11.0f, 9.5f, 8.0f, 6.5f, 5.0f}; |
|---|
| 2128 | float baseAR[17] = {19.0f, 18.5f, 18.0f, 17.5f, 17.0f, 16.5f, 16.0f, 15.5f, 15.0f, 14.5f, 12.0f, 10.5f, 8.5f, 7.5f, 6.5f, 6.5f, 5.0f}; |
|---|
| 2129 | // Lvl Diff -8 -7 -6 -5 -4 -3 -2 -1 +0 +1 +2 +3 +4 +5 +6 +7 +8 |
|---|
| 2130 | // Arr Pos 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|---|
| 2131 | int8 lvlDiff = static_cast<int8>(target->getLevel() - m_Unit->getLevel()); |
|---|
| 2132 | uint8 realLvlDiff = lvlDiff; |
|---|
| 2133 | if (lvlDiff > 8) |
|---|
| 2134 | { |
|---|
| 2135 | lvlDiff = 8; |
|---|
| 2136 | } |
|---|
| 2137 | if (lvlDiff < -8) |
|---|
| 2138 | { |
|---|
| 2139 | lvlDiff = -8; |
|---|
| 2140 | } |
|---|
| 2141 | if (!((Creature*)m_Unit)->CanSee(target)) |
|---|
| 2142 | return 0; |
|---|
| 2143 | |
|---|
| 2144 | // Retrieve aggrorange from table |
|---|
| 2145 | float AggroRange = baseAR[lvlDiff + 8]; |
|---|
| 2146 | |
|---|
| 2147 | // Check to see if the target is a player mining a node |
|---|
| 2148 | bool isMining = false; |
|---|
| 2149 | if (target->IsPlayer()) |
|---|
| 2150 | { |
|---|
| 2151 | if (target->IsCasting()) |
|---|
| 2152 | { |
|---|
| 2153 | // If nearby miners weren't spotted already we'll give them a little surprise. |
|---|
| 2154 | Spell * sp = target->GetCurrentSpell(); |
|---|
| 2155 | if (sp->GetProto()->Effect[0] == SPELL_EFFECT_OPEN_LOCK && sp->GetProto()->EffectMiscValue[0] == LOCKTYPE_MINING) |
|---|
| 2156 | { |
|---|
| 2157 | isMining = true; |
|---|
| 2158 | } |
|---|
| 2159 | } |
|---|
| 2160 | } |
|---|
| 2161 | |
|---|
| 2162 | // If the target is of a much higher level the aggro range must be scaled down, unless the target is mining a nearby resource node |
|---|
| 2163 | if (realLvlDiff > 8 && !isMining) |
|---|
| 2164 | { |
|---|
| 2165 | AggroRange += AggroRange * ((lvlDiff - 8) * 5 / 100); |
|---|
| 2166 | } |
|---|
| 2167 | |
|---|
| 2168 | // Multiply by elite value |
|---|
| 2169 | if (m_Unit->IsCreature() && ((Creature*)m_Unit)->GetCreatureInfo() && ((Creature*)m_Unit)->GetCreatureInfo()->Rank > 0) |
|---|
| 2170 | { |
|---|
| 2171 | AggroRange *= (((Creature*)m_Unit)->GetCreatureInfo()->Rank) * 1.50f; |
|---|
| 2172 | } |
|---|
| 2173 | |
|---|
| 2174 | // Cap Aggro range at 40.0f |
|---|
| 2175 | if (AggroRange > 40.0f) |
|---|
| 2176 | { |
|---|
| 2177 | AggroRange = 40.0f; |
|---|
| 2178 | } |
|---|
| 2179 | |
|---|
| 2180 | /* //printf("aggro range: %f , stealthlvl: %d , detectlvl: %d\n",AggroRange,target->GetStealthLevel(),m_Unit->m_stealthDetectBonus); |
|---|
| 2181 | if(! ((Creature*)m_Unit)->CanSee(target)) |
|---|
| 2182 | { |
|---|
| 2183 | AggroRange =0; |
|---|
| 2184 | // AggroRange *= ( 100.0f - (target->m_stealthLevel - m_Unit->m_stealthDetectBonus)* 20.0f ) / 100.0f; |
|---|
| 2185 | } |
|---|
| 2186 | */ |
|---|
| 2187 | |
|---|
| 2188 | // SPELL_AURA_MOD_DETECT_RANGE |
|---|
| 2189 | int32 modDetectRange = target->getDetectRangeMod(m_Unit->GetGUID()); |
|---|
| 2190 | AggroRange += modDetectRange; |
|---|
| 2191 | if (target->IsPlayer()) |
|---|
| 2192 | { |
|---|
| 2193 | AggroRange += static_cast< Player* >(target)->DetectedRange; |
|---|
| 2194 | } |
|---|
| 2195 | |
|---|
| 2196 | // Re-check if aggro range exceeds Minimum/Maximum caps |
|---|
| 2197 | if (AggroRange < 3.0f) |
|---|
| 2198 | { |
|---|
| 2199 | AggroRange = 3.0f; |
|---|
| 2200 | } |
|---|
| 2201 | if (AggroRange > 40.0f) |
|---|
| 2202 | { |
|---|
| 2203 | AggroRange = 40.0f; |
|---|
| 2204 | } |
|---|
| 2205 | |
|---|
| 2206 | return (AggroRange * AggroRange); |
|---|
| 2207 | } |
|---|
| 2208 | |
|---|
| 2209 | void AIInterface::_CalcDestinationAndMove(Unit *target, float dist) |
|---|
| 2210 | { |
|---|
| 2211 | if(!m_canMove || m_Unit->IsStunned()) |
|---|
| 2212 | { |
|---|
| 2213 | StopMovement(0); //Just Stop |
|---|
| 2214 | return; |
|---|
| 2215 | } |
|---|
| 2216 | |
|---|
| 2217 | if( target && ( target->GetTypeId() == TYPEID_UNIT || target->GetTypeId() == TYPEID_PLAYER) ) |
|---|
| 2218 | { |
|---|
| 2219 | #ifdef HACKY_SERVER_CLIENT_POS_SYNC |
|---|
| 2220 | moved_for_attack = true; |
|---|
| 2221 | #endif |
|---|
| 2222 | float ResX = target->GetPositionX(); |
|---|
| 2223 | float ResY = target->GetPositionY(); |
|---|
| 2224 | |
|---|
| 2225 | //avoid eating bandwidth with useless movement packets when target did not move since last position |
|---|
| 2226 | //this will work since it turned into a common myth that when you pull mob you should not move :D |
|---|
| 2227 | if( abs(m_last_target_x - ResX) < DISTANCE_TO_SMALL_TO_WALK |
|---|
| 2228 | && abs(m_last_target_y - ResY) < DISTANCE_TO_SMALL_TO_WALK && m_creatureState == MOVING) |
|---|
| 2229 | return; |
|---|
| 2230 | m_last_target_x = ResX; |
|---|
| 2231 | m_last_target_y = ResY; |
|---|
| 2232 | |
|---|
| 2233 | float ResZ = target->GetPositionZ(); |
|---|
| 2234 | |
|---|
| 2235 | float angle = m_Unit->calcAngle(m_Unit->GetPositionX(), m_Unit->GetPositionY(), ResX, ResY) * float(M_PI) / 180.0f; |
|---|
| 2236 | float x = dist * cosf(angle); |
|---|
| 2237 | float y = dist * sinf(angle); |
|---|
| 2238 | |
|---|
| 2239 | if(target->GetTypeId() == TYPEID_PLAYER && static_cast< Player* >( target )->m_isMoving ) |
|---|
| 2240 | { |
|---|
| 2241 | // cater for moving player vector based on orientation |
|---|
| 2242 | x -= cosf(target->GetOrientation()); |
|---|
| 2243 | y -= sinf(target->GetOrientation()); |
|---|
| 2244 | } |
|---|
| 2245 | |
|---|
| 2246 | m_nextPosX = ResX - x; |
|---|
| 2247 | m_nextPosY = ResY - y; |
|---|
| 2248 | m_nextPosZ = ResZ; |
|---|
| 2249 | } |
|---|
| 2250 | else |
|---|
| 2251 | { |
|---|
| 2252 | target = NULL; |
|---|
| 2253 | m_nextPosX = m_Unit->GetPositionX(); |
|---|
| 2254 | m_nextPosY = m_Unit->GetPositionY(); |
|---|
| 2255 | m_nextPosZ = m_Unit->GetPositionZ(); |
|---|
| 2256 | } |
|---|
| 2257 | |
|---|
| 2258 | /* |
|---|
| 2259 | if (sWorld.Collision) { |
|---|
| 2260 | float target_land_z=0.0f; |
|---|
| 2261 | if( m_Unit->GetMapMgr() != NULL ) |
|---|
| 2262 | { |
|---|
| 2263 | if(m_moveFly != true) |
|---|
| 2264 | { |
|---|
| 2265 | target_land_z = CollideInterface.GetHeight(m_Unit->GetMapId(), m_nextPosX, m_nextPosY, m_nextPosZ + 2.0f); |
|---|
| 2266 | if( target_land_z == NO_WMO_HEIGHT ) |
|---|
| 2267 | target_land_z = m_Unit->GetMapMgr()->GetLandHeight(m_nextPosX, m_nextPosY); |
|---|
| 2268 | } |
|---|
| 2269 | } |
|---|
| 2270 | |
|---|
| 2271 | if (m_nextPosZ > m_Unit->GetMapMgr()->GetWaterHeight(m_nextPosX, m_nextPosY) && target_land_z != 0.0f) |
|---|
| 2272 | m_nextPosZ=target_land_z; |
|---|
| 2273 | } |
|---|
| 2274 | */ |
|---|
| 2275 | |
|---|
| 2276 | float dx = m_nextPosX - m_Unit->GetPositionX(); |
|---|
| 2277 | float dy = m_nextPosY - m_Unit->GetPositionY(); |
|---|
| 2278 | if(dy != 0.0f) |
|---|
| 2279 | { |
|---|
| 2280 | float angle = atan2(dx, dy); |
|---|
| 2281 | m_Unit->SetOrientation(angle); |
|---|
| 2282 | } |
|---|
| 2283 | |
|---|
| 2284 | if(m_creatureState != MOVING) |
|---|
| 2285 | UpdateMove(); |
|---|
| 2286 | } |
|---|
| 2287 | |
|---|
| 2288 | float AIInterface::_CalcCombatRange(Unit* target, bool ranged) |
|---|
| 2289 | { |
|---|
| 2290 | if(!target) |
|---|
| 2291 | { |
|---|
| 2292 | return 0; |
|---|
| 2293 | } |
|---|
| 2294 | float range = 0.0f; |
|---|
| 2295 | float rang = 0.0f; |
|---|
| 2296 | if(ranged) |
|---|
| 2297 | { |
|---|
| 2298 | rang = 5.0f; |
|---|
| 2299 | } |
|---|
| 2300 | |
|---|
| 2301 | float selfreach = m_Unit->GetFloatValue(UNIT_FIELD_COMBATREACH); |
|---|
| 2302 | float targetradius; |
|---|
| 2303 | // targetradius = target->GetFloatValue(UNIT_FIELD_BOUNDINGRADIUS); //this is plain wrong. Represents i have no idea what :) |
|---|
| 2304 | targetradius = target->GetModelHalfSize(); |
|---|
| 2305 | float selfradius; |
|---|
| 2306 | // selfradius = m_Unit->GetFloatValue(UNIT_FIELD_BOUNDINGRADIUS); //this is plain wrong. Represents i have no idea what :) |
|---|
| 2307 | selfradius = m_Unit->GetModelHalfSize(); |
|---|
| 2308 | // float targetscale = target->GetFloatValue(OBJECT_FIELD_SCALE_X); |
|---|
| 2309 | // float selfscale = m_Unit->GetFloatValue(OBJECT_FIELD_SCALE_X); |
|---|
| 2310 | |
|---|
| 2311 | // range = ((((targetradius*targetradius)*targetscale) + selfreach) + ((selfradius*selfscale) + rang)); |
|---|
| 2312 | range = targetradius + selfreach + selfradius + rang; |
|---|
| 2313 | // if(range > 28.29f) range = 28.29f; |
|---|
| 2314 | return range; |
|---|
| 2315 | } |
|---|
| 2316 | |
|---|
| 2317 | float AIInterface::_CalcDistanceFromHome() |
|---|
| 2318 | { |
|---|
| 2319 | if (m_AIType == AITYPE_PET) |
|---|
| 2320 | { |
|---|
| 2321 | return m_Unit->GetDistanceSq(m_PetOwner); |
|---|
| 2322 | } |
|---|
| 2323 | else if(m_Unit->GetTypeId() == TYPEID_UNIT) |
|---|
| 2324 | { |
|---|
| 2325 | |
|---|
| 2326 | if(m_returnX !=0.0f && m_returnY != 0.0f) |
|---|
| 2327 | { |
|---|
| 2328 | return m_Unit->GetDistanceSq(m_returnX,m_returnY,m_returnZ); |
|---|
| 2329 | } |
|---|
| 2330 | } |
|---|
| 2331 | |
|---|
| 2332 | return 0.0f; |
|---|
| 2333 | } |
|---|
| 2334 | |
|---|
| 2335 | /************************************************************************************************************ |
|---|
| 2336 | SendMoveToPacket: |
|---|
| 2337 | Comments: Some comments on the SMSG_MONSTER_MOVE packet: |
|---|
| 2338 | the uint8 field: |
|---|
| 2339 | 0: Default known |
|---|
| 2340 | 1: Don't move known |
|---|
| 2341 | 2: there is an extra 3 floats, also known as a vector unknown |
|---|
| 2342 | 3: there is an extra uint64 most likely a guid. unknown |
|---|
| 2343 | 4: there is an extra float that causes the orientation to be set. known |
|---|
| 2344 | |
|---|
| 2345 | note: when this field is 1. |
|---|
| 2346 | there is no need to send the next 3 uint32's as they aren't used by the client |
|---|
| 2347 | |
|---|
| 2348 | the MoveFlags: |
|---|
| 2349 | 0x00000000 - Walk |
|---|
| 2350 | 0x00000100 - Teleport |
|---|
| 2351 | 0x00001000 - Run |
|---|
| 2352 | 0x00000200 - Fly - OLD FLAG, IS THIS STILL VALID? |
|---|
| 2353 | 0x00003000 - Fly |
|---|
| 2354 | some comments on that 0x00000300 - Fly = 0x00000100 | 0x00000200 |
|---|
| 2355 | |
|---|
| 2356 | waypoint's: |
|---|
| 2357 | TODO.... as they somehow seemed to be changed long time ago.. |
|---|
| 2358 | |
|---|
| 2359 | *************************************************************************************************************/ |
|---|
| 2360 | |
|---|
| 2361 | void AIInterface::SendMoveToPacket(float toX, float toY, float toZ, float toO, uint32 time, uint32 MoveFlags) |
|---|
| 2362 | { |
|---|
| 2363 | //this should NEVER be called directly !!!!!! |
|---|
| 2364 | //use MoveTo() |
|---|
| 2365 | |
|---|
| 2366 | #ifndef USING_BIG_ENDIAN |
|---|
| 2367 | StackWorldPacket<100> data(SMSG_MONSTER_MOVE); |
|---|
| 2368 | #else |
|---|
| 2369 | WorldPacket data(SMSG_MONSTER_MOVE, 100); |
|---|
| 2370 | #endif |
|---|
| 2371 | data << m_Unit->GetNewGUID(); |
|---|
| 2372 | data << uint8(0); //VLack: for 3.1.x support; I've discovered this in Mangos code while doing research on how to fix invisible mobs on 3.0.9 |
|---|
| 2373 | data << m_Unit->GetPositionX() << m_Unit->GetPositionY() << m_Unit->GetPositionZ(); |
|---|
| 2374 | data << getMSTime(); |
|---|
| 2375 | |
|---|
| 2376 | // Check if we have an orientation |
|---|
| 2377 | if(toO != 0.0f) |
|---|
| 2378 | { |
|---|
| 2379 | data << uint8(4); |
|---|
| 2380 | data << toO; |
|---|
| 2381 | } else { |
|---|
| 2382 | data << uint8(0); |
|---|
| 2383 | } |
|---|
| 2384 | data << MoveFlags; |
|---|
| 2385 | /* if(MoveFlags & 0x200000) //VLack: Aspire code for 3.1.x support - I don't know these flags, as the ones I knew are the 3 well known shown in the above comment and their combinations |
|---|
| 2386 | { |
|---|
| 2387 | data << uint8(0); |
|---|
| 2388 | data << uint32(0); |
|---|
| 2389 | } |
|---|
| 2390 | if(MoveFlags & 0x800) //VLack: Aspire code for 3.1.x support |
|---|
| 2391 | { |
|---|
| 2392 | data << float(0); |
|---|
| 2393 | data << uint32(0); |
|---|
| 2394 | }*/ |
|---|
| 2395 | data << time; |
|---|
| 2396 | data << uint32(1); // 1 waypoint |
|---|
| 2397 | data << toX << toY << toZ; |
|---|
| 2398 | |
|---|
| 2399 | #ifndef ENABLE_COMPRESSED_MOVEMENT_FOR_CREATURES |
|---|
| 2400 | bool self = m_Unit->GetTypeId() == TYPEID_PLAYER; |
|---|
| 2401 | m_Unit->SendMessageToSet( &data, self ); |
|---|
| 2402 | #else |
|---|
| 2403 | if( m_Unit->GetTypeId() == TYPEID_PLAYER ) |
|---|
| 2404 | static_cast<Player*>(m_Unit)->GetSession()->SendPacket(&data); |
|---|
| 2405 | |
|---|
| 2406 | for(set<Player*>::iterator itr = m_Unit->GetInRangePlayerSetBegin(); itr != m_Unit->GetInRangePlayerSetEnd(); ++itr) |
|---|
| 2407 | { |
|---|
| 2408 | if( (*itr)->GetPositionNC().Distance2DSq( m_Unit->GetPosition() ) >= World::m_movementCompressThresholdCreatures ) |
|---|
| 2409 | (*itr)->AppendMovementData( SMSG_MONSTER_MOVE, data.GetSize(), (const uint8*)data.GetBufferPointer() ); |
|---|
| 2410 | else |
|---|
| 2411 | (*itr)->GetSession()->SendPacket(&data); |
|---|
| 2412 | } |
|---|
| 2413 | #endif |
|---|
| 2414 | } |
|---|
| 2415 | |
|---|
| 2416 | /* |
|---|
| 2417 | void AIInterface::SendMoveToSplinesPacket(std::list<Waypoint> wp, bool run) |
|---|
| 2418 | { |
|---|
| 2419 | if(!m_canMove) |
|---|
| 2420 | { |
|---|
| 2421 | return; |
|---|
| 2422 | } |
|---|
| 2423 | |
|---|
| 2424 | WorldPacket data; |
|---|
| 2425 | |
|---|
| 2426 | uint8 DontMove = 0; |
|---|
| 2427 | uint32 travelTime = 0; |
|---|
| 2428 | for(std::list<Waypoint>::iterator i = wp.begin(); i != wp.end(); i++) |
|---|
| 2429 | { |
|---|
| 2430 | travelTime += i->time; |
|---|
| 2431 | } |
|---|
| 2432 | |
|---|
| 2433 | data.Initialize( SMSG_MONSTER_MOVE ); |
|---|
| 2434 | data << m_Unit->GetNewGUID(); |
|---|
| 2435 | data << m_Unit->GetPositionX() << m_Unit->GetPositionY() << m_Unit->GetPositionZ(); |
|---|
| 2436 | data << getMSTime(); |
|---|
| 2437 | data << uint8(DontMove); |
|---|
| 2438 | data << uint32(run ? 0x00000100 : 0x00000000); |
|---|
| 2439 | data << travelTime; |
|---|
| 2440 | data << (uint32)wp.size(); |
|---|
| 2441 | for(std::list<Waypoint>::iterator i = wp.begin(); i != wp.end(); i++) |
|---|
| 2442 | { |
|---|
| 2443 | data << i->x; |
|---|
| 2444 | data << i->y; |
|---|
| 2445 | data << i->z; |
|---|
| 2446 | } |
|---|
| 2447 | |
|---|
| 2448 | m_Unit->SendMessageToSet( &data, false ); |
|---|
| 2449 | } |
|---|
| 2450 | */ |
|---|
| 2451 | bool AIInterface::StopMovement(uint32 time) |
|---|
| 2452 | { |
|---|
| 2453 | m_moveTimer = time; //set pause after stopping |
|---|
| 2454 | m_creatureState = STOPPED; |
|---|
| 2455 | |
|---|
| 2456 | m_destinationX = m_destinationY = m_destinationZ = 0; |
|---|
| 2457 | m_nextPosX = m_nextPosY = m_nextPosZ = 0; |
|---|
| 2458 | m_timeMoved = 0; |
|---|
| 2459 | m_timeToMove = 0; |
|---|
| 2460 | |
|---|
| 2461 | WorldPacket data(27); |
|---|
| 2462 | data.SetOpcode(SMSG_MONSTER_MOVE); |
|---|
| 2463 | data << m_Unit->GetNewGUID(); |
|---|
| 2464 | data << uint8(0); //VLack: 3.1 SMSG_MONSTER_MOVE change... |
|---|
| 2465 | data << m_Unit->GetPositionX() << m_Unit->GetPositionY() << m_Unit->GetPositionZ(); |
|---|
| 2466 | data << getMSTime(); |
|---|
| 2467 | data << uint8(1); // "DontMove = 1" |
|---|
| 2468 | |
|---|
| 2469 | m_Unit->SendMessageToSet( &data, false ); |
|---|
| 2470 | return true; |
|---|
| 2471 | } |
|---|
| 2472 | |
|---|
| 2473 | void AIInterface::MoveTo(float x, float y, float z, float o) |
|---|
| 2474 | { |
|---|
| 2475 | m_sourceX = m_Unit->GetPositionX(); |
|---|
| 2476 | m_sourceY = m_Unit->GetPositionY(); |
|---|
| 2477 | m_sourceZ = m_Unit->GetPositionZ(); |
|---|
| 2478 | |
|---|
| 2479 | if(!m_canMove || m_Unit->IsStunned()) |
|---|
| 2480 | { |
|---|
| 2481 | StopMovement(0); //Just Stop |
|---|
| 2482 | return; |
|---|
| 2483 | } |
|---|
| 2484 | |
|---|
| 2485 | |
|---|
| 2486 | m_nextPosX = x; |
|---|
| 2487 | m_nextPosY = y; |
|---|
| 2488 | m_nextPosZ = z; |
|---|
| 2489 | |
|---|
| 2490 | /* //Andy |
|---|
| 2491 | #ifdef COLLISION |
|---|
| 2492 | float target_land_z=0.0f; |
|---|
| 2493 | if( m_Unit->GetMapMgr() != NULL ) |
|---|
| 2494 | { |
|---|
| 2495 | if(m_moveFly != true) |
|---|
| 2496 | { |
|---|
| 2497 | target_land_z = CollideInterface.GetHeight(m_Unit->GetMapId(), m_nextPosX, m_nextPosY, m_nextPosZ + 2.0f); |
|---|
| 2498 | if( target_land_z == NO_WMO_HEIGHT ) |
|---|
| 2499 | target_land_z = m_Unit->GetMapMgr()->GetLandHeight(m_nextPosX, m_nextPosY); |
|---|
| 2500 | } |
|---|
| 2501 | } |
|---|
| 2502 | |
|---|
| 2503 | if (m_nextPosZ > m_Unit->GetMapMgr()->GetWaterHeight(m_nextPosX, m_nextPosY) && target_land_z != 0.0f) |
|---|
| 2504 | m_nextPosZ=target_land_z; |
|---|
| 2505 | #endif*/ |
|---|
| 2506 | |
|---|
| 2507 | if ( m_creatureState != MOVING ) |
|---|
| 2508 | UpdateMove(); |
|---|
| 2509 | } |
|---|
| 2510 | |
|---|
| 2511 | bool AIInterface::IsFlying() |
|---|
| 2512 | { |
|---|
| 2513 | if(m_moveFly) |
|---|
| 2514 | return true; |
|---|
| 2515 | |
|---|
| 2516 | /*float z = m_Unit->GetMapMgr()->GetLandHeight(m_Unit->GetPositionX(), m_Unit->GetPositionY()); |
|---|
| 2517 | if(z) |
|---|
| 2518 | { |
|---|
| 2519 | if(m_Unit->GetPositionZ() >= (z + 1.0f)) //not on ground? Oo |
|---|
| 2520 | { |
|---|
| 2521 | return true; |
|---|
| 2522 | } |
|---|
| 2523 | } |
|---|
| 2524 | return false;*/ |
|---|
| 2525 | if( m_Unit->GetTypeId() == TYPEID_PLAYER ) |
|---|
| 2526 | return static_cast< Player* >( m_Unit )->FlyCheat; |
|---|
| 2527 | |
|---|
| 2528 | return false; |
|---|
| 2529 | } |
|---|
| 2530 | |
|---|
| 2531 | uint32 AIInterface::getMoveFlags() |
|---|
| 2532 | { |
|---|
| 2533 | uint32 MoveFlags = 0; |
|---|
| 2534 | if(m_moveFly == true) //Fly |
|---|
| 2535 | { |
|---|
| 2536 | m_flySpeed = m_Unit->m_flySpeed*0.001f; |
|---|
| 2537 | // MoveFlags = 0x300; |
|---|
| 2538 | MoveFlags = 0x3000; //VLack: flight flag changed on 3.1.1 |
|---|
| 2539 | } |
|---|
| 2540 | else if(m_moveSprint == true) //Sprint |
|---|
| 2541 | { |
|---|
| 2542 | m_runSpeed = (m_Unit->m_runSpeed+5.0f)*0.001f; |
|---|
| 2543 | // MoveFlags = 0x100; |
|---|
| 2544 | MoveFlags = 0x1000; //VLack: on 3.1.1 0x100 would teleport the NPC, and this is not what we want here! |
|---|
| 2545 | } |
|---|
| 2546 | else if(m_moveRun == true) //Run |
|---|
| 2547 | { |
|---|
| 2548 | m_runSpeed = m_Unit->m_runSpeed*0.001f; |
|---|
| 2549 | // MoveFlags = 0x100; |
|---|
| 2550 | MoveFlags = 0x1000; //VLack: on 3.1.1 0x100 would teleport the NPC, and this is not what we want here! |
|---|
| 2551 | } |
|---|
| 2552 | /* else //Walk |
|---|
| 2553 | { |
|---|
| 2554 | m_runSpeed = m_Unit->m_walkSpeed*0.001f; |
|---|
| 2555 | MoveFlags = 0x000; |
|---|
| 2556 | }*/ |
|---|
| 2557 | m_walkSpeed = m_Unit->m_walkSpeed*0.001f;//move distance per ms time |
|---|
| 2558 | return MoveFlags; |
|---|
| 2559 | } |
|---|
| 2560 | |
|---|
| 2561 | void AIInterface::UpdateMove() |
|---|
| 2562 | { |
|---|
| 2563 | //this should NEVER be called directly !!!!!! |
|---|
| 2564 | //use MoveTo() |
|---|
| 2565 | float distance = m_Unit->CalcDistance(m_nextPosX,m_nextPosY,m_nextPosZ); |
|---|
| 2566 | |
|---|
| 2567 | if(distance < DISTANCE_TO_SMALL_TO_WALK) |
|---|
| 2568 | return; //we don't want little movements here and there |
|---|
| 2569 | |
|---|
| 2570 | m_destinationX = m_nextPosX; |
|---|
| 2571 | m_destinationY = m_nextPosY; |
|---|
| 2572 | m_destinationZ = m_nextPosZ; |
|---|
| 2573 | |
|---|
| 2574 | /*if(m_moveFly != true) |
|---|
| 2575 | { |
|---|
| 2576 | if(m_Unit->GetMapMgr()) |
|---|
| 2577 | { |
|---|
| 2578 | float adt_Z = m_Unit->GetMapMgr()->GetLandHeight(m_destinationX, m_destinationY); |
|---|
| 2579 | if(fabsf(adt_Z - m_destinationZ) < 3.0f) |
|---|
| 2580 | m_destinationZ = adt_Z; |
|---|
| 2581 | } |
|---|
| 2582 | }*/ |
|---|
| 2583 | |
|---|
| 2584 | //this prevents updatemovement working on this function |
|---|
| 2585 | //m_nextPosX = m_nextPosY = m_nextPosZ = 0; |
|---|
| 2586 | |
|---|
| 2587 | uint32 moveTime; |
|---|
| 2588 | /* #ifdef INHERIT_FOLLOWED_UNIT_SPEED vojta - this is pointless and we don't need it anymore |
|---|
| 2589 | if( UnitToFollow ) |
|---|
| 2590 | { |
|---|
| 2591 | // moveTime = (uint32) (distance * 1000 / UnitToFollow->m_runSpeed ); //I wonder if run peed can ever drop to 0 |
|---|
| 2592 | //life sucks, due to calculations the pet will move slower with correct formulas. We add some catch-up speed |
|---|
| 2593 | moveTime = (uint32) (distance * 1000 / ( UnitToFollow->m_runSpeed * sqrt( distance ) ) ); //I wonder if run peed can ever drop to 0 |
|---|
| 2594 | } |
|---|
| 2595 | #endif */ |
|---|
| 2596 | if(m_moveFly) |
|---|
| 2597 | moveTime = (uint32) (distance / m_flySpeed); |
|---|
| 2598 | else if(m_moveRun) |
|---|
| 2599 | moveTime = (uint32) (distance / m_runSpeed); |
|---|
| 2600 | else moveTime = (uint32) (distance / m_walkSpeed); |
|---|
| 2601 | |
|---|
| 2602 | m_totalMoveTime = moveTime; |
|---|
| 2603 | |
|---|
| 2604 | if(m_Unit->GetTypeId() == TYPEID_UNIT) |
|---|
| 2605 | { |
|---|
| 2606 | Creature *creature = static_cast<Creature*>(m_Unit); |
|---|
| 2607 | // check if we're returning to our respawn location. if so, reset back to default |
|---|
| 2608 | // orientation |
|---|
| 2609 | if(creature->GetSpawnX() == m_destinationX && |
|---|
| 2610 | creature->GetSpawnY() == m_destinationY) |
|---|
| 2611 | { |
|---|
| 2612 | float o = creature->GetSpawnO(); |
|---|
| 2613 | creature->SetOrientation(o); |
|---|
| 2614 | } else { |
|---|
| 2615 | // Calculate the angle to our next position |
|---|
| 2616 | |
|---|
| 2617 | float dx = (float)m_destinationX - m_Unit->GetPositionX(); |
|---|
| 2618 | float dy = (float)m_destinationY - m_Unit->GetPositionY(); |
|---|
| 2619 | if(dy != 0.0f) |
|---|
| 2620 | { |
|---|
| 2621 | float angle = atan2(dy, dx); |
|---|
| 2622 | m_Unit->SetOrientation(angle); |
|---|
| 2623 | } |
|---|
| 2624 | } |
|---|
| 2625 | } |
|---|
| 2626 | |
|---|
| 2627 | if (m_Unit->GetCurrentSpell() == NULL) |
|---|
| 2628 | SendMoveToPacket(m_destinationX, m_destinationY, m_destinationZ, m_Unit->GetOrientation(), moveTime, getMoveFlags()); |
|---|
| 2629 | |
|---|
| 2630 | m_timeToMove = moveTime; |
|---|
| 2631 | m_timeMoved = 0; |
|---|
| 2632 | if(m_moveTimer == 0) |
|---|
| 2633 | { |
|---|
| 2634 | m_moveTimer = UNIT_MOVEMENT_INTERPOLATE_INTERVAL; // update every few msecs |
|---|
| 2635 | } |
|---|
| 2636 | |
|---|
| 2637 | m_creatureState = MOVING; |
|---|
| 2638 | } |
|---|
| 2639 | |
|---|
| 2640 | void AIInterface::SendCurrentMove(Player* plyr/*uint64 guid*/) |
|---|
| 2641 | { |
|---|
| 2642 | if(m_destinationX == 0.0f && m_destinationY == 0.0f && m_destinationZ == 0.0f) return; //invalid move |
|---|
| 2643 | ByteBuffer *splineBuf = new ByteBuffer(20*4); |
|---|
| 2644 | *splineBuf << uint32(0); // spline flags |
|---|
| 2645 | *splineBuf << uint32((m_totalMoveTime - m_timeToMove)+m_moveTimer); //Time Passed (start Position) //should be generated/save |
|---|
| 2646 | *splineBuf << uint32(m_totalMoveTime); //Total Time //should be generated/save |
|---|
| 2647 | *splineBuf << uint32(0); //Unknown |
|---|
| 2648 | *splineBuf << uint32(4); //Spline Count // lets try this |
|---|
| 2649 | |
|---|
| 2650 | *splineBuf << m_sourceX << m_sourceY << m_sourceZ; |
|---|
| 2651 | *splineBuf << m_Unit->GetPositionX() << m_Unit->GetPositionY() << m_Unit->GetPositionZ(); |
|---|
| 2652 | *splineBuf << m_destinationX << m_destinationY << m_destinationZ + 0.1f; |
|---|
| 2653 | *splineBuf << m_destinationX << m_destinationY << m_destinationZ + 0.2f; |
|---|
| 2654 | *splineBuf << uint8(0); |
|---|
| 2655 | *splineBuf << m_destinationX << m_destinationY << m_destinationZ; |
|---|
| 2656 | |
|---|
| 2657 | plyr->AddSplinePacket(m_Unit->GetGUID(), splineBuf); |
|---|
| 2658 | |
|---|
| 2659 | //This should only be called by Players AddInRangeObject() ONLY |
|---|
| 2660 | //using guid cuz when I attempted to use pointer the player was deleted when this event was called some times |
|---|
| 2661 | //Player* plyr = World::GetPlayer(guid); |
|---|
| 2662 | //if(!plyr) return; |
|---|
| 2663 | |
|---|
| 2664 | /*if(m_destinationX == 0.0f && m_destinationY == 0.0f && m_destinationZ == 0.0f) return; //invalid move |
|---|
| 2665 | uint32 moveTime = m_timeToMove-m_timeMoved; |
|---|
| 2666 | //uint32 moveTime = (m_timeToMove-m_timeMoved)+m_moveTimer; |
|---|
| 2667 | WorldPacket data(50); |
|---|
| 2668 | data.SetOpcode( SMSG_MONSTER_MOVE ); |
|---|
| 2669 | data << m_Unit->GetNewGUID(); |
|---|
| 2670 | data << m_Unit->GetPositionX() << m_Unit->GetPositionY() << m_Unit->GetPositionZ(); |
|---|
| 2671 | data << getMSTime(); |
|---|
| 2672 | data << uint8(0); |
|---|
| 2673 | data << getMoveFlags(); |
|---|
| 2674 | |
|---|
| 2675 | //float distance = m_Unit->CalcDistance(m_destinationX, m_destinationY, m_destinationZ); |
|---|
| 2676 | //uint32 moveTime = (uint32) (distance / m_runSpeed); |
|---|
| 2677 | |
|---|
| 2678 | data << moveTime; |
|---|
| 2679 | data << uint32(1); //Number of Waypoints |
|---|
| 2680 | data << m_destinationX << m_destinationY << m_destinationZ; |
|---|
| 2681 | plyr->GetSession()->SendPacket(&data);*/ |
|---|
| 2682 | |
|---|
| 2683 | } |
|---|
| 2684 | |
|---|
| 2685 | bool AIInterface::setInFront(Unit* target) // not the best way to do it, though |
|---|
| 2686 | { |
|---|
| 2687 | //angle the object has to face |
|---|
| 2688 | float angle = m_Unit->calcAngle(m_Unit->GetPositionX(), m_Unit->GetPositionY(), target->GetPositionX(), target->GetPositionY() ); |
|---|
| 2689 | //Change angle slowly 2000ms to turn 180 deg around |
|---|
| 2690 | if(angle > 180) angle += 90; |
|---|
| 2691 | else angle -= 90; //angle < 180 |
|---|
| 2692 | m_Unit->getEasyAngle(angle); |
|---|
| 2693 | //Convert from degrees to radians (180 deg = PI rad) |
|---|
| 2694 | float orientation = angle / float(180 / M_PI); |
|---|
| 2695 | //Update Orientation Server Side |
|---|
| 2696 | m_Unit->SetPosition(m_Unit->GetPositionX(), m_Unit->GetPositionY(), m_Unit->GetPositionZ(), orientation); |
|---|
| 2697 | |
|---|
| 2698 | return m_Unit->isInFront(target); |
|---|
| 2699 | } |
|---|
| 2700 | |
|---|
| 2701 | bool AIInterface::addWayPoint(WayPoint* wp) |
|---|
| 2702 | { |
|---|
| 2703 | if(!m_waypoints) |
|---|
| 2704 | m_waypoints = new WayPointMap ; |
|---|
| 2705 | if(!wp) |
|---|
| 2706 | return false; |
|---|
| 2707 | if(wp->id <= 0) |
|---|
| 2708 | return false; //not valid id |
|---|
| 2709 | |
|---|
| 2710 | if(m_waypoints->size() <= wp->id) |
|---|
| 2711 | m_waypoints->resize(wp->id+1); |
|---|
| 2712 | |
|---|
| 2713 | if((*m_waypoints)[wp->id] == NULL) |
|---|
| 2714 | { |
|---|
| 2715 | (*m_waypoints)[wp->id] = wp; |
|---|
| 2716 | return true; |
|---|
| 2717 | } |
|---|
| 2718 | return false; |
|---|
| 2719 | } |
|---|
| 2720 | |
|---|
| 2721 | void AIInterface::changeWayPointID(uint32 oldwpid, uint32 newwpid) |
|---|
| 2722 | { |
|---|
| 2723 | if(!m_waypoints)return; |
|---|
| 2724 | if(newwpid <= 0) |
|---|
| 2725 | return; //not valid id |
|---|
| 2726 | if(newwpid > m_waypoints->size()) |
|---|
| 2727 | return; //not valid id |
|---|
| 2728 | if(oldwpid > m_waypoints->size()) |
|---|
| 2729 | return; |
|---|
| 2730 | |
|---|
| 2731 | if(newwpid == oldwpid) |
|---|
| 2732 | return; //same spot |
|---|
| 2733 | |
|---|
| 2734 | //already wp with that id ? |
|---|
| 2735 | WayPoint* originalwp = getWayPoint(newwpid); |
|---|
| 2736 | if(!originalwp) |
|---|
| 2737 | return; |
|---|
| 2738 | WayPoint* oldwp = getWayPoint(oldwpid); |
|---|
| 2739 | if(!oldwp) |
|---|
| 2740 | return; |
|---|
| 2741 | |
|---|
| 2742 | oldwp->id = newwpid; |
|---|
| 2743 | originalwp->id = oldwpid; |
|---|
| 2744 | (*m_waypoints)[oldwp->id] = oldwp; |
|---|
| 2745 | (*m_waypoints)[originalwp->id] = originalwp; |
|---|
| 2746 | |
|---|
| 2747 | //SaveAll to db |
|---|
| 2748 | saveWayPoints(); |
|---|
| 2749 | } |
|---|
| 2750 | |
|---|
| 2751 | void AIInterface::deleteWayPoint(uint32 wpid) |
|---|
| 2752 | { |
|---|
| 2753 | if(!m_waypoints)return; |
|---|
| 2754 | if(wpid <= 0) |
|---|
| 2755 | return; //not valid id |
|---|
| 2756 | if(wpid > m_waypoints->size()) |
|---|
| 2757 | return; //not valid id |
|---|
| 2758 | |
|---|
| 2759 | WayPointMap new_waypoints; |
|---|
| 2760 | uint32 newpid = 1; |
|---|
| 2761 | for(WayPointMap::iterator itr = m_waypoints->begin(); itr != m_waypoints->end(); ++itr) |
|---|
| 2762 | { |
|---|
| 2763 | if((*itr) == NULL || (*itr)->id == wpid) |
|---|
| 2764 | { |
|---|
| 2765 | if((*itr) != NULL) |
|---|
| 2766 | delete (*itr); |
|---|
| 2767 | |
|---|
| 2768 | continue; |
|---|
| 2769 | } |
|---|
| 2770 | |
|---|
| 2771 | new_waypoints.push_back(*itr); |
|---|
| 2772 | } |
|---|
| 2773 | |
|---|
| 2774 | m_waypoints->clear(); |
|---|
| 2775 | m_waypoints->push_back((WayPoint*)NULL); // waypoint 0 |
|---|
| 2776 | for(WayPointMap::iterator itr = new_waypoints.begin(); itr != new_waypoints.end(); ++itr) |
|---|
| 2777 | { |
|---|
| 2778 | (*itr)->id = newpid++; |
|---|
| 2779 | m_waypoints->push_back(*itr); |
|---|
| 2780 | } |
|---|
| 2781 | |
|---|
| 2782 | saveWayPoints(); |
|---|
| 2783 | } |
|---|
| 2784 | |
|---|
| 2785 | bool AIInterface::showWayPoints(Player* pPlayer, bool Backwards) |
|---|
| 2786 | { |
|---|
| 2787 | if(!m_waypoints) |
|---|
| 2788 | return false; |
|---|
| 2789 | |
|---|
| 2790 | //wpid of 0 == all |
|---|
| 2791 | WayPointMap::const_iterator itr; |
|---|
| 2792 | if(m_WayPointsShowing == true) |
|---|
| 2793 | return false; |
|---|
| 2794 | |
|---|
| 2795 | m_WayPointsShowing = true; |
|---|
| 2796 | |
|---|
| 2797 | WayPoint* wp = NULL; |
|---|
| 2798 | for (itr = m_waypoints->begin(); itr != m_waypoints->end(); itr++) |
|---|
| 2799 | { |
|---|
| 2800 | if( (*itr) != NULL ) |
|---|
| 2801 | { |
|---|
| 2802 | wp = *itr; |
|---|
| 2803 | |
|---|
| 2804 | //Create |
|---|
| 2805 | Creature* pWayPoint = new Creature((uint64)HIGHGUID_TYPE_WAYPOINT << 32 | wp->id); |
|---|
| 2806 | pWayPoint->CreateWayPoint(wp->id,pPlayer->GetMapId(),wp->x,wp->y,wp->z,0); |
|---|
| 2807 | pWayPoint->SetUInt32Value(OBJECT_FIELD_ENTRY, 300000); |
|---|
| 2808 | pWayPoint->SetFloatValue(OBJECT_FIELD_SCALE_X, 0.5f); |
|---|
| 2809 | if(Backwards) |
|---|
| 2810 | { |
|---|
| 2811 | uint32 DisplayID = (wp->backwardskinid == 0)? GetUnit()->GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID) : wp->backwardskinid; |
|---|
| 2812 | pWayPoint->SetUInt32Value(UNIT_FIELD_DISPLAYID, DisplayID); |
|---|
| 2813 | pWayPoint->SetUInt32Value(UNIT_NPC_EMOTESTATE, wp->backwardemoteid); |
|---|
| 2814 | } |
|---|
| 2815 | else |
|---|
| 2816 | { |
|---|
| 2817 | uint32 DisplayID = (wp->forwardskinid == 0)? GetUnit()->GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID) : wp->forwardskinid; |
|---|
| 2818 | pWayPoint->SetUInt32Value(UNIT_FIELD_DISPLAYID, DisplayID); |
|---|
| 2819 | pWayPoint->SetUInt32Value(UNIT_NPC_EMOTESTATE, wp->forwardemoteid); |
|---|
| 2820 | } |
|---|
| 2821 | pWayPoint->SetUInt32Value(UNIT_FIELD_LEVEL, wp->id); |
|---|
| 2822 | pWayPoint->SetUInt32Value(UNIT_NPC_FLAGS, 0); |
|---|
| 2823 | pWayPoint->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE , pPlayer->GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE)); |
|---|
| 2824 | pWayPoint->SetUInt32Value(UNIT_FIELD_HEALTH, 1); |
|---|
| 2825 | pWayPoint->SetUInt32Value(UNIT_FIELD_MAXHEALTH, 1); |
|---|
| 2826 | pWayPoint->SetUInt32Value(UNIT_FIELD_STAT0, wp->flags); |
|---|
| 2827 | |
|---|
| 2828 | //Create on client |
|---|
| 2829 | ByteBuffer buf(3000); |
|---|
| 2830 | uint32 count = pWayPoint->BuildCreateUpdateBlockForPlayer(&buf, pPlayer); |
|---|
| 2831 | pPlayer->PushCreationData(&buf, count); |
|---|
| 2832 | |
|---|
| 2833 | //root the object |
|---|
| 2834 | WorldPacket data1; |
|---|
| 2835 | data1.Initialize(SMSG_FORCE_MOVE_ROOT); |
|---|
| 2836 | data1 << pWayPoint->GetNewGUID(); |
|---|
| 2837 | pPlayer->GetSession()->SendPacket( &data1 ); |
|---|
| 2838 | |
|---|
| 2839 | //Cleanup |
|---|
| 2840 | delete pWayPoint; |
|---|
| 2841 | } |
|---|
| 2842 | } |
|---|
| 2843 | return true; |
|---|
| 2844 | } |
|---|
| 2845 | |
|---|
| 2846 | bool AIInterface::hideWayPoints(Player* pPlayer) |
|---|
| 2847 | { |
|---|
| 2848 | if(!m_waypoints) |
|---|
| 2849 | return false; |
|---|
| 2850 | |
|---|
| 2851 | //wpid of 0 == all |
|---|
| 2852 | if(m_WayPointsShowing != true) return false; |
|---|
| 2853 | m_WayPointsShowing = false; |
|---|
| 2854 | WayPointMap::const_iterator itr; |
|---|
| 2855 | |
|---|
| 2856 | // slightly better way to do this |
|---|
| 2857 | uint64 guid; |
|---|
| 2858 | |
|---|
| 2859 | for (itr = m_waypoints->begin(); itr != m_waypoints->end(); itr++) |
|---|
| 2860 | { |
|---|
| 2861 | if( (*itr) != NULL ) |
|---|
| 2862 | { |
|---|
| 2863 | // avoid C4293 |
|---|
| 2864 | guid = ((uint64)HIGHGUID_TYPE_WAYPOINT << 32) | (*itr)->id; |
|---|
| 2865 | WoWGuid wowguid(guid); |
|---|
| 2866 | pPlayer->PushOutOfRange(wowguid); |
|---|
| 2867 | } |
|---|
| 2868 | } |
|---|
| 2869 | return true; |
|---|
| 2870 | } |
|---|
| 2871 | |
|---|
| 2872 | bool AIInterface::saveWayPoints() |
|---|
| 2873 | { |
|---|
| 2874 | if(!m_waypoints)return false; |
|---|
| 2875 | |
|---|
| 2876 | if(!GetUnit()) return false; |
|---|
| 2877 | if(GetUnit()->GetTypeId() != TYPEID_UNIT) return false; |
|---|
| 2878 | |
|---|
| 2879 | WorldDatabase.Execute("DELETE FROM creature_waypoints WHERE spawnid = %u", ((Creature*)GetUnit())->GetSQL_id()); |
|---|
| 2880 | WayPointMap::const_iterator itr; |
|---|
| 2881 | WayPoint* wp = NULL; |
|---|
| 2882 | std::stringstream ss; |
|---|
| 2883 | |
|---|
| 2884 | for (itr = m_waypoints->begin(); itr != m_waypoints->end(); itr++) |
|---|
| 2885 | { |
|---|
| 2886 | if((*itr) == NULL) |
|---|
| 2887 | continue; |
|---|
| 2888 | |
|---|
| 2889 | wp = (*itr); |
|---|
| 2890 | |
|---|
| 2891 | //Save |
|---|
| 2892 | ss.str(""); |
|---|
| 2893 | ss << "REPLACE INTO creature_waypoints "; |
|---|
| 2894 | ss << "(spawnid,waypointid,position_x,position_y,position_z,waittime,flags,forwardemoteoneshot,forwardemoteid,backwardemoteoneshot,backwardemoteid,forwardskinid,backwardskinid) VALUES ("; |
|---|
| 2895 | ss << ((Creature*)GetUnit())->GetSQL_id() << ", "; |
|---|
| 2896 | ss << wp->id << ", "; |
|---|
| 2897 | ss << wp->x << ", "; |
|---|
| 2898 | ss << wp->y << ", "; |
|---|
| 2899 | ss << wp->z << ", "; |
|---|
| 2900 | ss << wp->waittime << ", "; |
|---|
| 2901 | ss << wp->flags << ", "; |
|---|
| 2902 | ss << wp->forwardemoteoneshot << ", "; |
|---|
| 2903 | ss << wp->forwardemoteid << ", "; |
|---|
| 2904 | ss << wp->backwardemoteoneshot << ", "; |
|---|
| 2905 | ss << wp->backwardemoteid << ", "; |
|---|
| 2906 | ss << wp->forwardskinid << ", "; |
|---|
| 2907 | ss << wp->backwardskinid << ")\0"; |
|---|
| 2908 | WorldDatabase.Query( ss.str().c_str() ); |
|---|
| 2909 | } |
|---|
| 2910 | return true; |
|---|
| 2911 | } |
|---|
| 2912 | |
|---|
| 2913 | void AIInterface::deleteWaypoints() |
|---|
| 2914 | { |
|---|
| 2915 | if(!m_waypoints) |
|---|
| 2916 | return; |
|---|
| 2917 | |
|---|
| 2918 | for(WayPointMap::iterator itr = m_waypoints->begin(); itr != m_waypoints->end(); ++itr) |
|---|
| 2919 | { |
|---|
| 2920 | if((*itr) != NULL) |
|---|
| 2921 | delete (*itr); |
|---|
| 2922 | } |
|---|
| 2923 | m_waypoints->clear(); |
|---|
| 2924 | } |
|---|
| 2925 | |
|---|
| 2926 | WayPoint* AIInterface::getWayPoint(uint32 wpid) |
|---|
| 2927 | { |
|---|
| 2928 | if(!m_waypoints)return NULL; |
|---|
| 2929 | if(wpid >= m_waypoints->size()) |
|---|
| 2930 | return NULL; //not valid id |
|---|
| 2931 | |
|---|
| 2932 | /*WayPointMap::const_iterator itr = m_waypoints->find( wpid ); |
|---|
| 2933 | if( itr != m_waypoints->end( ) ) |
|---|
| 2934 | return itr->second;*/ |
|---|
| 2935 | return m_waypoints->at(wpid); |
|---|
| 2936 | } |
|---|
| 2937 | |
|---|
| 2938 | void AIInterface::_UpdateMovement(uint32 p_time) |
|---|
| 2939 | { |
|---|
| 2940 | if(!m_Unit->isAlive()) |
|---|
| 2941 | { |
|---|
| 2942 | StopMovement(0); |
|---|
| 2943 | return; |
|---|
| 2944 | } |
|---|
| 2945 | |
|---|
| 2946 | //move after finishing our current spell |
|---|
| 2947 | if ( m_Unit->GetCurrentSpell() != NULL ) |
|---|
| 2948 | return; |
|---|
| 2949 | |
|---|
| 2950 | uint32 timediff = 0; |
|---|
| 2951 | |
|---|
| 2952 | if(m_moveTimer > 0) |
|---|
| 2953 | { |
|---|
| 2954 | if(p_time >= m_moveTimer) |
|---|
| 2955 | { |
|---|
| 2956 | timediff = p_time - m_moveTimer; |
|---|
| 2957 | m_moveTimer = 0; |
|---|
| 2958 | } |
|---|
| 2959 | else |
|---|
| 2960 | m_moveTimer -= p_time; |
|---|
| 2961 | } |
|---|
| 2962 | |
|---|
| 2963 | if(m_timeToMove > 0) |
|---|
| 2964 | { |
|---|
| 2965 | m_timeMoved = m_timeToMove <= p_time + m_timeMoved ? m_timeToMove : p_time + m_timeMoved; |
|---|
| 2966 | } |
|---|
| 2967 | |
|---|
| 2968 | if(m_creatureState == MOVING) |
|---|
| 2969 | { |
|---|
| 2970 | if(!m_moveTimer) |
|---|
| 2971 | { |
|---|
| 2972 | if(m_timeMoved == m_timeToMove) //reached destination |
|---|
| 2973 | { |
|---|
| 2974 | /* if(m_fastMove) |
|---|
| 2975 | { |
|---|
| 2976 | m_Unit->UpdateSpeed(); |
|---|
| 2977 | m_fastMove = false; |
|---|
| 2978 | }*/ |
|---|
| 2979 | |
|---|
| 2980 | if(m_moveType == MOVEMENTTYPE_WANTEDWP)//We reached wanted wp stop now |
|---|
| 2981 | m_moveType = MOVEMENTTYPE_DONTMOVEWP; |
|---|
| 2982 | |
|---|
| 2983 | float wayO = 0.0f; |
|---|
| 2984 | |
|---|
| 2985 | if((GetWayPointsCount() != 0) && (m_AIState == STATE_IDLE || m_AIState == STATE_SCRIPTMOVE)) //if we attacking don't use wps |
|---|
| 2986 | { |
|---|
| 2987 | WayPoint* wp = getWayPoint(getCurrentWaypoint()); |
|---|
| 2988 | if(wp) |
|---|
| 2989 | { |
|---|
| 2990 | CALL_SCRIPT_EVENT(m_Unit, OnReachWP)(wp->id, !m_moveBackward); |
|---|
| 2991 | if(((Creature*)m_Unit)->has_waypoint_text) |
|---|
| 2992 | objmgr.HandleMonsterSayEvent(((Creature*)m_Unit), MONSTER_SAY_EVENT_RANDOM_WAYPOINT); |
|---|
| 2993 | |
|---|
| 2994 | //Lets face to correct orientation |
|---|
| 2995 | wayO = wp->o; |
|---|
| 2996 | m_moveTimer = wp->waittime; //wait before next move |
|---|
| 2997 | if(!m_moveBackward) |
|---|
| 2998 | { |
|---|
| 2999 | if(wp->forwardemoteoneshot) |
|---|
| 3000 | { |
|---|
| 3001 | GetUnit()->Emote(EmoteType(wp->forwardemoteid)); |
|---|
| 3002 | } |
|---|
| 3003 | else |
|---|
| 3004 | { |
|---|
| 3005 | if(GetUnit()->GetUInt32Value(UNIT_NPC_EMOTESTATE) != wp->forwardemoteid) |
|---|
| 3006 | { |
|---|
| 3007 | GetUnit()->SetUInt32Value(UNIT_NPC_EMOTESTATE, wp->forwardemoteid); |
|---|
| 3008 | } |
|---|
| 3009 | } |
|---|
| 3010 | } |
|---|
| 3011 | else |
|---|
| 3012 | { |
|---|
| 3013 | if(wp->backwardemoteoneshot) |
|---|
| 3014 | { |
|---|
| 3015 | GetUnit()->Emote(EmoteType(wp->backwardemoteid)); |
|---|
| 3016 | } |
|---|
| 3017 | else |
|---|
| 3018 | { |
|---|
| 3019 | if(GetUnit()->GetUInt32Value(UNIT_NPC_EMOTESTATE) != wp->backwardemoteid) |
|---|
| 3020 | { |
|---|
| 3021 | GetUnit()->SetUInt32Value(UNIT_NPC_EMOTESTATE, wp->backwardemoteid); |
|---|
| 3022 | } |
|---|
| 3023 | } |
|---|
| 3024 | } |
|---|
| 3025 | } |
|---|
| 3026 | else |
|---|
| 3027 | m_moveTimer = RandomUInt(m_moveRun ? 5000 : 10000); // wait before next move |
|---|
| 3028 | } |
|---|
| 3029 | |
|---|
| 3030 | m_creatureState = STOPPED; |
|---|
| 3031 | m_moveSprint = false; |
|---|
| 3032 | |
|---|
| 3033 | if(m_MovementType == MOVEMENTTYPE_DONTMOVEWP) |
|---|
| 3034 | m_Unit->SetPosition(m_destinationX, m_destinationY, m_destinationZ, wayO, true); |
|---|
| 3035 | else |
|---|
| 3036 | m_Unit->SetPosition(m_destinationX, m_destinationY, m_destinationZ, m_Unit->GetOrientation(), true); |
|---|
| 3037 | |
|---|
| 3038 | m_destinationX = m_destinationY = m_destinationZ = 0; |
|---|
| 3039 | m_timeMoved = 0; |
|---|
| 3040 | m_timeToMove = 0; |
|---|
| 3041 | } |
|---|
| 3042 | else |
|---|
| 3043 | { |
|---|
| 3044 | //Move Server Side Update |
|---|
| 3045 | float q = (float)m_timeMoved / (float)m_timeToMove; |
|---|
| 3046 | float x = m_Unit->GetPositionX() + (m_destinationX - m_Unit->GetPositionX()) * q; |
|---|
| 3047 | float y = m_Unit->GetPositionY() + (m_destinationY - m_Unit->GetPositionY()) * q; |
|---|
| 3048 | float z = m_Unit->GetPositionZ() + (m_destinationZ - m_Unit->GetPositionZ()) * q; |
|---|
| 3049 | |
|---|
| 3050 | //Andy |
|---|
| 3051 | if (sWorld.Collision) { |
|---|
| 3052 | float target_land_z=0.0f; |
|---|
| 3053 | if( m_Unit->GetMapMgr() != NULL ) |
|---|
| 3054 | { |
|---|
| 3055 | if(m_moveFly != true) |
|---|
| 3056 | { |
|---|
| 3057 | target_land_z = CollideInterface.GetHeight(m_Unit->GetMapId(), x, y, z + 2.0f); |
|---|
| 3058 | if ( target_land_z == NO_WMO_HEIGHT ) |
|---|
| 3059 | { |
|---|
| 3060 | target_land_z = m_Unit->GetMapMgr()->GetLandHeight(x, y); |
|---|
| 3061 | if ( target_land_z == 999999.0f ) |
|---|
| 3062 | target_land_z = z; |
|---|
| 3063 | } |
|---|
| 3064 | } |
|---|
| 3065 | |
|---|
| 3066 | if ( z > m_Unit->GetMapMgr()->GetWaterHeight( m_nextPosX, m_nextPosY ) && target_land_z != 0.0f ) |
|---|
| 3067 | z = target_land_z; |
|---|
| 3068 | } |
|---|
| 3069 | } |
|---|
| 3070 | |
|---|
| 3071 | m_Unit->SetPosition(x, y, z, m_Unit->GetOrientation()); |
|---|
| 3072 | |
|---|
| 3073 | m_timeToMove -= m_timeMoved; |
|---|
| 3074 | m_timeMoved = 0; |
|---|
| 3075 | m_moveTimer = (UNIT_MOVEMENT_INTERPOLATE_INTERVAL < m_timeToMove) ? UNIT_MOVEMENT_INTERPOLATE_INTERVAL : m_timeToMove; |
|---|
| 3076 | } |
|---|
| 3077 | //**** Movement related stuff that should be done after a move update (Keeps Client and Server Synced) ****// |
|---|
| 3078 | //**** Process the Pending Move ****// |
|---|
| 3079 | if(m_nextPosX != 0.0f && m_nextPosY != 0.0f) |
|---|
| 3080 | { |
|---|
| 3081 | UpdateMove(); |
|---|
| 3082 | } |
|---|
| 3083 | } |
|---|
| 3084 | } |
|---|
| 3085 | else if(m_creatureState == STOPPED && (m_AIState == STATE_IDLE || m_AIState == STATE_SCRIPTMOVE) && !m_moveTimer && !m_timeToMove && UnitToFollow == NULL) //creature is stopped and out of Combat |
|---|
| 3086 | { |
|---|
| 3087 | if(sWorld.getAllowMovement() == false) //is creature movement enabled? |
|---|
| 3088 | return; |
|---|
| 3089 | |
|---|
| 3090 | if(m_Unit->GetUInt32Value(UNIT_FIELD_DISPLAYID) == 5233) //if Spirit Healer don't move |
|---|
| 3091 | return; |
|---|
| 3092 | |
|---|
| 3093 | // do we have a formation? |
|---|
| 3094 | if(m_formationLinkSqlId != 0) |
|---|
| 3095 | { |
|---|
| 3096 | if(!m_formationLinkTarget) |
|---|
| 3097 | { |
|---|
| 3098 | // haven't found our target yet |
|---|
| 3099 | Creature * c = static_cast<Creature*>(m_Unit); |
|---|
| 3100 | if(!c->haslinkupevent) |
|---|
| 3101 | { |
|---|
| 3102 | // register linkup event |
|---|
| 3103 | c->haslinkupevent = true; |
|---|
| 3104 | sEventMgr.AddEvent(c, &Creature::FormationLinkUp, m_formationLinkSqlId, |
|---|
| 3105 | EVENT_CREATURE_FORMATION_LINKUP, 1000, 0,EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT); |
|---|
| 3106 | } |
|---|
| 3107 | } |
|---|
| 3108 | else |
|---|
| 3109 | { |
|---|
| 3110 | // we've got a formation target, set unittofollow to this |
|---|
| 3111 | UnitToFollow = m_formationLinkTarget; |
|---|
| 3112 | FollowDistance = m_formationFollowDistance; |
|---|
| 3113 | m_fallowAngle = m_formationFollowAngle; |
|---|
| 3114 | } |
|---|
| 3115 | } |
|---|
| 3116 | if(UnitToFollow == 0) |
|---|
| 3117 | { |
|---|
| 3118 | // no formation, use waypoints |
|---|
| 3119 | int destpoint = -1; |
|---|
| 3120 | |
|---|
| 3121 | // If creature has no waypoints just wander aimlessly around spawnpoint |
|---|
| 3122 | if(GetWayPointsCount()==0) //no waypoints |
|---|
| 3123 | { |
|---|
| 3124 | /* if(m_moveRandom) |
|---|
| 3125 | { |
|---|
| 3126 | if((rand()%10)==0) |
|---|
| 3127 | { |
|---|
| 3128 | float wanderDistance = rand()%4 + 2; |
|---|
| 3129 | float wanderX = ((wanderDistance*rand()) / RAND_MAX) - wanderDistance / 2; |
|---|
| 3130 | float wanderY = ((wanderDistance*rand()) / RAND_MAX) - wanderDistance / 2; |
|---|
| 3131 | float wanderZ = 0; // FIX ME ( I don't know how to get appropriate Z coord, maybe use client height map data) |
|---|
| 3132 | |
|---|
| 3133 | if(m_Unit->CalcDistance(m_Unit->GetPositionX(), m_Unit->GetPositionY(), m_Unit->GetPositionZ(), ((Creature*)m_Unit)->respawn_cord[0], ((Creature*)m_Unit)->respawn_cord[1], ((Creature*)m_Unit)->respawn_cord[2])>15) |
|---|
| 3134 | { |
|---|
| 3135 | //return home |
|---|
| 3136 | MoveTo(((Creature*)m_Unit)->respawn_cord[0],((Creature*)m_Unit)->respawn_cord[1],((Creature*)m_Unit)->respawn_cord[2],false); |
|---|
| 3137 | } |
|---|
| 3138 | else |
|---|
| 3139 | { |
|---|
| 3140 | MoveTo(m_Unit->GetPositionX() + wanderX, m_Unit->GetPositionY() + wanderY, m_Unit->GetPositionZ() + wanderZ,false); |
|---|
| 3141 | } |
|---|
| 3142 | } |
|---|
| 3143 | } |
|---|
| 3144 | */ |
|---|
| 3145 | return; |
|---|
| 3146 | } |
|---|
| 3147 | else //we do have waypoints |
|---|
| 3148 | { |
|---|
| 3149 | if(m_moveType == MOVEMENTTYPE_RANDOMWP) //is random move on if so move to a random waypoint |
|---|
| 3150 | { |
|---|
| 3151 | if(GetWayPointsCount() > 1) |
|---|
| 3152 | destpoint = RandomUInt((uint32)GetWayPointsCount()); |
|---|
| 3153 | } |
|---|
| 3154 | else if (m_moveType == MOVEMENTTYPE_CIRCLEWP) //random move is not on lets follow the path in circles |
|---|
| 3155 | { |
|---|
| 3156 | // 1 -> 10 then 1 -> 10 |
|---|
| 3157 | m_currentWaypoint++; |
|---|
| 3158 | if (m_currentWaypoint > GetWayPointsCount()) m_currentWaypoint = 1; //Happens when you delete last wp seems to continue ticking |
|---|
| 3159 | destpoint = m_currentWaypoint; |
|---|
| 3160 | m_moveBackward = false; |
|---|
| 3161 | } |
|---|
| 3162 | else if(m_moveType == MOVEMENTTYPE_WANTEDWP)//Move to wanted wp |
|---|
| 3163 | { |
|---|
| 3164 | if(m_currentWaypoint) |
|---|
| 3165 | { |
|---|
| 3166 | if(GetWayPointsCount() > 0) |
|---|
| 3167 | { |
|---|
| 3168 | destpoint = m_currentWaypoint; |
|---|
| 3169 | } |
|---|
| 3170 | else |
|---|
| 3171 | destpoint = -1; |
|---|
| 3172 | } |
|---|
| 3173 | } |
|---|
| 3174 | else if(m_moveType == MOVEMENTTYPE_FORWARDTHANSTOP)// move to end, then stop |
|---|
| 3175 | { |
|---|
| 3176 | ++m_currentWaypoint; |
|---|
| 3177 | if(m_currentWaypoint > GetWayPointsCount()) |
|---|
| 3178 | { |
|---|
| 3179 | //hmm maybe we should stop being path walker since we are waiting here anyway |
|---|
| 3180 | destpoint = -1; |
|---|
| 3181 | } |
|---|
| 3182 | else |
|---|
| 3183 | destpoint = m_currentWaypoint; |
|---|
| 3184 | } |
|---|
| 3185 | else if(m_moveType != MOVEMENTTYPE_QUEST && m_moveType != MOVEMENTTYPE_DONTMOVEWP)//4 Unused |
|---|
| 3186 | { |
|---|
| 3187 | // 1 -> 10 then 10 -> 1 |
|---|
| 3188 | if (m_currentWaypoint > GetWayPointsCount()) m_currentWaypoint = 1; //Happens when you delete last wp seems to continue ticking |
|---|
| 3189 | if (m_currentWaypoint == GetWayPointsCount()) // Are we on the last waypoint? if so walk back |
|---|
| 3190 | m_moveBackward = true; |
|---|
| 3191 | if (m_currentWaypoint == 1) // Are we on the first waypoint? if so lets goto the second waypoint |
|---|
| 3192 | m_moveBackward = false; |
|---|
| 3193 | if (!m_moveBackward) // going 1..n |
|---|
| 3194 | destpoint = ++m_currentWaypoint; |
|---|
| 3195 | else // going n..1 |
|---|
| 3196 | destpoint = --m_currentWaypoint; |
|---|
| 3197 | } |
|---|
| 3198 | |
|---|
| 3199 | if(destpoint != -1) |
|---|
| 3200 | { |
|---|
| 3201 | WayPoint* wp = getWayPoint(destpoint); |
|---|
| 3202 | if(wp) |
|---|
| 3203 | { |
|---|
| 3204 | if(!m_moveBackward) |
|---|
| 3205 | { |
|---|
| 3206 | if((wp->forwardskinid != 0) && (GetUnit()->GetUInt32Value(UNIT_FIELD_DISPLAYID) != wp->forwardskinid)) |
|---|
| 3207 | { |
|---|
| 3208 | GetUnit()->SetUInt32Value(UNIT_FIELD_DISPLAYID, wp->forwardskinid); |
|---|
| 3209 | GetUnit()->EventModelChange(); |
|---|
| 3210 | } |
|---|
| 3211 | } |
|---|
| 3212 | else |
|---|
| 3213 | { |
|---|
| 3214 | if((wp->backwardskinid != 0) && (GetUnit()->GetUInt32Value(UNIT_FIELD_DISPLAYID) != wp->backwardskinid)) |
|---|
| 3215 | { |
|---|
| 3216 | GetUnit()->SetUInt32Value(UNIT_FIELD_DISPLAYID, wp->backwardskinid); |
|---|
| 3217 | GetUnit()->EventModelChange(); |
|---|
| 3218 | } |
|---|
| 3219 | } |
|---|
| 3220 | m_moveFly = (wp->flags == 768) ? 1 : 0; |
|---|
| 3221 | m_moveRun = (wp->flags == 256) ? 1 : 0; |
|---|
| 3222 | MoveTo(wp->x, wp->y, wp->z, 0); |
|---|
| 3223 | } |
|---|
| 3224 | } |
|---|
| 3225 | } |
|---|
| 3226 | } |
|---|
| 3227 | } |
|---|
| 3228 | |
|---|
| 3229 | //Fear Code |
|---|
| 3230 | if(m_AIState == STATE_FEAR && UnitToFear != NULL && m_creatureState == STOPPED) |
|---|
| 3231 | { |
|---|
| 3232 | if(getMSTime() > m_FearTimer) // Wait at point for x ms ;) |
|---|
| 3233 | { |
|---|
| 3234 | float Fx; |
|---|
| 3235 | float Fy; |
|---|
| 3236 | float Fz; |
|---|
| 3237 | |
|---|
| 3238 | if( sWorld.DisableFearMovement ) |
|---|
| 3239 | { |
|---|
| 3240 | if( m_Unit->GetMapId() == 529|| m_Unit->GetMapId() == 566 || m_Unit->GetMapId() == 489 || m_Unit->GetMapId() == 572 || m_Unit->GetMapId() == 562 || m_Unit->GetMapId() == 559 ) |
|---|
| 3241 | { |
|---|
| 3242 | return; |
|---|
| 3243 | } |
|---|
| 3244 | } |
|---|
| 3245 | // Calculate new angle to target. |
|---|
| 3246 | float Fo = m_Unit->calcRadAngle(UnitToFear->GetPositionX(), UnitToFear->GetPositionY(), m_Unit->GetPositionX(), m_Unit->GetPositionY()); |
|---|
| 3247 | double fAngleAdd = RandomDouble(((M_PI/2) * 2)) - (M_PI/2); |
|---|
| 3248 | Fo += (float)fAngleAdd; |
|---|
| 3249 | |
|---|
| 3250 | float dist = m_Unit->CalcDistance(UnitToFear); |
|---|
| 3251 | if(dist > 30.0f || (Rand(25) && dist > 10.0f)) // not too far or too close |
|---|
| 3252 | { |
|---|
| 3253 | if( m_Unit->GetMapId() == 572 || m_Unit->GetMapId() == 562 || m_Unit->GetMapId() == 559 ) //GET MAP ID |
|---|
| 3254 | { |
|---|
| 3255 | Fx = m_Unit->GetPositionX(); |
|---|
| 3256 | Fy = m_Unit->GetPositionY(); |
|---|
| 3257 | } |
|---|
| 3258 | else |
|---|
| 3259 | { |
|---|
| 3260 | Fx = m_Unit->GetPositionX() - (RandomFloat(15.f)+5.0f)*cosf(Fo); |
|---|
| 3261 | Fy = m_Unit->GetPositionY() - (RandomFloat(15.f)+5.0f)*sinf(Fo); |
|---|
| 3262 | } |
|---|
| 3263 | } |
|---|
| 3264 | else |
|---|
| 3265 | { |
|---|
| 3266 | Fx = m_Unit->GetPositionX() + (RandomFloat(20.f)+5.0f)*cosf(Fo); |
|---|
| 3267 | Fy = m_Unit->GetPositionY() + (RandomFloat(20.f)+5.0f)*sinf(Fo); |
|---|
| 3268 | } |
|---|
| 3269 | // Check if this point is in water. |
|---|
| 3270 | float wl = m_Unit->GetMapMgr()->GetWaterHeight(Fx, Fy); |
|---|
| 3271 | // uint8 wt = m_Unit->GetMapMgr()->GetWaterType(Fx, Fy); |
|---|
| 3272 | |
|---|
| 3273 | if (sWorld.Collision) { |
|---|
| 3274 | Fz = CollideInterface.GetHeight(m_Unit->GetMapId(), Fx, Fy, m_Unit->GetPositionZ() + 2.0f); |
|---|
| 3275 | if( Fz == NO_WMO_HEIGHT ) |
|---|
| 3276 | Fz = m_Unit->GetMapMgr()->GetLandHeight(Fx, Fy); |
|---|
| 3277 | else |
|---|
| 3278 | { |
|---|
| 3279 | if( CollideInterface.GetFirstPoint(m_Unit->GetMapId(), m_Unit->GetPositionX(), m_Unit->GetPositionY(), m_Unit->GetPositionZ() + 2.0f, |
|---|
| 3280 | Fx, Fy, Fz + 2.0f, Fx, Fy, Fz, -1.0f) ) |
|---|
| 3281 | { |
|---|
| 3282 | //Fz = CollideInterface.GetHeight(m_Unit->GetMapId(), Fx, Fy, m_Unit->GetPositionZ() + 2.0f); |
|---|
| 3283 | } |
|---|
| 3284 | } |
|---|
| 3285 | |
|---|
| 3286 | if( fabs( m_Unit->GetPositionZ() - Fz ) > 10.0f || |
|---|
| 3287 | ( wl != 0.0f && Fz < wl ) ) // in water |
|---|
| 3288 | { |
|---|
| 3289 | m_FearTimer=getMSTime() + 500; |
|---|
| 3290 | } |
|---|
| 3291 | else if( CollideInterface.CheckLOS(m_Unit->GetMapId(), m_Unit->GetPositionX(), m_Unit->GetPositionY(), m_Unit->GetPositionZ() + 2.0f, Fx, Fy, Fz) ) |
|---|
| 3292 | { |
|---|
| 3293 | MoveTo(Fx, Fy, Fz, Fo); |
|---|
| 3294 | m_FearTimer = m_totalMoveTime + getMSTime() + 400; |
|---|
| 3295 | } |
|---|
| 3296 | else |
|---|
| 3297 | { |
|---|
| 3298 | StopMovement(0); |
|---|
| 3299 | } |
|---|
| 3300 | } else { |
|---|
| 3301 | Fz = m_Unit->GetMapMgr()->GetLandHeight(Fx, Fy); |
|---|
| 3302 | if(fabs(m_Unit->GetPositionZ()-Fz) > 4 || (Fz != 0.0f && Fz < (wl-2.0f))) |
|---|
| 3303 | m_FearTimer=getMSTime()+100; |
|---|
| 3304 | else |
|---|
| 3305 | { |
|---|
| 3306 | MoveTo(Fx, Fy, Fz, Fo); |
|---|
| 3307 | m_FearTimer = m_totalMoveTime + getMSTime() + 200; |
|---|
| 3308 | } |
|---|
| 3309 | } |
|---|
| 3310 | } |
|---|
| 3311 | } |
|---|
| 3312 | |
|---|
| 3313 | // Wander AI movement code |
|---|
| 3314 | if(m_AIState == STATE_WANDER && m_creatureState == STOPPED) |
|---|
| 3315 | { |
|---|
| 3316 | if(getMSTime() < m_WanderTimer) // is it time to move again? |
|---|
| 3317 | return; |
|---|
| 3318 | |
|---|
| 3319 | // calculate a random distance and angle to move |
|---|
| 3320 | float wanderD = RandomFloat(2.0f) + 2.0f; |
|---|
| 3321 | float wanderO = RandomFloat(6.283f); |
|---|
| 3322 | float wanderX = m_Unit->GetPositionX() + wanderD * cosf(wanderO); |
|---|
| 3323 | float wanderY = m_Unit->GetPositionY() + wanderD * sinf(wanderO); |
|---|
| 3324 | |
|---|
| 3325 | if (sWorld.Collision) { |
|---|
| 3326 | float wanderZ = CollideInterface.GetHeight(m_Unit->GetMapId(), wanderX, wanderY, m_Unit->GetPositionZ() + 2.0f); |
|---|
| 3327 | float wanderZ2 = wanderZ; |
|---|
| 3328 | if( wanderZ == NO_WMO_HEIGHT ) |
|---|
| 3329 | wanderZ = m_Unit->GetMapMgr()->GetLandHeight(wanderX, wanderY); |
|---|
| 3330 | else |
|---|
| 3331 | { |
|---|
| 3332 | if( CollideInterface.GetFirstPoint(m_Unit->GetMapId(), m_Unit->GetPositionX(), m_Unit->GetPositionY(), m_Unit->GetPositionZ() + 2.0f, |
|---|
| 3333 | wanderX, wanderY, wanderZ + 2.0f, wanderX, wanderY, wanderZ, -1.0f) ) |
|---|
| 3334 | { |
|---|
| 3335 | //wanderZ = CollideInterface.GetHeight(m_Unit->GetMapId(), wanderX, wanderY, m_Unit->GetPositionZ() + 2.0f); |
|---|
| 3336 | } |
|---|
| 3337 | else |
|---|
| 3338 | wanderZ = wanderZ2; |
|---|
| 3339 | } |
|---|
| 3340 | |
|---|
| 3341 | if( fabs( m_Unit->GetPositionZ() - wanderZ ) > 10.0f ) |
|---|
| 3342 | { |
|---|
| 3343 | m_WanderTimer=getMSTime() + 1000; |
|---|
| 3344 | } |
|---|
| 3345 | else if(CollideInterface.CheckLOS(m_Unit->GetMapId(), m_Unit->GetPositionX(), m_Unit->GetPositionY(), m_Unit->GetPositionZ() + 2.0f, wanderX, wanderY, wanderZ)) |
|---|
| 3346 | { |
|---|
| 3347 | m_Unit->SetOrientation(wanderO); |
|---|
| 3348 | MoveTo(wanderX, wanderY, wanderZ, wanderO); |
|---|
| 3349 | m_WanderTimer = getMSTime() + m_totalMoveTime + 300; // time till next move (+ pause) |
|---|
| 3350 | } |
|---|
| 3351 | else |
|---|
| 3352 | { |
|---|
| 3353 | StopMovement(0); |
|---|
| 3354 | } |
|---|
| 3355 | } else { |
|---|
| 3356 | float wanderZ = m_Unit->GetMapMgr()->GetLandHeight(wanderX, wanderY); |
|---|
| 3357 | |
|---|
| 3358 | // without these next checks we could fall through the "ground" (WMO) and get stuck |
|---|
| 3359 | // wander won't work correctly in cities until we get some way to fix this and remove these checks |
|---|
| 3360 | float currentZ = m_Unit->GetPositionZ(); |
|---|
| 3361 | float landZ = m_Unit->GetMapMgr()->GetLandHeight(m_Unit->GetPositionX(), m_Unit->GetPositionY()); |
|---|
| 3362 | |
|---|
| 3363 | if( currentZ > landZ + 1.0f // are we more than 1yd above ground? (possible WMO) |
|---|
| 3364 | || wanderZ < currentZ - 5.0f // is our destination land height too low? (possible WMO) |
|---|
| 3365 | || wanderZ > currentZ + wanderD) // is our destination too high to climb? |
|---|
| 3366 | { |
|---|
| 3367 | m_WanderTimer = getMSTime() + 1000; // wait 1 second before we try again |
|---|
| 3368 | return; |
|---|
| 3369 | } |
|---|
| 3370 | |
|---|
| 3371 | m_Unit->SetOrientation(wanderO); |
|---|
| 3372 | MoveTo(wanderX, wanderY, wanderZ, wanderO); |
|---|
| 3373 | m_WanderTimer = getMSTime() + m_totalMoveTime + 300; // time till next move (+ pause) |
|---|
| 3374 | } |
|---|
| 3375 | } |
|---|
| 3376 | |
|---|
| 3377 | //Unit Follow Code |
|---|
| 3378 | if(UnitToFollow != NULL) |
|---|
| 3379 | { |
|---|
| 3380 | #if !defined(WIN32) && !defined(HACKY_CRASH_FIXES) |
|---|
| 3381 | if( UnitToFollow->event_GetCurrentInstanceId() != m_Unit->event_GetCurrentInstanceId() || !UnitToFollow->IsInWorld() ) |
|---|
| 3382 | UnitToFollow = NULL; |
|---|
| 3383 | else |
|---|
| 3384 | { |
|---|
| 3385 | #else |
|---|
| 3386 | __try |
|---|
| 3387 | { |
|---|
| 3388 | if( UnitToFollow->event_GetCurrentInstanceId() != m_Unit->event_GetCurrentInstanceId() || !UnitToFollow->IsInWorld() ) |
|---|
| 3389 | UnitToFollow = NULL; |
|---|
| 3390 | else |
|---|
| 3391 | { |
|---|
| 3392 | #endif |
|---|
| 3393 | if(m_AIState == STATE_IDLE || m_AIState == STATE_FOLLOWING) |
|---|
| 3394 | { |
|---|
| 3395 | float dist = m_Unit->GetDistanceSq(UnitToFollow); |
|---|
| 3396 | |
|---|
| 3397 | // re-calculate orientation based on target's movement |
|---|
| 3398 | if(m_lastFollowX != UnitToFollow->GetPositionX() || |
|---|
| 3399 | m_lastFollowY != UnitToFollow->GetPositionY()) |
|---|
| 3400 | { |
|---|
| 3401 | float dx = UnitToFollow->GetPositionX() - m_Unit->GetPositionX(); |
|---|
| 3402 | float dy = UnitToFollow->GetPositionY() - m_Unit->GetPositionY(); |
|---|
| 3403 | if(dy != 0.0f) |
|---|
| 3404 | { |
|---|
| 3405 | float angle = atan2(dx,dy); |
|---|
| 3406 | m_Unit->SetOrientation(angle); |
|---|
| 3407 | } |
|---|
| 3408 | m_lastFollowX = UnitToFollow->GetPositionX(); |
|---|
| 3409 | m_lastFollowY = UnitToFollow->GetPositionY(); |
|---|
| 3410 | } |
|---|
| 3411 | |
|---|
| 3412 | if (dist > (FollowDistance*FollowDistance)) //if out of range |
|---|
| 3413 | { |
|---|
| 3414 | m_AIState = STATE_FOLLOWING; |
|---|
| 3415 | |
|---|
| 3416 | if(dist > 25.0f) //25 yard away lets run else we will loose the them |
|---|
| 3417 | m_moveRun = true; |
|---|
| 3418 | else |
|---|
| 3419 | m_moveRun = false; |
|---|
| 3420 | |
|---|
| 3421 | if(m_AIType == AITYPE_PET || UnitToFollow == m_formationLinkTarget) //Unit is Pet/formation |
|---|
| 3422 | { |
|---|
| 3423 | if(dist > 900.0f/*30*/) |
|---|
| 3424 | m_moveSprint = true; |
|---|
| 3425 | |
|---|
| 3426 | float delta_x = UnitToFollow->GetPositionX(); |
|---|
| 3427 | float delta_y = UnitToFollow->GetPositionY(); |
|---|
| 3428 | float d = 3; |
|---|
| 3429 | if(m_formationLinkTarget) |
|---|
| 3430 | d = m_formationFollowDistance; |
|---|
| 3431 | |
|---|
| 3432 | MoveTo(delta_x+(d*(cosf(m_fallowAngle+UnitToFollow->GetOrientation()))), |
|---|
| 3433 | delta_y+(d*(sinf(m_fallowAngle+UnitToFollow->GetOrientation()))), |
|---|
| 3434 | UnitToFollow->GetPositionZ(),UnitToFollow->GetOrientation()); |
|---|
| 3435 | } |
|---|
| 3436 | else |
|---|
| 3437 | { |
|---|
| 3438 | _CalcDestinationAndMove(UnitToFollow, FollowDistance); |
|---|
| 3439 | } |
|---|
| 3440 | } |
|---|
| 3441 | } |
|---|
| 3442 | } |
|---|
| 3443 | #if defined(WIN32) && defined(HACKY_CRASH_FIXES) |
|---|
| 3444 | } |
|---|
| 3445 | __except(EXCEPTION_EXECUTE_HANDLER) |
|---|
| 3446 | { |
|---|
| 3447 | UnitToFollow = NULL; |
|---|
| 3448 | } |
|---|
| 3449 | #endif |
|---|
| 3450 | } |
|---|
| 3451 | } |
|---|
| 3452 | |
|---|
| 3453 | void AIInterface::CastSpell(Unit* caster, SpellEntry *spellInfo, SpellCastTargets targets) |
|---|
| 3454 | { |
|---|
| 3455 | if( m_AIType != AITYPE_PET && disable_spell ) |
|---|
| 3456 | return; |
|---|
| 3457 | |
|---|
| 3458 | // Stop movement while casting. |
|---|
| 3459 | m_AIState = STATE_CASTING; |
|---|
| 3460 | #ifdef _AI_DEBUG |
|---|
| 3461 | sLog.outString("AI DEBUG: Unit %u casting spell %s on target "I64FMT, caster->GetEntry(), |
|---|
| 3462 | sSpellStore.LookupString(spellInfo->Name), targets.m_unitTarget); |
|---|
| 3463 | #endif |
|---|
| 3464 | |
|---|
| 3465 | //i wonder if this will lead to a memory leak :S |
|---|
| 3466 | Spell *nspell = new Spell(caster, spellInfo, false, NULL); |
|---|
| 3467 | if (!nspell) |
|---|
| 3468 | return; |
|---|
| 3469 | nspell->prepare(&targets); |
|---|
| 3470 | } |
|---|
| 3471 | |
|---|
| 3472 | SpellEntry *AIInterface::getSpellEntry(uint32 spellId) |
|---|
| 3473 | { |
|---|
| 3474 | SpellEntry *spellInfo = dbcSpell.LookupEntry(spellId ); |
|---|
| 3475 | |
|---|
| 3476 | if(!spellInfo) |
|---|
| 3477 | { |
|---|
| 3478 | sLog.outError("WORLD: unknown spell id %i\n", spellId); |
|---|
| 3479 | return NULL; |
|---|
| 3480 | } |
|---|
| 3481 | |
|---|
| 3482 | return spellInfo; |
|---|
| 3483 | } |
|---|
| 3484 | |
|---|
| 3485 | SpellCastTargets AIInterface::setSpellTargets(SpellEntry *spellInfo, Unit* target) |
|---|
| 3486 | { |
|---|
| 3487 | SpellCastTargets targets; |
|---|
| 3488 | targets.m_unitTarget = target ? target->GetGUID() : 0; |
|---|
| 3489 | targets.m_itemTarget = 0; |
|---|
| 3490 | targets.m_srcX = targets.m_destX = m_Unit->GetPositionX(); |
|---|
| 3491 | targets.m_srcY = targets.m_destY = m_Unit->GetPositionY(); |
|---|
| 3492 | targets.m_srcZ = targets.m_destZ = m_Unit->GetPositionZ(); |
|---|
| 3493 | |
|---|
| 3494 | if(m_nextSpell->spelltargetType == TTYPE_SINGLETARGET) |
|---|
| 3495 | { |
|---|
| 3496 | targets.m_targetMask = 2; |
|---|
| 3497 | } |
|---|
| 3498 | else if(m_nextSpell->spelltargetType == TTYPE_SOURCE) |
|---|
| 3499 | { |
|---|
| 3500 | targets.m_targetMask = 32; |
|---|
| 3501 | // targets.m_srcX = m_Unit->GetPositionX(); |
|---|
| 3502 | // targets.m_srcY = m_Unit->GetPositionY(); |
|---|
| 3503 | // targets.m_srcZ = m_Unit->GetPositionZ(); |
|---|
| 3504 | } |
|---|
| 3505 | else if(m_nextSpell->spelltargetType == TTYPE_DESTINATION) |
|---|
| 3506 | { |
|---|
| 3507 | targets.m_targetMask = 64; |
|---|
| 3508 | if( target != NULL ) |
|---|
| 3509 | { |
|---|
| 3510 | targets.m_destX = target->GetPositionX(); |
|---|
| 3511 | targets.m_destY = target->GetPositionY(); |
|---|
| 3512 | targets.m_destZ = target->GetPositionZ(); |
|---|
| 3513 | } |
|---|
| 3514 | else |
|---|
| 3515 | { |
|---|
| 3516 | targets.m_destX = m_Unit->GetPositionX(); |
|---|
| 3517 | targets.m_destY = m_Unit->GetPositionY(); |
|---|
| 3518 | targets.m_destZ = m_Unit->GetPositionZ(); |
|---|
| 3519 | } |
|---|
| 3520 | } |
|---|
| 3521 | else if(m_nextSpell->spelltargetType == TTYPE_CASTER) |
|---|
| 3522 | { |
|---|
| 3523 | targets.m_targetMask = 2; |
|---|
| 3524 | targets.m_unitTarget = m_Unit->GetGUID(); |
|---|
| 3525 | } |
|---|
| 3526 | |
|---|
| 3527 | return targets; |
|---|
| 3528 | } |
|---|
| 3529 | |
|---|
| 3530 | AI_Spell *AIInterface::getSpell() |
|---|
| 3531 | { |
|---|
| 3532 | if(next_spell_time > (uint32)UNIXTIME) |
|---|
| 3533 | return NULL; |
|---|
| 3534 | |
|---|
| 3535 | waiting_for_cooldown = false; |
|---|
| 3536 | |
|---|
| 3537 | // look at our spells |
|---|
| 3538 | AI_Spell * sp = NULL; |
|---|
| 3539 | AI_Spell * def_spell = NULL; |
|---|
| 3540 | uint32 cool_time=0; |
|---|
| 3541 | uint32 cool_time2; |
|---|
| 3542 | uint32 nowtime = getMSTime(); |
|---|
| 3543 | |
|---|
| 3544 | if(m_Unit->IsPet()) |
|---|
| 3545 | { |
|---|
| 3546 | sp = def_spell = ((Pet*)m_Unit)->HandleAutoCastEvent(); |
|---|
| 3547 | } |
|---|
| 3548 | else |
|---|
| 3549 | { |
|---|
| 3550 | for(list<AI_Spell*>::iterator itr = m_spells.begin(); itr != m_spells.end();) |
|---|
| 3551 | { |
|---|
| 3552 | sp = *itr; |
|---|
| 3553 | ++itr; |
|---|
| 3554 | if( |
|---|
| 3555 | // sp->cooldowntime && //Zack : no need for this double check here |
|---|
| 3556 | nowtime < sp->cooldowntime |
|---|
| 3557 | // && sp->procChance >= 100 //Zack: why was this put here ? It makes mobs spam spells like no tomorrow |
|---|
| 3558 | ) |
|---|
| 3559 | { |
|---|
| 3560 | cool_time2=sp->cooldowntime-nowtime; |
|---|
| 3561 | if(!cool_time || cool_time2<cool_time) |
|---|
| 3562 | cool_time=cool_time2; |
|---|
| 3563 | |
|---|
| 3564 | waiting_for_cooldown = true; |
|---|
| 3565 | continue; |
|---|
| 3566 | } |
|---|
| 3567 | |
|---|
| 3568 | if(sp->procCount && sp->procCounter >= sp->procCount) |
|---|
| 3569 | continue; |
|---|
| 3570 | |
|---|
| 3571 | if(sp->agent == AGENT_SPELL) |
|---|
| 3572 | { |
|---|
| 3573 | if (sp->spellType == STYPE_BUFF) |
|---|
| 3574 | { |
|---|
| 3575 | // cast the buff at requested percent only if we don't have it already |
|---|
| 3576 | if(sp->procChance >= 100 || Rand(sp->procChance)) |
|---|
| 3577 | { |
|---|
| 3578 | if(!m_Unit->HasBuff(sp->spell->Id)) |
|---|
| 3579 | { |
|---|
| 3580 | return sp; |
|---|
| 3581 | } |
|---|
| 3582 | } |
|---|
| 3583 | } |
|---|
| 3584 | else |
|---|
| 3585 | { |
|---|
| 3586 | if(def_spell!=0) |
|---|
| 3587 | continue; |
|---|
| 3588 | |
|---|
| 3589 | // cast the spell at requested percent. |
|---|
| 3590 | if(sp->procChance >= 100 || Rand(sp->procChance)) |
|---|
| 3591 | { |
|---|
| 3592 | //focus/mana requirement |
|---|
| 3593 | switch(sp->spell->powerType) |
|---|
| 3594 | { |
|---|
| 3595 | case POWER_TYPE_MANA: |
|---|
| 3596 | if(m_Unit->GetUInt32Value(UNIT_FIELD_POWER1) < sp->spell->manaCost) |
|---|
| 3597 | continue; |
|---|
| 3598 | break; |
|---|
| 3599 | |
|---|
| 3600 | case POWER_TYPE_FOCUS: |
|---|
| 3601 | if(m_Unit->GetUInt32Value(UNIT_FIELD_POWER3) < sp->spell->manaCost) |
|---|
| 3602 | continue; |
|---|
| 3603 | break; |
|---|
| 3604 | } |
|---|
| 3605 | def_spell = sp; |
|---|
| 3606 | //we got a selected spell, we can exit loop now |
|---|
| 3607 | break; |
|---|
| 3608 | } |
|---|
| 3609 | else //we failed casting it due to given chance, we activate false cooldown on it to not spam searching this list |
|---|
| 3610 | { |
|---|
| 3611 | // sp->cooldowntime = nowtime + sp->cooldown / ( sp->procChance + 1 ); |
|---|
| 3612 | cool_time2 = 2000; |
|---|
| 3613 | sp->cooldowntime = nowtime + cool_time2; |
|---|
| 3614 | if( !cool_time || cool_time2 < cool_time ) |
|---|
| 3615 | cool_time = cool_time2; |
|---|
| 3616 | } |
|---|
| 3617 | } |
|---|
| 3618 | } |
|---|
| 3619 | } |
|---|
| 3620 | } |
|---|
| 3621 | |
|---|
| 3622 | if(def_spell) |
|---|
| 3623 | { |
|---|
| 3624 | // set cooldown |
|---|
| 3625 | if(def_spell->procCount) |
|---|
| 3626 | def_spell->procCounter++; |
|---|
| 3627 | |
|---|
| 3628 | if(def_spell->cooldown) |
|---|
| 3629 | def_spell->cooldowntime = nowtime + def_spell->cooldown; |
|---|
| 3630 | |
|---|
| 3631 | waiting_for_cooldown = false; |
|---|
| 3632 | return def_spell; |
|---|
| 3633 | } |
|---|
| 3634 | |
|---|
| 3635 | // save some loops if waiting for cooldownz |
|---|
| 3636 | if(cool_time) |
|---|
| 3637 | { |
|---|
| 3638 | cool_time2 = cool_time / 1000; |
|---|
| 3639 | if(cool_time2) |
|---|
| 3640 | next_spell_time = (uint32)UNIXTIME + cool_time2; |
|---|
| 3641 | } |
|---|
| 3642 | else |
|---|
| 3643 | { |
|---|
| 3644 | next_spell_time = (uint32)UNIXTIME + MOB_SPELLCAST_REFRESH_COOLDOWN_INTERVAL; |
|---|
| 3645 | waiting_for_cooldown = false; |
|---|
| 3646 | } |
|---|
| 3647 | |
|---|
| 3648 | #ifdef _AI_DEBUG |
|---|
| 3649 | sLog.outString("AI DEBUG: Returning no spell for unit %u", m_Unit->GetEntry()); |
|---|
| 3650 | #endif |
|---|
| 3651 | return 0; |
|---|
| 3652 | } |
|---|
| 3653 | |
|---|
| 3654 | void AIInterface::addSpellToList(AI_Spell *sp) |
|---|
| 3655 | { |
|---|
| 3656 | if(!sp || !sp->spell) |
|---|
| 3657 | return; |
|---|
| 3658 | |
|---|
| 3659 | AI_Spell * sp2 = new AI_Spell; |
|---|
| 3660 | memcpy(sp2, sp, sizeof(AI_Spell)); |
|---|
| 3661 | m_spells.push_back(sp2); |
|---|
| 3662 | } |
|---|
| 3663 | |
|---|
| 3664 | uint32 AIInterface::getThreatByGUID(uint64 guid) |
|---|
| 3665 | { |
|---|
| 3666 | |
|---|
| 3667 | if( m_Unit->GetMapMgr() == NULL ) |
|---|
| 3668 | return 0; |
|---|
| 3669 | |
|---|
| 3670 | Unit *obj = m_Unit->GetMapMgr()->GetUnit(guid); |
|---|
| 3671 | if(obj) |
|---|
| 3672 | return getThreatByPtr(obj); |
|---|
| 3673 | |
|---|
| 3674 | return 0; |
|---|
| 3675 | } |
|---|
| 3676 | |
|---|
| 3677 | uint32 AIInterface::getThreatByPtr(Unit* obj) |
|---|
| 3678 | { |
|---|
| 3679 | if( !obj || m_Unit->GetMapMgr() == NULL) |
|---|
| 3680 | return 0; |
|---|
| 3681 | TargetMap::iterator it = m_aiTargets.find(obj->GetGUID()); |
|---|
| 3682 | if(it != m_aiTargets.end()) |
|---|
| 3683 | { |
|---|
| 3684 | Unit *tempUnit = m_Unit->GetMapMgr()->GetUnit(it->first); |
|---|
| 3685 | if (tempUnit) |
|---|
| 3686 | return it->second + tempUnit->GetThreatModifyer(); |
|---|
| 3687 | else |
|---|
| 3688 | return it->second; |
|---|
| 3689 | |
|---|
| 3690 | } |
|---|
| 3691 | return 0; |
|---|
| 3692 | } |
|---|
| 3693 | |
|---|
| 3694 | /* |
|---|
| 3695 | #if defined(WIN32) && defined(HACKY_CRASH_FIXES) |
|---|
| 3696 | __declspec(noinline) bool ___CheckTarget(Unit * ptr, Unit * him) |
|---|
| 3697 | { |
|---|
| 3698 | __try |
|---|
| 3699 | { |
|---|
| 3700 | if( him->GetInstanceID() != ptr->GetInstanceID() || !him->isAlive() || !isAttackable( ptr, him ) ) |
|---|
| 3701 | { |
|---|
| 3702 | return false; |
|---|
| 3703 | } |
|---|
| 3704 | } |
|---|
| 3705 | __except(EXCEPTION_EXECUTE_HANDLER) |
|---|
| 3706 | { |
|---|
| 3707 | return false; |
|---|
| 3708 | } |
|---|
| 3709 | return true; |
|---|
| 3710 | } |
|---|
| 3711 | #endif |
|---|
| 3712 | */ |
|---|
| 3713 | |
|---|
| 3714 | //should return a valid target |
|---|
| 3715 | Unit *AIInterface::GetMostHated() |
|---|
| 3716 | { |
|---|
| 3717 | if( m_Unit->GetMapMgr() == NULL ) |
|---|
| 3718 | return NULL; |
|---|
| 3719 | |
|---|
| 3720 | Unit *ResultUnit= NULL; |
|---|
| 3721 | |
|---|
| 3722 | //override mosthated with taunted target. Basic combat checks are made for it. |
|---|
| 3723 | //What happens if we can't see tauntedby unit ? |
|---|
| 3724 | ResultUnit = getTauntedBy(); |
|---|
| 3725 | if( ResultUnit != NULL ) |
|---|
| 3726 | return ResultUnit; |
|---|
| 3727 | |
|---|
| 3728 | pair<Unit*, int32> currentTarget; |
|---|
| 3729 | currentTarget.first = 0; |
|---|
| 3730 | currentTarget.second = -1; |
|---|
| 3731 | |
|---|
| 3732 | LockAITargets(true); |
|---|
| 3733 | |
|---|
| 3734 | TargetMap::iterator it2 = m_aiTargets.begin(); |
|---|
| 3735 | TargetMap::iterator itr; |
|---|
| 3736 | for(; it2 != m_aiTargets.end();) |
|---|
| 3737 | { |
|---|
| 3738 | itr = it2; |
|---|
| 3739 | ++it2; |
|---|
| 3740 | |
|---|
| 3741 | /* check the target is valid */ |
|---|
| 3742 | /* |
|---|
| 3743 | #if defined(WIN32) && defined(HACKY_CRASH_FIXES) |
|---|
| 3744 | if(!___CheckTarget( m_Unit, itr->first ) ) |
|---|
| 3745 | { |
|---|
| 3746 | if( m_nextTarget == itr->first ) |
|---|
| 3747 | m_nextTarget = NULL; |
|---|
| 3748 | |
|---|
| 3749 | m_aiTargets.erase(itr); |
|---|
| 3750 | continue; |
|---|
| 3751 | } |
|---|
| 3752 | #else |
|---|
| 3753 | if(itr->first->event_GetCurrentInstanceId() != m_Unit->event_GetCurrentInstanceId() || !itr->first->isAlive() || !isAttackable(m_Unit, itr->first)) |
|---|
| 3754 | { |
|---|
| 3755 | m_aiTargets.erase(itr); |
|---|
| 3756 | continue; |
|---|
| 3757 | } |
|---|
| 3758 | #endif |
|---|
| 3759 | */ |
|---|
| 3760 | // this is a much slower version then the previous one but it causes a lot of crashes and that is above speed right now. |
|---|
| 3761 | Unit *ai_t = m_Unit->GetMapMgr()->GetUnit( itr->first ); |
|---|
| 3762 | |
|---|
| 3763 | if( !ai_t || ai_t->GetInstanceID() != m_Unit->GetInstanceID() || !ai_t->isAlive() || !isAttackable( m_Unit, ai_t ) ) |
|---|
| 3764 | { |
|---|
| 3765 | if( GetNextTarget() == ai_t ) |
|---|
| 3766 | SetNextTarget( (Unit*)NULL ); |
|---|
| 3767 | |
|---|
| 3768 | m_aiTargets.erase(itr); |
|---|
| 3769 | continue; |
|---|
| 3770 | } |
|---|
| 3771 | |
|---|
| 3772 | if((itr->second + ai_t->GetThreatModifyer()) > currentTarget.second) |
|---|
| 3773 | { |
|---|
| 3774 | /* new target */ |
|---|
| 3775 | currentTarget.first = ai_t; |
|---|
| 3776 | currentTarget.second = itr->second + ai_t->GetThreatModifyer(); |
|---|
| 3777 | m_currentHighestThreat = currentTarget.second; |
|---|
| 3778 | } |
|---|
| 3779 | |
|---|
| 3780 | /* there are no more checks needed here... the needed checks are done by CheckTarget() */ |
|---|
| 3781 | } |
|---|
| 3782 | |
|---|
| 3783 | LockAITargets(false); |
|---|
| 3784 | |
|---|
| 3785 | return currentTarget.first; |
|---|
| 3786 | } |
|---|
| 3787 | Unit *AIInterface::GetSecondHated() |
|---|
| 3788 | { |
|---|
| 3789 | if( m_Unit->GetMapMgr() == NULL ) |
|---|
| 3790 | return NULL; |
|---|
| 3791 | |
|---|
| 3792 | Unit *ResultUnit=GetMostHated(); |
|---|
| 3793 | |
|---|
| 3794 | pair<Unit*, int32> currentTarget; |
|---|
| 3795 | currentTarget.first = 0; |
|---|
| 3796 | currentTarget.second = -1; |
|---|
| 3797 | |
|---|
| 3798 | LockAITargets(true); |
|---|
| 3799 | |
|---|
| 3800 | TargetMap::iterator it2 = m_aiTargets.begin(); |
|---|
| 3801 | TargetMap::iterator itr; |
|---|
| 3802 | for(; it2 != m_aiTargets.end();) |
|---|
| 3803 | { |
|---|
| 3804 | itr = it2; |
|---|
| 3805 | ++it2; |
|---|
| 3806 | |
|---|
| 3807 | /* check the target is valid */ |
|---|
| 3808 | Unit *ai_t = m_Unit->GetMapMgr()->GetUnit( itr->first ); |
|---|
| 3809 | if(!ai_t || ai_t->GetInstanceID() != m_Unit->GetInstanceID() || !ai_t->isAlive() || !isAttackable(m_Unit, ai_t)) |
|---|
| 3810 | { |
|---|
| 3811 | m_aiTargets.erase(itr); |
|---|
| 3812 | continue; |
|---|
| 3813 | } |
|---|
| 3814 | |
|---|
| 3815 | if((itr->second + ai_t->GetThreatModifyer()) > currentTarget.second && |
|---|
| 3816 | ai_t != ResultUnit) |
|---|
| 3817 | { |
|---|
| 3818 | /* new target */ |
|---|
| 3819 | currentTarget.first = ai_t; |
|---|
| 3820 | currentTarget.second = itr->second + ai_t->GetThreatModifyer(); |
|---|
| 3821 | m_currentHighestThreat = currentTarget.second; |
|---|
| 3822 | } |
|---|
| 3823 | } |
|---|
| 3824 | |
|---|
| 3825 | LockAITargets(false); |
|---|
| 3826 | |
|---|
| 3827 | return currentTarget.first; |
|---|
| 3828 | } |
|---|
| 3829 | bool AIInterface::modThreatByGUID(uint64 guid, int32 mod) |
|---|
| 3830 | { |
|---|
| 3831 | if (!m_aiTargets.size()) |
|---|
| 3832 | return false; |
|---|
| 3833 | |
|---|
| 3834 | if( m_Unit->GetMapMgr() == NULL ) |
|---|
| 3835 | return false; |
|---|
| 3836 | |
|---|
| 3837 | Unit *obj = m_Unit->GetMapMgr()->GetUnit(guid); |
|---|
| 3838 | if(obj) |
|---|
| 3839 | return modThreatByPtr(obj, mod); |
|---|
| 3840 | |
|---|
| 3841 | return false; |
|---|
| 3842 | } |
|---|
| 3843 | |
|---|
| 3844 | bool AIInterface::modThreatByPtr(Unit* obj, int32 mod) |
|---|
| 3845 | { |
|---|
| 3846 | if(!obj) |
|---|
| 3847 | return false; |
|---|
| 3848 | |
|---|
| 3849 | LockAITargets(true); |
|---|
| 3850 | |
|---|
| 3851 | int32 tempthreat; |
|---|
| 3852 | TargetMap::iterator it = m_aiTargets.find(obj->GetGUID()); |
|---|
| 3853 | if(it != m_aiTargets.end()) |
|---|
| 3854 | { |
|---|
| 3855 | it->second += mod; |
|---|
| 3856 | if (it->second < 1) |
|---|
| 3857 | it->second = 1; |
|---|
| 3858 | |
|---|
| 3859 | tempthreat = it->second + obj->GetThreatModifyer(); |
|---|
| 3860 | if (tempthreat < 1) |
|---|
| 3861 | tempthreat = 1; |
|---|
| 3862 | if(tempthreat > m_currentHighestThreat) |
|---|
| 3863 | { |
|---|
| 3864 | // new target! |
|---|
| 3865 | if(!isTaunted) |
|---|
| 3866 | { |
|---|
| 3867 | m_currentHighestThreat = tempthreat; |
|---|
| 3868 | SetNextTarget(obj); |
|---|
| 3869 | } |
|---|
| 3870 | } |
|---|
| 3871 | } |
|---|
| 3872 | else |
|---|
| 3873 | { |
|---|
| 3874 | m_aiTargets.insert( make_pair( obj->GetGUID(), mod ) ); |
|---|
| 3875 | |
|---|
| 3876 | tempthreat = mod + obj->GetThreatModifyer(); |
|---|
| 3877 | if (tempthreat < 1) |
|---|
| 3878 | tempthreat = 1; |
|---|
| 3879 | if(tempthreat > m_currentHighestThreat) |
|---|
| 3880 | { |
|---|
| 3881 | if(!isTaunted) |
|---|
| 3882 | { |
|---|
| 3883 | m_currentHighestThreat = tempthreat; |
|---|
| 3884 | SetNextTarget(obj); |
|---|
| 3885 | } |
|---|
| 3886 | } |
|---|
| 3887 | } |
|---|
| 3888 | |
|---|
| 3889 | LockAITargets(false); |
|---|
| 3890 | |
|---|
| 3891 | if(obj == GetNextTarget()) |
|---|
| 3892 | { |
|---|
| 3893 | // check for a possible decrease in threat. |
|---|
| 3894 | if(mod < 0) |
|---|
| 3895 | { |
|---|
| 3896 | SetNextTarget(GetMostHated()); |
|---|
| 3897 | //if there is no more new targets then we can walk back home ? |
|---|
| 3898 | if(!GetNextTarget()) |
|---|
| 3899 | HandleEvent(EVENT_LEAVECOMBAT, m_Unit, 0); |
|---|
| 3900 | } |
|---|
| 3901 | } |
|---|
| 3902 | return true; |
|---|
| 3903 | } |
|---|
| 3904 | |
|---|
| 3905 | void AIInterface::RemoveThreatByGUID(uint64 guid) |
|---|
| 3906 | { |
|---|
| 3907 | if (!m_aiTargets.size()) |
|---|
| 3908 | return; |
|---|
| 3909 | |
|---|
| 3910 | if( m_Unit->GetMapMgr() == NULL ) |
|---|
| 3911 | return; |
|---|
| 3912 | |
|---|
| 3913 | Unit *obj = m_Unit->GetMapMgr()->GetUnit(guid); |
|---|
| 3914 | if(obj) |
|---|
| 3915 | RemoveThreatByPtr(obj); |
|---|
| 3916 | } |
|---|
| 3917 | |
|---|
| 3918 | void AIInterface::RemoveThreatByPtr(Unit* obj) |
|---|
| 3919 | { |
|---|
| 3920 | if(!obj) |
|---|
| 3921 | return; |
|---|
| 3922 | |
|---|
| 3923 | LockAITargets(true); |
|---|
| 3924 | |
|---|
| 3925 | TargetMap::iterator it = m_aiTargets.find(obj->GetGUID()); |
|---|
| 3926 | if(it != m_aiTargets.end()) |
|---|
| 3927 | { |
|---|
| 3928 | m_aiTargets.erase(it); |
|---|
| 3929 | //check if we are in combat and need a new target |
|---|
| 3930 | if(obj==GetNextTarget()) |
|---|
| 3931 | { |
|---|
| 3932 | SetNextTarget(GetMostHated()); |
|---|
| 3933 | //if there is no more new targets then we can walk back home ? |
|---|
| 3934 | if(!GetNextTarget()) |
|---|
| 3935 | HandleEvent(EVENT_LEAVECOMBAT, m_Unit, 0); |
|---|
| 3936 | } |
|---|
| 3937 | } |
|---|
| 3938 | |
|---|
| 3939 | LockAITargets(false); |
|---|
| 3940 | } |
|---|
| 3941 | |
|---|
| 3942 | void AIInterface::addAssistTargets(Unit* Friend) |
|---|
| 3943 | { |
|---|
| 3944 | // stop adding stuff that gives errors on linux! |
|---|
| 3945 | m_assistTargets.insert(Friend); |
|---|
| 3946 | } |
|---|
| 3947 | |
|---|
| 3948 | void AIInterface::WipeHateList() |
|---|
| 3949 | { |
|---|
| 3950 | for(TargetMap::iterator itr = m_aiTargets.begin(); itr != m_aiTargets.end(); ++itr) |
|---|
| 3951 | itr->second = 0; |
|---|
| 3952 | m_currentHighestThreat = 0; |
|---|
| 3953 | } |
|---|
| 3954 | void AIInterface::ClearHateList() //without leaving combat |
|---|
| 3955 | { |
|---|
| 3956 | for(TargetMap::iterator itr = m_aiTargets.begin(); itr != m_aiTargets.end(); ++itr) |
|---|
| 3957 | itr->second = 1; |
|---|
| 3958 | m_currentHighestThreat = 1; |
|---|
| 3959 | } |
|---|
| 3960 | |
|---|
| 3961 | void AIInterface::WipeTargetList() |
|---|
| 3962 | { |
|---|
| 3963 | SetNextTarget( (Unit*)NULL ); |
|---|
| 3964 | |
|---|
| 3965 | m_nextSpell = NULL; |
|---|
| 3966 | m_currentHighestThreat = 0; |
|---|
| 3967 | LockAITargets( true ); |
|---|
| 3968 | m_aiTargets.clear(); |
|---|
| 3969 | LockAITargets( false ); |
|---|
| 3970 | m_Unit->CombatStatus.Vanished(); |
|---|
| 3971 | } |
|---|
| 3972 | |
|---|
| 3973 | bool AIInterface::taunt(Unit* caster, bool apply) |
|---|
| 3974 | { |
|---|
| 3975 | if(apply) |
|---|
| 3976 | { |
|---|
| 3977 | //wowwiki says that we cannot override this spell |
|---|
| 3978 | if(GetIsTaunted()) |
|---|
| 3979 | return false; |
|---|
| 3980 | |
|---|
| 3981 | if(!caster) |
|---|
| 3982 | { |
|---|
| 3983 | isTaunted = false; |
|---|
| 3984 | return false; |
|---|
| 3985 | } |
|---|
| 3986 | |
|---|
| 3987 | //check if we can attack taunter. Maybe it's a hack or a bug if we fail this test |
|---|
| 3988 | if(isHostile(m_Unit, caster)) |
|---|
| 3989 | { |
|---|
| 3990 | //check if we have to add him to our agro list |
|---|
| 3991 | //GetMostHated(); //update our most hated list/ Note that at this point we do not have a taunter yet. If we would have then this funtion will not give real mosthated |
|---|
| 3992 | int32 oldthreat = getThreatByPtr(caster); |
|---|
| 3993 | //make sure we rush the target anyway. Since we are not taunted yet, this will also set our target |
|---|
| 3994 | modThreatByPtr(caster,abs(m_currentHighestThreat-oldthreat)+1); //we need to be the most hated at this moment |
|---|
| 3995 | // SetNextTarget(caster); |
|---|
| 3996 | } |
|---|
| 3997 | isTaunted = true; |
|---|
| 3998 | tauntedBy = caster; |
|---|
| 3999 | } |
|---|
| 4000 | else |
|---|
| 4001 | { |
|---|
| 4002 | isTaunted = false; |
|---|
| 4003 | tauntedBy = NULL; |
|---|
| 4004 | //taunt is over, we should get a new target based on most hated list |
|---|
| 4005 | SetNextTarget(GetMostHated()); |
|---|
| 4006 | } |
|---|
| 4007 | |
|---|
| 4008 | return true; |
|---|
| 4009 | } |
|---|
| 4010 | |
|---|
| 4011 | Unit* AIInterface::getTauntedBy() |
|---|
| 4012 | { |
|---|
| 4013 | if(GetIsTaunted()) |
|---|
| 4014 | { |
|---|
| 4015 | return tauntedBy; |
|---|
| 4016 | } |
|---|
| 4017 | else |
|---|
| 4018 | { |
|---|
| 4019 | return NULL; |
|---|
| 4020 | } |
|---|
| 4021 | } |
|---|
| 4022 | |
|---|
| 4023 | bool AIInterface::GetIsTaunted() |
|---|
| 4024 | { |
|---|
| 4025 | if(isTaunted) |
|---|
| 4026 | { |
|---|
| 4027 | if(!tauntedBy || !tauntedBy->isAlive()) |
|---|
| 4028 | { |
|---|
| 4029 | isTaunted = false; |
|---|
| 4030 | tauntedBy = NULL; |
|---|
| 4031 | } |
|---|
| 4032 | } |
|---|
| 4033 | return isTaunted; |
|---|
| 4034 | } |
|---|
| 4035 | |
|---|
| 4036 | void AIInterface::SetSoulLinkedWith(Unit* target) |
|---|
| 4037 | { |
|---|
| 4038 | if (!target) |
|---|
| 4039 | return; |
|---|
| 4040 | soullinkedWith = target; |
|---|
| 4041 | isSoulLinked = true; |
|---|
| 4042 | } |
|---|
| 4043 | |
|---|
| 4044 | Unit* AIInterface::getSoullinkedWith() |
|---|
| 4045 | { |
|---|
| 4046 | if(GetIsTaunted()) |
|---|
| 4047 | { |
|---|
| 4048 | return soullinkedWith; |
|---|
| 4049 | } |
|---|
| 4050 | else |
|---|
| 4051 | { |
|---|
| 4052 | return NULL; |
|---|
| 4053 | } |
|---|
| 4054 | } |
|---|
| 4055 | |
|---|
| 4056 | bool AIInterface::GetIsSoulLinked() |
|---|
| 4057 | { |
|---|
| 4058 | if(isSoulLinked) |
|---|
| 4059 | { |
|---|
| 4060 | if(!soullinkedWith || !soullinkedWith->isAlive()) |
|---|
| 4061 | { |
|---|
| 4062 | isSoulLinked = false; |
|---|
| 4063 | soullinkedWith = NULL; |
|---|
| 4064 | } |
|---|
| 4065 | } |
|---|
| 4066 | return isSoulLinked; |
|---|
| 4067 | } |
|---|
| 4068 | |
|---|
| 4069 | void AIInterface::CheckTarget(Unit* target) |
|---|
| 4070 | { |
|---|
| 4071 | if( target == NULL ) |
|---|
| 4072 | return; |
|---|
| 4073 | |
|---|
| 4074 | if( target == UnitToFollow ) // fix for crash here |
|---|
| 4075 | { |
|---|
| 4076 | UnitToFollow = NULL; |
|---|
| 4077 | m_lastFollowX = m_lastFollowY = 0; |
|---|
| 4078 | FollowDistance = 0; |
|---|
| 4079 | } |
|---|
| 4080 | |
|---|
| 4081 | if( target == UnitToFollow_backup ) |
|---|
| 4082 | { |
|---|
| 4083 | UnitToFollow_backup = NULL; |
|---|
| 4084 | } |
|---|
| 4085 | |
|---|
| 4086 | AssistTargetSet::iterator itr = m_assistTargets.find(target); |
|---|
| 4087 | if(itr != m_assistTargets.end()) |
|---|
| 4088 | m_assistTargets.erase(itr); |
|---|
| 4089 | |
|---|
| 4090 | |
|---|
| 4091 | LockAITargets(true); |
|---|
| 4092 | |
|---|
| 4093 | TargetMap::iterator it2 = m_aiTargets.find( target->GetGUID() ); |
|---|
| 4094 | if( it2 != m_aiTargets.end() || target == GetNextTarget() ) |
|---|
| 4095 | { |
|---|
| 4096 | target->CombatStatus.RemoveAttacker( m_Unit, m_Unit->GetGUID() ); |
|---|
| 4097 | m_Unit->CombatStatus.RemoveAttackTarget( target ); |
|---|
| 4098 | |
|---|
| 4099 | if(it2 != m_aiTargets.end()) |
|---|
| 4100 | { |
|---|
| 4101 | m_aiTargets.erase(it2); |
|---|
| 4102 | } |
|---|
| 4103 | |
|---|
| 4104 | if (target == GetNextTarget()) // no need to cast on these.. mem addresses are still the same |
|---|
| 4105 | { |
|---|
| 4106 | SetNextTarget( (Unit*)NULL ); |
|---|
| 4107 | m_nextSpell = NULL; |
|---|
| 4108 | |
|---|
| 4109 | // find the one with the next highest threat |
|---|
| 4110 | GetMostHated(); |
|---|
| 4111 | } |
|---|
| 4112 | } |
|---|
| 4113 | |
|---|
| 4114 | LockAITargets(false); |
|---|
| 4115 | |
|---|
| 4116 | if( target->GetTypeId() == TYPEID_UNIT ) |
|---|
| 4117 | { |
|---|
| 4118 | it2 = target->GetAIInterface()->m_aiTargets.find( m_Unit->GetGUID() ); |
|---|
| 4119 | if( it2 != target->GetAIInterface()->m_aiTargets.end() ) |
|---|
| 4120 | { |
|---|
| 4121 | target->GetAIInterface()->LockAITargets(true); |
|---|
| 4122 | target->GetAIInterface()->m_aiTargets.erase( it2 ); |
|---|
| 4123 | target->GetAIInterface()->LockAITargets(false); |
|---|
| 4124 | } |
|---|
| 4125 | |
|---|
| 4126 | if( target->GetAIInterface()->GetNextTarget() == m_Unit ) |
|---|
| 4127 | { |
|---|
| 4128 | target->GetAIInterface()->SetNextTarget( (Unit*)NULL ); |
|---|
| 4129 | target->GetAIInterface()->m_nextSpell = NULL; |
|---|
| 4130 | target->GetAIInterface()->GetMostHated(); |
|---|
| 4131 | } |
|---|
| 4132 | |
|---|
| 4133 | if( target->GetAIInterface()->UnitToFollow == m_Unit ) |
|---|
| 4134 | target->GetAIInterface()->UnitToFollow = NULL; |
|---|
| 4135 | } |
|---|
| 4136 | |
|---|
| 4137 | if(target == UnitToFear) |
|---|
| 4138 | UnitToFear = NULL; |
|---|
| 4139 | |
|---|
| 4140 | if(tauntedBy == target) |
|---|
| 4141 | tauntedBy = NULL; |
|---|
| 4142 | } |
|---|
| 4143 | |
|---|
| 4144 | uint32 AIInterface::_CalcThreat(uint32 damage, SpellEntry * sp, Unit* Attacker) |
|---|
| 4145 | { |
|---|
| 4146 | if(!Attacker) |
|---|
| 4147 | return 0; // No attacker means no threat and we prevent crashes this way |
|---|
| 4148 | |
|---|
| 4149 | if(m_Unit->m_faction != NULL && Attacker->m_faction != NULL) |
|---|
| 4150 | if (isSameFaction(m_Unit,Attacker)) |
|---|
| 4151 | return 0; |
|---|
| 4152 | |
|---|
| 4153 | int32 mod = 0; |
|---|
| 4154 | if( sp != NULL && sp->ThreatForSpell != 0 ) |
|---|
| 4155 | { |
|---|
| 4156 | mod = sp->ThreatForSpell; |
|---|
| 4157 | } |
|---|
| 4158 | if( sp != NULL && sp->ThreatForSpellCoef != 0.0f ) |
|---|
| 4159 | mod += int32(damage * sp->ThreatForSpellCoef); |
|---|
| 4160 | else |
|---|
| 4161 | mod += damage; |
|---|
| 4162 | |
|---|
| 4163 | if( sp != NULL && sp->SpellGroupType && Attacker) |
|---|
| 4164 | { |
|---|
| 4165 | SM_FIValue(Attacker->SM_FThreat,&mod,sp->SpellGroupType); |
|---|
| 4166 | SM_PIValue(Attacker->SM_PThreat,&mod,sp->SpellGroupType); |
|---|
| 4167 | } |
|---|
| 4168 | |
|---|
| 4169 | if (Attacker->getClass() == ROGUE) |
|---|
| 4170 | mod = int32(mod * 0.71); // Rogues generate 0.71x threat per damage. |
|---|
| 4171 | |
|---|
| 4172 | // modify threat by Buffs |
|---|
| 4173 | if (sp != NULL) |
|---|
| 4174 | mod += (mod * Attacker->GetGeneratedThreatModifyer(sp->School) / 100); |
|---|
| 4175 | else |
|---|
| 4176 | mod += (mod * Attacker->GetGeneratedThreatModifyer(0) / 100); |
|---|
| 4177 | |
|---|
| 4178 | if (mod < 1) |
|---|
| 4179 | mod = 1; |
|---|
| 4180 | |
|---|
| 4181 | return mod; |
|---|
| 4182 | } |
|---|
| 4183 | |
|---|
| 4184 | void AIInterface::WipeReferences() |
|---|
| 4185 | { |
|---|
| 4186 | m_nextSpell = 0; |
|---|
| 4187 | m_currentHighestThreat = 0; |
|---|
| 4188 | LockAITargets(true); |
|---|
| 4189 | m_aiTargets.clear(); |
|---|
| 4190 | LockAITargets(false); |
|---|
| 4191 | SetNextTarget( (Unit*)NULL ); |
|---|
| 4192 | UnitToFear = 0; |
|---|
| 4193 | UnitToFollow = 0; |
|---|
| 4194 | tauntedBy = 0; |
|---|
| 4195 | m_Unit->AquireInrangeLock(); //make sure to release lock before exit function ! |
|---|
| 4196 | //Clear targettable |
|---|
| 4197 | for(set<Object*>::iterator itr = m_Unit->GetInRangeSetBegin(); itr != m_Unit->GetInRangeSetEnd(); ++itr) |
|---|
| 4198 | if( (*itr) && (*itr)->GetTypeId() == TYPEID_UNIT && static_cast<Unit*>(*itr)->GetAIInterface()) |
|---|
| 4199 | static_cast<Unit*>(*itr)->GetAIInterface()->RemoveThreatByPtr( m_Unit ); |
|---|
| 4200 | m_Unit->ReleaseInrangeLock(); |
|---|
| 4201 | |
|---|
| 4202 | } |
|---|
| 4203 | |
|---|
| 4204 | void AIInterface::ResetProcCounts() |
|---|
| 4205 | { |
|---|
| 4206 | for(list<AI_Spell*>::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) |
|---|
| 4207 | if((*itr)->procCount) |
|---|
| 4208 | (*itr)->procCounter=0; |
|---|
| 4209 | } |
|---|
| 4210 | |
|---|
| 4211 | //we only cast once a spell and we will set his health and resistances. Note that this can be made with db too ! |
|---|
| 4212 | void AIInterface::Event_Summon_EE_totem( uint32 summon_duration ) |
|---|
| 4213 | { |
|---|
| 4214 | //some say it should inherit the level of the caster |
|---|
| 4215 | Unit *caster = m_Unit->GetMapMgr()->GetUnit( m_Unit->GetUInt64Value( UNIT_FIELD_CREATEDBY ) ); |
|---|
| 4216 | uint32 new_level = 0; |
|---|
| 4217 | if( caster ) |
|---|
| 4218 | new_level = caster->getLevel( ); |
|---|
| 4219 | //timer should not reach this value thus not cast this spell again |
|---|
| 4220 | m_totemspelltimer = 0xEFFFFFFF; |
|---|
| 4221 | //creatures do not support PETs and the spell uses that effect so we force a summon guardian thing |
|---|
| 4222 | Creature *ourslave = m_Unit->create_guardian( 15352, summon_duration, float(-M_PI * 2), new_level ); |
|---|
| 4223 | if( ourslave == NULL ) |
|---|
| 4224 | return; |
|---|
| 4225 | |
|---|
| 4226 | m_Unit->AddGuardianRef( ourslave ); |
|---|
| 4227 | ourslave->ResistanceModPct[ NATURE_DAMAGE ] = 100; //we should be immune to nature dmg. This can be also set in db |
|---|
| 4228 | ourslave->m_noRespawn = true; |
|---|
| 4229 | ourslave->SetOwner( caster ); |
|---|
| 4230 | ourslave->SetUInt32Value( UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED | UNIT_FLAG_SELF_RES ); |
|---|
| 4231 | |
|---|
| 4232 | // we want the elemental to have the same pvp flag as the shaman who popped the totem |
|---|
| 4233 | if( caster->IsPvPFlagged() ) |
|---|
| 4234 | ourslave->SetPvPFlag(); |
|---|
| 4235 | else |
|---|
| 4236 | ourslave->RemovePvPFlag(); |
|---|
| 4237 | |
|---|
| 4238 | if( caster->IsFFAPvPFlagged() ) |
|---|
| 4239 | ourslave->SetFFAPvPFlag(); |
|---|
| 4240 | else |
|---|
| 4241 | ourslave->RemoveFFAPvPFlag(); |
|---|
| 4242 | } |
|---|
| 4243 | |
|---|
| 4244 | //we only cast once a spell and we will set his health and resistances. Note that this can be made with db too ! |
|---|
| 4245 | void AIInterface::Event_Summon_FE_totem( uint32 summon_duration ) |
|---|
| 4246 | { |
|---|
| 4247 | //some say it should inherit the level of the caster |
|---|
| 4248 | Unit *caster = m_Unit->GetMapMgr()->GetUnit( m_Unit->GetUInt64Value( UNIT_FIELD_CREATEDBY ) ); |
|---|
| 4249 | uint32 new_level = 0; |
|---|
| 4250 | if( caster != NULL ) |
|---|
| 4251 | new_level = caster->getLevel( ); |
|---|
| 4252 | //timer should not reach this value thus not cast this spell again |
|---|
| 4253 | m_totemspelltimer = 0xEFFFFFFF; |
|---|
| 4254 | //creatures do not support PETs and the spell uses that effect so we force a summon guardian thing |
|---|
| 4255 | Creature *ourslave = m_Unit->create_guardian( 15438, summon_duration, float(-M_PI * 2), new_level ); |
|---|
| 4256 | if( ourslave == NULL ) |
|---|
| 4257 | return; |
|---|
| 4258 | |
|---|
| 4259 | m_Unit->AddGuardianRef( ourslave ); |
|---|
| 4260 | ourslave->ResistanceModPct[ FIRE_DAMAGE ] = 100; //we should be immune to nature dmg. This can be also set in db |
|---|
| 4261 | ourslave->m_noRespawn = true; |
|---|
| 4262 | ourslave->SetOwner( caster ); |
|---|
| 4263 | ourslave->SetUInt32Value( UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED | UNIT_FLAG_SELF_RES ); |
|---|
| 4264 | |
|---|
| 4265 | // we want the elemental to have the same pvp flag as the shaman who popped the totem |
|---|
| 4266 | if( caster->IsPvPFlagged() ) |
|---|
| 4267 | ourslave->SetPvPFlag(); |
|---|
| 4268 | else |
|---|
| 4269 | ourslave->RemovePvPFlag(); |
|---|
| 4270 | |
|---|
| 4271 | if( caster->IsFFAPvPFlagged() ) |
|---|
| 4272 | ourslave->SetFFAPvPFlag(); |
|---|
| 4273 | else |
|---|
| 4274 | ourslave->RemoveFFAPvPFlag(); |
|---|
| 4275 | } |
|---|
| 4276 | /* |
|---|
| 4277 | void AIInterface::CancelSpellCast() |
|---|
| 4278 | { |
|---|
| 4279 | //hmm unit spell casting is not the same as Ai spell casting ? Have to test this |
|---|
| 4280 | if(m_Unit->IsCasting()) |
|---|
| 4281 | m_Unit->m_currentSpell->safe_cancel(); |
|---|
| 4282 | //i can see this crashing already :P. |
|---|
| 4283 | m_AIState = STATE_IDLE; |
|---|
| 4284 | } |
|---|
| 4285 | */ |
|---|
| 4286 | |
|---|
| 4287 | void AIInterface::EventChangeFaction( Unit *ForceAttackersToHateThisInstead ) |
|---|
| 4288 | { |
|---|
| 4289 | m_nextSpell = 0; |
|---|
| 4290 | m_currentHighestThreat = 0; |
|---|
| 4291 | //we need a new hatred list |
|---|
| 4292 | LockAITargets(true); |
|---|
| 4293 | m_aiTargets.clear(); |
|---|
| 4294 | LockAITargets(false); |
|---|
| 4295 | //we need a new assist list |
|---|
| 4296 | m_assistTargets.clear(); |
|---|
| 4297 | //Clear targettable |
|---|
| 4298 | if( ForceAttackersToHateThisInstead == NULL ) |
|---|
| 4299 | { |
|---|
| 4300 | m_Unit->AquireInrangeLock(); //make sure to release lock before exit function ! |
|---|
| 4301 | for(set<Object*>::iterator itr = m_Unit->GetInRangeSetBegin(); itr != m_Unit->GetInRangeSetEnd(); ++itr) |
|---|
| 4302 | if( (*itr) && (*itr)->GetTypeId() == TYPEID_UNIT && static_cast<Unit*>(*itr)->GetAIInterface() ) |
|---|
| 4303 | static_cast<Unit*>(*itr)->GetAIInterface()->RemoveThreatByPtr( m_Unit ); |
|---|
| 4304 | m_Unit->ReleaseInrangeLock(); |
|---|
| 4305 | SetNextTarget( (Unit*)NULL ); |
|---|
| 4306 | } |
|---|
| 4307 | else |
|---|
| 4308 | { |
|---|
| 4309 | m_Unit->AquireInrangeLock(); //make sure to release lock before exit function ! |
|---|
| 4310 | for(set<Object*>::iterator itr = m_Unit->GetInRangeSetBegin(); itr != m_Unit->GetInRangeSetEnd(); ++itr) |
|---|
| 4311 | if( (*itr) && (*itr)->GetTypeId() == TYPEID_UNIT && static_cast<Unit*>(*itr)->GetAIInterface() |
|---|
| 4312 | && static_cast<Unit*>(*itr)->GetAIInterface()->getThreatByPtr( m_Unit ) )//this guy will join me in fight since I'm telling him "sorry i was controlled" |
|---|
| 4313 | { |
|---|
| 4314 | static_cast<Unit*>(*itr)->GetAIInterface()->modThreatByPtr( ForceAttackersToHateThisInstead, 10 ); //just aping to be bale to hate him in case we got nothing else |
|---|
| 4315 | static_cast<Unit*>(*itr)->GetAIInterface()->RemoveThreatByPtr( m_Unit ); |
|---|
| 4316 | } |
|---|
| 4317 | m_Unit->ReleaseInrangeLock(); |
|---|
| 4318 | modThreatByPtr( ForceAttackersToHateThisInstead, 1 ); |
|---|
| 4319 | SetNextTarget( ForceAttackersToHateThisInstead ); |
|---|
| 4320 | } |
|---|
| 4321 | } |
|---|
| 4322 | |
|---|
| 4323 | void AIInterface::WipeCurrentTarget() |
|---|
| 4324 | { |
|---|
| 4325 | if( GetNextTarget() ) |
|---|
| 4326 | { |
|---|
| 4327 | LockAITargets( true ); |
|---|
| 4328 | TargetMap::iterator itr = m_aiTargets.find( GetNextTarget()->GetGUID() ); |
|---|
| 4329 | if( itr != m_aiTargets.end() ) |
|---|
| 4330 | m_aiTargets.erase( itr ); |
|---|
| 4331 | LockAITargets( false ); |
|---|
| 4332 | } |
|---|
| 4333 | |
|---|
| 4334 | SetNextTarget( (Unit*)NULL ); |
|---|
| 4335 | |
|---|
| 4336 | if( GetNextTarget() == UnitToFollow ) |
|---|
| 4337 | UnitToFollow = NULL; |
|---|
| 4338 | |
|---|
| 4339 | if( GetNextTarget() == UnitToFollow_backup ) |
|---|
| 4340 | UnitToFollow_backup = NULL; |
|---|
| 4341 | } |
|---|
| 4342 | |
|---|
| 4343 | #ifdef HACKY_CRASH_FIXES |
|---|
| 4344 | |
|---|
| 4345 | bool AIInterface::CheckCurrentTarget() |
|---|
| 4346 | { |
|---|
| 4347 | //in case target was removed from map since our last check on him |
|---|
| 4348 | if( GetNextTarget() == NULL ) |
|---|
| 4349 | { |
|---|
| 4350 | WipeCurrentTarget(); |
|---|
| 4351 | return false; |
|---|
| 4352 | } |
|---|
| 4353 | |
|---|
| 4354 | bool cansee = false; |
|---|
| 4355 | if( GetNextTarget()->GetInstanceID() == m_Unit->GetInstanceID()) |
|---|
| 4356 | { |
|---|
| 4357 | if( m_Unit->GetTypeId() == TYPEID_UNIT ) |
|---|
| 4358 | cansee = static_cast< Creature* >( m_Unit )->CanSee( GetNextTarget() ); |
|---|
| 4359 | else |
|---|
| 4360 | cansee = static_cast< Player* >( m_Unit )->CanSee( GetNextTarget() ); |
|---|
| 4361 | } |
|---|
| 4362 | else |
|---|
| 4363 | { |
|---|
| 4364 | WipeCurrentTarget(); |
|---|
| 4365 | } |
|---|
| 4366 | |
|---|
| 4367 | return cansee; |
|---|
| 4368 | } |
|---|
| 4369 | |
|---|
| 4370 | bool AIInterface::TargetUpdateCheck(Unit * ptr) |
|---|
| 4371 | { |
|---|
| 4372 | __try |
|---|
| 4373 | { |
|---|
| 4374 | if( ptr->event_GetCurrentInstanceId() != m_Unit->event_GetCurrentInstanceId() || |
|---|
| 4375 | !ptr->isAlive() || m_Unit->GetDistanceSq(ptr) >= 6400.0f ) |
|---|
| 4376 | { |
|---|
| 4377 | return false; |
|---|
| 4378 | } |
|---|
| 4379 | } |
|---|
| 4380 | __except(EXCEPTION_EXECUTE_HANDLER) |
|---|
| 4381 | { |
|---|
| 4382 | return false; |
|---|
| 4383 | } |
|---|
| 4384 | |
|---|
| 4385 | return true; |
|---|
| 4386 | } |
|---|
| 4387 | |
|---|
| 4388 | #endif |
|---|
| 4389 | Unit* AIInterface::GetNextTarget() |
|---|
| 4390 | { |
|---|
| 4391 | if (m_nextTarget && m_Unit && m_Unit->GetMapMgr()) return m_Unit->GetMapMgr()->GetUnit(m_nextTarget); |
|---|
| 4392 | return NULL; |
|---|
| 4393 | } |
|---|
| 4394 | |
|---|
| 4395 | void AIInterface::SetNextTarget (Unit *nextTarget) { |
|---|
| 4396 | if (nextTarget) |
|---|
| 4397 | SetNextTarget(nextTarget->GetGUID()); |
|---|
| 4398 | else |
|---|
| 4399 | SetNextTarget((uint64)NULL); |
|---|
| 4400 | } |
|---|
| 4401 | |
|---|
| 4402 | void AIInterface::SetNextTarget (uint64 nextTarget) |
|---|
| 4403 | { |
|---|
| 4404 | m_nextTarget = nextTarget; |
|---|
| 4405 | m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, m_nextTarget); |
|---|
| 4406 | if(nextTarget) |
|---|
| 4407 | { |
|---|
| 4408 | #ifdef ENABLE_GRACEFULL_HIT |
|---|
| 4409 | have_graceful_hit=false; |
|---|
| 4410 | #endif |
|---|
| 4411 | } |
|---|
| 4412 | } |
|---|