diff options
Diffstat (limited to 'src/server/game/AI/CreatureAI.cpp')
-rw-r--r-- | src/server/game/AI/CreatureAI.cpp | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp new file mode 100644 index 0000000000..226fd7083b --- /dev/null +++ b/src/server/game/AI/CreatureAI.cpp @@ -0,0 +1,268 @@ +/* + * Copyright (C) + * Copyright (C) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "CreatureAI.h" +#include "CreatureAIImpl.h" +#include "Creature.h" +#include "World.h" +#include "SpellMgr.h" +#include "Vehicle.h" +#include "Log.h" +#include "MapReference.h" +#include "Player.h" +#include "CreatureTextMgr.h" + +//Disable CreatureAI when charmed +void CreatureAI::OnCharmed(bool /*apply*/) +{ + //me->IsAIEnabled = !apply;*/ + me->NeedChangeAI = true; + me->IsAIEnabled = false; +} + +AISpellInfoType* UnitAI::AISpellInfo; +AISpellInfoType* GetAISpellInfo(uint32 i) { return &CreatureAI::AISpellInfo[i]; } + +void CreatureAI::Talk(uint8 id, WorldObject const* whisperTarget /*= NULL*/) +{ + sCreatureTextMgr->SendChat(me, id, whisperTarget); +} + +void CreatureAI::DoZoneInCombat(Creature* creature /*= NULL*/, float maxRangeToNearestTarget /* = 50.0f*/) +{ + if (!creature) + creature = me; + + if (!creature->CanHaveThreatList()) + return; + + Map* map = creature->GetMap(); + if (!map->IsDungeon()) //use IsDungeon instead of Instanceable, in case battlegrounds will be instantiated + { + sLog->outError("DoZoneInCombat call for map that isn't an instance (creature entry = %d)", creature->GetTypeId() == TYPEID_UNIT ? creature->ToCreature()->GetEntry() : 0); + return; + } + + // Xinef: Skip creatures in evade mode + if (!creature->HasReactState(REACT_PASSIVE) && !creature->GetVictim() && !creature->IsInEvadeMode()) + { + if (Unit* nearTarget = creature->SelectNearestTarget(maxRangeToNearestTarget)) + creature->AI()->AttackStart(nearTarget); + else if (creature->IsSummon()) + { + if (Unit* summoner = creature->ToTempSummon()->GetSummoner()) + { + Unit* target = summoner->getAttackerForHelper(); + if (!target && summoner->CanHaveThreatList() && !summoner->getThreatManager().isThreatListEmpty()) + target = summoner->getThreatManager().getHostilTarget(); + if (target && (creature->IsFriendlyTo(summoner) || creature->IsHostileTo(target))) + creature->AI()->AttackStart(target); + } + } + } + + if (!creature->HasReactState(REACT_PASSIVE) && !creature->GetVictim()) + { + sLog->outError("DoZoneInCombat called for creature that has empty threat list (creature entry = %u)", creature->GetEntry()); + return; + } + + Map::PlayerList const& playerList = map->GetPlayers(); + + if (playerList.isEmpty()) + return; + + for (Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr) + { + if (Player* player = itr->GetSource()) + { + if (player->IsGameMaster()) + continue; + + if (player->IsAlive()) + { + creature->SetInCombatWith(player); + player->SetInCombatWith(creature); + creature->AddThreat(player, 0.0f); + } + + /* Causes certain things to never leave the threat list (Priest Lightwell, etc): + for (Unit::ControlSet::const_iterator itr = player->m_Controlled.begin(); itr != player->m_Controlled.end(); ++itr) + { + creature->SetInCombatWith(*itr); + (*itr)->SetInCombatWith(creature); + creature->AddThreat(*itr, 0.0f); + }*/ + } + } +} + +// scripts does not take care about MoveInLineOfSight loops +// MoveInLineOfSight can be called inside another MoveInLineOfSight and cause stack overflow +void CreatureAI::MoveInLineOfSight_Safe(Unit* who) +{ + if (m_MoveInLineOfSight_locked == true) + return; + m_MoveInLineOfSight_locked = true; + MoveInLineOfSight(who); + m_MoveInLineOfSight_locked = false; +} + +void CreatureAI::MoveInLineOfSight(Unit* who) +{ + if (me->GetVictim()) + return; + + // pussywizard: civilian, non-combat pet or any other NOT HOSTILE TO ANYONE (!) + if (me->IsMoveInLineOfSightDisabled()) + if (me->GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET || // nothing more to do, return + !who->IsInCombat() || // if not in combat, nothing more to do + !me->IsWithinDist(who, ATTACK_DISTANCE)) // if in combat and in dist - neutral to all can actually assist other creatures + return; + + if (me->CanStartAttack(who)) + AttackStart(who); +} + +void CreatureAI::EnterEvadeMode() +{ + if (!_EnterEvadeMode()) + return; + + ;//sLog->outDebug(LOG_FILTER_UNITS, "Creature %u enters evade mode.", me->GetEntry()); + + if (!me->GetVehicle()) // otherwise me will be in evade mode forever + { + if (Unit* owner = me->GetCharmerOrOwner()) + { + me->GetMotionMaster()->Clear(false); + me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MOTION_SLOT_ACTIVE); + } + else + { + // Required to prevent attacking creatures that are evading and cause them to reenter combat + // Does not apply to MoveFollow + me->AddUnitState(UNIT_STATE_EVADE); + me->GetMotionMaster()->MoveTargetedHome(); + } + } + + Reset(); + + if (me->IsVehicle()) // use the same sequence of addtoworld, aireset may remove all summons! + me->GetVehicleKit()->Reset(true); +} + +/*void CreatureAI::AttackedBy(Unit* attacker) +{ + if (!me->GetVictim()) + AttackStart(attacker); +}*/ + +void CreatureAI::SetGazeOn(Unit* target) +{ + if (me->IsValidAttackTarget(target)) + { + AttackStart(target); + me->SetReactState(REACT_PASSIVE); + } +} + +bool CreatureAI::UpdateVictimWithGaze() +{ + if (!me->IsInCombat()) + return false; + + if (me->HasReactState(REACT_PASSIVE)) + { + if (me->GetVictim()) + return true; + else + me->SetReactState(REACT_AGGRESSIVE); + } + + if (Unit* victim = me->SelectVictim()) + AttackStart(victim); + return me->GetVictim(); +} + +bool CreatureAI::UpdateVictim() +{ + if (!me->IsInCombat()) + return false; + + if (!me->HasReactState(REACT_PASSIVE)) + { + if (Unit* victim = me->SelectVictim()) + AttackStart(victim); + return me->GetVictim(); + } + // xinef: if we have any victim, just return true + else if (me->GetVictim() && me->GetExactDist(me->GetVictim()) < 30.0f) + return true; + else if (me->getThreatManager().isThreatListEmpty()) + { + EnterEvadeMode(); + return false; + } + + return true; +} + +bool CreatureAI::_EnterEvadeMode() +{ + if (!me->IsAlive()) + return false; + + // don't remove vehicle auras, passengers aren't supposed to drop off the vehicle + // don't remove clone caster on evade (to be verified) + me->RemoveEvadeAuras(); + + // sometimes bosses stuck in combat? + me->DeleteThreatList(); + me->CombatStop(true); + me->LoadCreaturesAddon(true); + me->SetLootRecipient(NULL); + me->ResetPlayerDamageReq(); + me->SetLastDamagedTime(0); + + if (me->IsInEvadeMode()) + return false; + + return true; +} + +Creature* CreatureAI::DoSummon(uint32 entry, const Position& pos, uint32 despawnTime, TempSummonType summonType) +{ + return me->SummonCreature(entry, pos, summonType, despawnTime); +} + +Creature* CreatureAI::DoSummon(uint32 entry, WorldObject* obj, float radius, uint32 despawnTime, TempSummonType summonType) +{ + Position pos; + obj->GetRandomNearPosition(pos, radius); + return me->SummonCreature(entry, pos, summonType, despawnTime); +} + +Creature* CreatureAI::DoSummonFlyer(uint32 entry, WorldObject* obj, float flightZ, float radius, uint32 despawnTime, TempSummonType summonType) +{ + Position pos; + obj->GetRandomNearPosition(pos, radius); + pos.m_positionZ += flightZ; + return me->SummonCreature(entry, pos, summonType, despawnTime); +} |