First Commit

For Azeroth!
This commit is contained in:
Yehonal
2016-06-26 10:39:44 +02:00
commit e8e94a0a66
3777 changed files with 1419268 additions and 0 deletions

View File

@@ -0,0 +1,323 @@
/*
* 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 "CombatAI.h"
#include "SpellMgr.h"
#include "SpellInfo.h"
#include "Vehicle.h"
#include "ObjectAccessor.h"
#include "Player.h"
/////////////////
// AggressorAI
/////////////////
int AggressorAI::Permissible(const Creature* creature)
{
// have some hostile factions, it will be selected by IsHostileTo check at MoveInLineOfSight
if (!creature->IsCivilian() && !creature->IsNeutralToAll())
return PERMIT_BASE_PROACTIVE;
return PERMIT_BASE_NO;
}
void AggressorAI::UpdateAI(uint32 /*diff*/)
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
/////////////////
// CombatAI
/////////////////
void CombatAI::InitializeAI()
{
for (uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
if (me->m_spells[i] && sSpellMgr->GetSpellInfo(me->m_spells[i]))
spells.push_back(me->m_spells[i]);
CreatureAI::InitializeAI();
}
void CombatAI::Reset()
{
events.Reset();
}
void CombatAI::JustDied(Unit* killer)
{
for (SpellVct::iterator i = spells.begin(); i != spells.end(); ++i)
if (AISpellInfo[*i].condition == AICOND_DIE)
me->CastSpell(killer, *i, true);
}
void CombatAI::EnterCombat(Unit* who)
{
for (SpellVct::iterator i = spells.begin(); i != spells.end(); ++i)
{
if (AISpellInfo[*i].condition == AICOND_AGGRO)
me->CastSpell(who, *i, false);
else if (AISpellInfo[*i].condition == AICOND_COMBAT)
events.ScheduleEvent(*i, AISpellInfo[*i].cooldown + rand()%AISpellInfo[*i].cooldown);
}
}
void CombatAI::UpdateAI(uint32 diff)
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
if (uint32 spellId = events.ExecuteEvent())
{
DoCast(spellId);
events.ScheduleEvent(spellId, AISpellInfo[spellId].cooldown + rand()%AISpellInfo[spellId].cooldown);
}
else
DoMeleeAttackIfReady();
}
/////////////////
// CasterAI
/////////////////
void CasterAI::InitializeAI()
{
CombatAI::InitializeAI();
m_attackDist = 30.0f;
for (SpellVct::iterator itr = spells.begin(); itr != spells.end(); ++itr)
if (AISpellInfo[*itr].condition == AICOND_COMBAT && m_attackDist > GetAISpellInfo(*itr)->maxRange)
m_attackDist = GetAISpellInfo(*itr)->maxRange;
if (m_attackDist == 30.0f)
m_attackDist = MELEE_RANGE;
}
void CasterAI::EnterCombat(Unit* who)
{
if (spells.empty())
return;
uint32 spell = rand()%spells.size();
uint32 count = 0;
for (SpellVct::iterator itr = spells.begin(); itr != spells.end(); ++itr, ++count)
{
if (AISpellInfo[*itr].condition == AICOND_AGGRO)
me->CastSpell(who, *itr, false);
else if (AISpellInfo[*itr].condition == AICOND_COMBAT)
{
uint32 cooldown = GetAISpellInfo(*itr)->realCooldown;
if (count == spell)
{
DoCast(spells[spell]);
cooldown += me->GetCurrentSpellCastTime(*itr);
}
events.ScheduleEvent(*itr, cooldown);
}
}
}
void CasterAI::UpdateAI(uint32 diff)
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->GetVictim()->HasBreakableByDamageCrowdControlAura(me))
{
me->InterruptNonMeleeSpells(false);
return;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
if (uint32 spellId = events.ExecuteEvent())
{
DoCast(spellId);
uint32 casttime = me->GetCurrentSpellCastTime(spellId);
events.ScheduleEvent(spellId, (casttime ? casttime : 500) + GetAISpellInfo(spellId)->realCooldown);
}
}
//////////////
// ArcherAI
//////////////
ArcherAI::ArcherAI(Creature* c) : CreatureAI(c)
{
if (!me->m_spells[0])
sLog->outError("ArcherAI set for creature (entry = %u) with spell1=0. AI will do nothing", me->GetEntry());
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(me->m_spells[0]);
m_minRange = spellInfo ? spellInfo->GetMinRange(false) : 0;
if (!m_minRange)
m_minRange = MELEE_RANGE;
me->m_CombatDistance = spellInfo ? spellInfo->GetMaxRange(false) : 0;
me->m_SightDistance = me->m_CombatDistance;
}
void ArcherAI::AttackStart(Unit* who)
{
if (!who)
return;
if (me->IsWithinCombatRange(who, m_minRange))
{
if (me->Attack(who, true) && !who->IsFlying())
me->GetMotionMaster()->MoveChase(who);
}
else
{
if (me->Attack(who, false) && !who->IsFlying())
me->GetMotionMaster()->MoveChase(who, me->m_CombatDistance);
}
if (who->IsFlying())
me->GetMotionMaster()->MoveIdle();
}
void ArcherAI::UpdateAI(uint32 /*diff*/)
{
if (!UpdateVictim())
return;
if (!me->IsWithinCombatRange(me->GetVictim(), m_minRange))
DoSpellAttackIfReady(me->m_spells[0]);
else
DoMeleeAttackIfReady();
}
//////////////
// TurretAI
//////////////
TurretAI::TurretAI(Creature* c) : CreatureAI(c)
{
if (!me->m_spells[0])
sLog->outError("TurretAI set for creature (entry = %u) with spell1=0. AI will do nothing", me->GetEntry());
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(me->m_spells[0]);
m_minRange = spellInfo ? spellInfo->GetMinRange(false) : 0;
me->m_CombatDistance = spellInfo ? spellInfo->GetMaxRange(false) : 0;
me->m_SightDistance = me->m_CombatDistance;
}
bool TurretAI::CanAIAttack(const Unit* /*who*/) const
{
// TODO: use one function to replace it
if (!me->IsWithinCombatRange(me->GetVictim(), me->m_CombatDistance)
|| (m_minRange && me->IsWithinCombatRange(me->GetVictim(), m_minRange)))
return false;
return true;
}
void TurretAI::AttackStart(Unit* who)
{
if (who)
me->Attack(who, false);
}
void TurretAI::UpdateAI(uint32 /*diff*/)
{
if (!UpdateVictim())
return;
if( me->m_spells[0] )
DoSpellAttackIfReady(me->m_spells[0]);
}
//////////////
// VehicleAI
//////////////
VehicleAI::VehicleAI(Creature* c) : CreatureAI(c), m_ConditionsTimer(VEHICLE_CONDITION_CHECK_TIME)
{
LoadConditions();
m_DoDismiss = false;
m_DismissTimer = VEHICLE_DISMISS_TIME;
}
//NOTE: VehicleAI::UpdateAI runs even while the vehicle is mounted
void VehicleAI::UpdateAI(uint32 diff)
{
CheckConditions(diff);
if (m_DoDismiss)
{
if (m_DismissTimer < diff)
{
m_DoDismiss = false;
me->DespawnOrUnsummon();
}
else
m_DismissTimer -= diff;
}
}
void VehicleAI::OnCharmed(bool apply)
{
if (!me->GetVehicleKit()->IsVehicleInUse() && !apply && !conditions.empty()) // was used and has conditions
m_DoDismiss = true; // needs reset
else if (apply)
m_DoDismiss = false; // in use again
m_DismissTimer = VEHICLE_DISMISS_TIME;//reset timer
}
void VehicleAI::LoadConditions()
{
conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry());
//if (!conditions.empty())
;//sLog->outDebug(LOG_FILTER_CONDITIONSYS, "VehicleAI::LoadConditions: loaded %u conditions", uint32(conditions.size()));
}
void VehicleAI::CheckConditions(uint32 diff)
{
if (m_ConditionsTimer < diff)
{
if (!conditions.empty())
{
if (Vehicle* vehicleKit = me->GetVehicleKit())
for (SeatMap::iterator itr = vehicleKit->Seats.begin(); itr != vehicleKit->Seats.end(); ++itr)
if (Unit* passenger = ObjectAccessor::GetUnit(*me, itr->second.Passenger.Guid))
{
if (Player* player = passenger->ToPlayer())
{
if (!sConditionMgr->IsObjectMeetToConditions(player, me, conditions))
{
player->ExitVehicle();
return; // check other pessanger in next tick
}
}
}
}
m_ConditionsTimer = VEHICLE_CONDITION_CHECK_TIME;
}
else
m_ConditionsTimer -= diff;
}

View File

@@ -0,0 +1,120 @@
/*
* 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/>.
*/
#ifndef TRINITY_COMBATAI_H
#define TRINITY_COMBATAI_H
#include "CreatureAI.h"
#include "CreatureAIImpl.h"
#include "ConditionMgr.h"
class Creature;
class AggressorAI : public CreatureAI
{
public:
explicit AggressorAI(Creature* c) : CreatureAI(c) {}
void UpdateAI(uint32);
static int Permissible(const Creature*);
};
typedef std::vector<uint32> SpellVct;
class CombatAI : public CreatureAI
{
public:
explicit CombatAI(Creature* c) : CreatureAI(c) {}
void InitializeAI();
void Reset();
void EnterCombat(Unit* who);
void JustDied(Unit* killer);
void UpdateAI(uint32 diff);
static int Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
protected:
EventMap events;
SpellVct spells;
};
class CasterAI : public CombatAI
{
public:
explicit CasterAI(Creature* c) : CombatAI(c) { m_attackDist = MELEE_RANGE; }
void InitializeAI();
void AttackStart(Unit* victim) { AttackStartCaster(victim, m_attackDist); }
void UpdateAI(uint32 diff);
void EnterCombat(Unit* /*who*/);
private:
float m_attackDist;
};
struct ArcherAI : public CreatureAI
{
public:
explicit ArcherAI(Creature* c);
void AttackStart(Unit* who);
void UpdateAI(uint32 diff);
static int Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
protected:
float m_minRange;
};
struct TurretAI : public CreatureAI
{
public:
explicit TurretAI(Creature* c);
bool CanAIAttack(const Unit* who) const;
void AttackStart(Unit* who);
void UpdateAI(uint32 diff);
static int Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
protected:
float m_minRange;
};
#define VEHICLE_CONDITION_CHECK_TIME 1000
#define VEHICLE_DISMISS_TIME 5000
struct VehicleAI : public CreatureAI
{
public:
explicit VehicleAI(Creature* creature);
void UpdateAI(uint32 diff);
void MoveInLineOfSight(Unit*) {}
void AttackStart(Unit*) {}
void OnCharmed(bool apply);
static int Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
private:
void LoadConditions();
void CheckConditions(uint32 diff);
ConditionList conditions;
uint32 m_ConditionsTimer;
bool m_DoDismiss;
uint32 m_DismissTimer;
};
#endif

View File

@@ -0,0 +1,29 @@
/*
* 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 "GameObjectAI.h"
//GameObjectAI::GameObjectAI(GameObject* g) : go(g) {}
int GameObjectAI::Permissible(const GameObject* go)
{
if (go->GetAIName() == "GameObjectAI")
return PERMIT_BASE_SPECIAL;
return PERMIT_BASE_NO;
}
NullGameObjectAI::NullGameObjectAI(GameObject* g) : GameObjectAI(g) {}

View File

@@ -0,0 +1,76 @@
/*
* 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/>.
*/
#ifndef TRINITY_GAMEOBJECTAI_H
#define TRINITY_GAMEOBJECTAI_H
#include "Define.h"
#include <list>
#include "Object.h"
#include "QuestDef.h"
#include "GameObject.h"
#include "CreatureAI.h"
class GameObjectAI
{
protected:
GameObject* const go;
public:
explicit GameObjectAI(GameObject* g) : go(g) {}
virtual ~GameObjectAI() {}
virtual void UpdateAI(uint32 /*diff*/) {}
virtual void InitializeAI() { Reset(); }
virtual void Reset() { }
// Pass parameters between AI
virtual void DoAction(int32 /*param = 0 */) {}
virtual void SetGUID(uint64 /*guid*/, int32 /*id = 0 */) {}
virtual uint64 GetGUID(int32 /*id = 0 */) const { return 0; }
static int Permissible(GameObject const* go);
virtual bool GossipHello(Player* /*player*/, bool reportUse) { return false; }
virtual bool GossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) { return false; }
virtual bool GossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, char const* /*code*/) { return false; }
virtual bool QuestAccept(Player* /*player*/, Quest const* /*quest*/) { return false; }
virtual bool QuestReward(Player* /*player*/, Quest const* /*quest*/, uint32 /*opt*/) { return false; }
virtual uint32 GetDialogStatus(Player* /*player*/) { return DIALOG_STATUS_SCRIPTED_NO_STATUS; }
virtual void Destroyed(Player* /*player*/, uint32 /*eventId*/) {}
virtual uint32 GetData(uint32 /*id*/) const { return 0; }
virtual void SetData64(uint32 /*id*/, uint64 /*value*/) {}
virtual uint64 GetData64(uint32 /*id*/) const { return 0; }
virtual void SetData(uint32 /*id*/, uint32 /*value*/) {}
virtual void OnGameEvent(bool /*start*/, uint16 /*eventId*/) {}
virtual void OnStateChanged(uint32 /*state*/, Unit* /*unit*/) {}
virtual void EventInform(uint32 /*eventId*/) {}
virtual void SpellHit(Unit* unit, const SpellInfo* spellInfo) {}
};
class NullGameObjectAI : public GameObjectAI
{
public:
explicit NullGameObjectAI(GameObject* g);
void UpdateAI(uint32 /*diff*/) {}
static int Permissible(GameObject const* /*go*/) { return PERMIT_BASE_IDLE; }
};
#endif

View File

@@ -0,0 +1,69 @@
/*
* 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 "GuardAI.h"
#include "Errors.h"
#include "Player.h"
#include "ObjectAccessor.h"
#include "World.h"
#include "CreatureAIImpl.h"
int GuardAI::Permissible(Creature const* creature)
{
if (creature->IsGuard())
return PERMIT_BASE_SPECIAL;
return PERMIT_BASE_NO;
}
GuardAI::GuardAI(Creature* creature) : ScriptedAI(creature)
{
}
void GuardAI::Reset()
{
ScriptedAI::Reset();
me->CastSpell(me, 18950 /*SPELL_INVISIBILITY_AND_STEALTH_DETECTION*/, true);
}
void GuardAI::EnterEvadeMode()
{
if (!me->IsAlive())
{
me->GetMotionMaster()->MoveIdle();
me->CombatStop(true);
me->DeleteThreatList();
return;
}
;//sLog->outDebug(LOG_FILTER_UNITS, "Guard entry: %u enters evade mode.", me->GetEntry());
me->RemoveAllAuras();
me->DeleteThreatList();
me->CombatStop(true);
// Remove ChaseMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
me->GetMotionMaster()->MoveTargetedHome();
}
void GuardAI::JustDied(Unit* killer)
{
if (Player* player = killer->GetCharmerOrOwnerPlayerOrPlayerItself())
me->SendZoneUnderAttackMessage(player);
}

View File

@@ -0,0 +1,37 @@
/*
* 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/>.
*/
#ifndef TRINITY_GUARDAI_H
#define TRINITY_GUARDAI_H
#include "ScriptedCreature.h"
class Creature;
class GuardAI : public ScriptedAI
{
public:
explicit GuardAI(Creature* creature);
static int Permissible(Creature const* creature);
void Reset();
void EnterEvadeMode();
void JustDied(Unit* killer);
};
#endif

View File

@@ -0,0 +1,92 @@
/*
* 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 "PassiveAI.h"
#include "Creature.h"
#include "TemporarySummon.h"
PassiveAI::PassiveAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); }
PossessedAI::PossessedAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); }
NullCreatureAI::NullCreatureAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); }
void PassiveAI::UpdateAI(uint32)
{
if (me->IsInCombat() && me->getAttackers().empty())
EnterEvadeMode();
}
void PossessedAI::AttackStart(Unit* target)
{
me->Attack(target, true);
}
void PossessedAI::UpdateAI(uint32 /*diff*/)
{
if (me->GetVictim())
{
if (!me->IsValidAttackTarget(me->GetVictim()))
me->AttackStop();
else
DoMeleeAttackIfReady();
}
}
void PossessedAI::JustDied(Unit* /*u*/)
{
// We died while possessed, disable our loot
me->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
}
void PossessedAI::KilledUnit(Unit* victim)
{
// We killed a creature, disable victim's loot
//if (victim->GetTypeId() == TYPEID_UNIT)
// victim->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
}
void CritterAI::DamageTaken(Unit*, uint32&, DamageEffectType, SpellSchoolMask)
{
if (!me->HasUnitState(UNIT_STATE_FLEEING))
me->SetControlled(true, UNIT_STATE_FLEEING);
_combatTimer = 1;
}
void CritterAI::EnterEvadeMode()
{
if (me->HasUnitState(UNIT_STATE_FLEEING))
me->SetControlled(false, UNIT_STATE_FLEEING);
CreatureAI::EnterEvadeMode();
_combatTimer = 0;
}
void CritterAI::UpdateAI(uint32 diff)
{
if (me->IsInCombat())
{
_combatTimer += diff;
if (_combatTimer >= 5000)
EnterEvadeMode();
}
}
void TriggerAI::IsSummonedBy(Unit* summoner)
{
if (me->m_spells[0])
me->CastSpell(me, me->m_spells[0], false, 0, 0, summoner ? summoner->GetGUID() : 0);
}

View File

@@ -0,0 +1,89 @@
/*
* 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/>.
*/
#ifndef TRINITY_PASSIVEAI_H
#define TRINITY_PASSIVEAI_H
#include "CreatureAI.h"
//#include "CreatureAIImpl.h"
class PassiveAI : public CreatureAI
{
public:
explicit PassiveAI(Creature* c);
void MoveInLineOfSight(Unit*) {}
void AttackStart(Unit*) {}
void UpdateAI(uint32);
static int Permissible(const Creature*) { return PERMIT_BASE_IDLE; }
};
class PossessedAI : public CreatureAI
{
public:
explicit PossessedAI(Creature* c);
void MoveInLineOfSight(Unit*) {}
void AttackStart(Unit* target);
void UpdateAI(uint32);
void EnterEvadeMode() {}
void JustDied(Unit*);
void KilledUnit(Unit* victim);
static int Permissible(const Creature*) { return PERMIT_BASE_IDLE; }
};
class NullCreatureAI : public CreatureAI
{
public:
explicit NullCreatureAI(Creature* c);
void MoveInLineOfSight(Unit*) {}
void AttackStart(Unit*) {}
void UpdateAI(uint32) {}
void EnterEvadeMode() {}
void OnCharmed(bool /*apply*/) {}
static int Permissible(const Creature*) { return PERMIT_BASE_IDLE; }
};
class CritterAI : public PassiveAI
{
public:
explicit CritterAI(Creature* c) : PassiveAI(c) { _combatTimer = 0; }
void DamageTaken(Unit* /*done_by*/, uint32& /*damage*/, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask);
void EnterEvadeMode();
void UpdateAI(uint32);
// Xinef: Added
private:
uint32 _combatTimer;
};
class TriggerAI : public NullCreatureAI
{
public:
explicit TriggerAI(Creature* c) : NullCreatureAI(c) {}
void IsSummonedBy(Unit* summoner);
};
#endif

View File

@@ -0,0 +1,742 @@
/*
* 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 "PetAI.h"
#include "Errors.h"
#include "Pet.h"
#include "Player.h"
#include "DBCStores.h"
#include "Spell.h"
#include "ObjectAccessor.h"
#include "SpellMgr.h"
#include "Creature.h"
#include "World.h"
#include "Util.h"
#include "Group.h"
#include "SpellInfo.h"
#include "SpellAuraEffects.h"
#include "WorldSession.h"
int PetAI::Permissible(const Creature* creature)
{
if (creature->IsPet())
return PERMIT_BASE_SPECIAL;
return PERMIT_BASE_NO;
}
PetAI::PetAI(Creature* c) : CreatureAI(c), i_tracker(TIME_INTERVAL_LOOK)
{
UpdateAllies();
}
bool PetAI::_needToStop()
{
// This is needed for charmed creatures, as once their target was reset other effects can trigger threat
if (me->IsCharmed() && me->GetVictim() == me->GetCharmer())
return true;
// xinef: dont allow to follow targets out of visibility range
if (me->GetExactDist(me->GetVictim()) > me->GetVisibilityRange()-5.0f)
return true;
// dont allow pets to follow targets far away from owner
if (Unit* owner = me->GetCharmerOrOwner())
if (owner->GetExactDist(me) >= (owner->GetVisibilityRange()-10.0f))
return true;
if (!me->_CanDetectFeignDeathOf(me->GetVictim()))
return true;
if (me->isTargetNotAcceptableByMMaps(me->GetVictim()->GetGUID(), sWorld->GetGameTime(), me->GetVictim()))
return true;
return !me->CanCreatureAttack(me->GetVictim());
}
void PetAI::_stopAttack()
{
if (!me->IsAlive())
{
;//sLog->outStaticDebug("Creature stoped attacking cuz his dead [guid=%u]", me->GetGUIDLow());
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
me->CombatStop();
me->getHostileRefManager().deleteReferences();
return;
}
me->AttackStop();
me->InterruptNonMeleeSpells(false);
me->GetCharmInfo()->SetIsCommandAttack(false);
ClearCharmInfoFlags();
HandleReturnMovement();
}
void PetAI::_doMeleeAttack()
{
// Xinef: Imps cannot attack with melee
if (!_canMeleeAttack())
return;
DoMeleeAttackIfReady();
}
bool PetAI::_canMeleeAttack() const
{
return me->GetEntry() != 416 /*ENTRY_IMP*/ &&
me->GetEntry() != 510 /*ENTRY_WATER_ELEMENTAL*/ &&
me->GetEntry() != 37994 /*ENTRY_WATER_ELEMENTAL_PERM*/;
}
void PetAI::UpdateAI(uint32 diff)
{
if (!me->IsAlive() || !me->GetCharmInfo())
return;
Unit* owner = me->GetCharmerOrOwner();
if (m_updateAlliesTimer <= diff)
// UpdateAllies self set update timer
UpdateAllies();
else
m_updateAlliesTimer -= diff;
if (me->GetVictim() && me->GetVictim()->IsAlive())
{
// is only necessary to stop casting, the pet must not exit combat
if (me->GetVictim()->HasBreakableByDamageCrowdControlAura(me))
{
me->InterruptNonMeleeSpells(false);
return;
}
if (_needToStop())
{
;//sLog->outStaticDebug("Pet AI stopped attacking [guid=%u]", me->GetGUIDLow());
_stopAttack();
return;
}
// Check before attacking to prevent pets from leaving stay position
if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY))
{
if (me->GetCharmInfo()->IsCommandAttack() || (me->GetCharmInfo()->IsAtStay() && me->IsWithinMeleeRange(me->GetVictim())))
_doMeleeAttack();
}
else
_doMeleeAttack();
}
else if (!me->GetCharmInfo() || (!me->GetCharmInfo()->GetForcedSpell() && !me->HasUnitState(UNIT_STATE_CASTING)))
{
if (me->HasReactState(REACT_AGGRESSIVE) || me->GetCharmInfo()->IsAtStay())
{
// Every update we need to check targets only in certain cases
// Aggressive - Allow auto select if owner or pet don't have a target
// Stay - Only pick from pet or owner targets / attackers so targets won't run by
// while chasing our owner. Don't do auto select.
// All other cases (ie: defensive) - Targets are assigned by AttackedBy(), OwnerAttackedBy(), OwnerAttacked(), etc.
Unit* nextTarget = SelectNextTarget(me->HasReactState(REACT_AGGRESSIVE));
if (nextTarget)
AttackStart(nextTarget);
else
HandleReturnMovement();
}
else
HandleReturnMovement();
}
// xinef: charm info must be always available
if (!me->GetCharmInfo())
return;
// Autocast (casted only in combat or persistent spells in any state)
if (!me->HasUnitState(UNIT_STATE_CASTING))
{
if (owner && owner->GetTypeId() == TYPEID_PLAYER && me->GetCharmInfo()->GetForcedSpell() && me->GetCharmInfo()->GetForcedTarget())
{
owner->ToPlayer()->GetSession()->HandlePetActionHelper(me, me->GetGUID(), abs(me->GetCharmInfo()->GetForcedSpell()), ACT_ENABLED, me->GetCharmInfo()->GetForcedTarget());
// xinef: if spell was casted properly and we are in passive mode, handle return
if (!me->GetCharmInfo()->GetForcedSpell() && me->HasReactState(REACT_PASSIVE))
{
if (me->HasUnitState(UNIT_STATE_CASTING))
{
me->GetMotionMaster()->Clear(false);
me->StopMoving();
}
else
_stopAttack();
}
return;
}
// xinef: dont allow ghouls to cast spells below 75 energy
if (me->IsPet() && me->ToPet()->IsPetGhoul() && me->GetPower(POWER_ENERGY) < 75)
return;
typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList;
TargetSpellList targetSpellStore;
for (uint8 i = 0; i < me->GetPetAutoSpellSize(); ++i)
{
uint32 spellID = me->GetPetAutoSpellOnPos(i);
if (!spellID)
continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID);
if (!spellInfo)
continue;
if (me->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
continue;
// check spell cooldown, this should be checked in CheckCast...
if (me->HasSpellCooldown(spellInfo->Id))
continue;
if (spellInfo->IsPositive())
{
if (spellInfo->CanBeUsedInCombat())
{
// Check if we're in combat or commanded to attack
if (!me->IsInCombat() && !me->GetCharmInfo()->IsCommandAttack())
continue;
}
Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0);
spell->LoadScripts(); // xinef: load for CanAutoCast (calling CheckPetCast)
bool spellUsed = false;
// Some spells can target enemy or friendly (DK Ghoul's Leap)
// Check for enemy first (pet then owner)
Unit* target = me->getAttackerForHelper();
if (!target && owner)
target = owner->getAttackerForHelper();
if (target)
{
if (CanAttack(target) && spell->CanAutoCast(target))
{
targetSpellStore.push_back(std::make_pair(target, spell));
spellUsed = true;
}
}
// No enemy, check friendly
if (!spellUsed)
{
for (std::set<uint64>::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
{
Unit* ally = ObjectAccessor::GetUnit(*me, *tar);
//only buff targets that are in combat, unless the spell can only be cast while out of combat
if (!ally)
continue;
if (spell->CanAutoCast(ally))
{
targetSpellStore.push_back(std::make_pair(ally, spell));
spellUsed = true;
break;
}
}
}
// No valid targets at all
if (!spellUsed)
delete spell;
}
else if (me->GetVictim() && CanAttack(me->GetVictim(), spellInfo) && spellInfo->CanBeUsedInCombat())
{
Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0);
if (spell->CanAutoCast(me->GetVictim()))
targetSpellStore.push_back(std::make_pair(me->GetVictim(), spell));
else
delete spell;
}
}
//found units to cast on to
if (!targetSpellStore.empty())
{
uint32 index = urand(0, targetSpellStore.size() - 1);
Spell* spell = targetSpellStore[index].second;
Unit* target = targetSpellStore[index].first;
targetSpellStore.erase(targetSpellStore.begin() + index);
SpellCastTargets targets;
targets.SetUnitTarget(target);
if (!me->HasInArc(M_PI, target))
{
me->SetInFront(target);
if (target && target->GetTypeId() == TYPEID_PLAYER)
me->SendUpdateToPlayer(target->ToPlayer());
if (owner && owner->GetTypeId() == TYPEID_PLAYER)
me->SendUpdateToPlayer(owner->ToPlayer());
}
me->AddSpellCooldown(spell->m_spellInfo->Id, 0, 0);
spell->prepare(&targets);
}
// deleted cached Spell objects
for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr)
delete itr->second;
}
}
void PetAI::UpdateAllies()
{
Unit* owner = me->GetCharmerOrOwner();
Group* group = NULL;
m_updateAlliesTimer = 10*IN_MILLISECONDS; //update friendly targets every 10 seconds, lesser checks increase performance
if (!owner)
return;
else if (owner->GetTypeId() == TYPEID_PLAYER)
group = owner->ToPlayer()->GetGroup();
//only pet and owner/not in group->ok
if (m_AllySet.size() == 2 && !group)
return;
//owner is in group; group members filled in already (no raid -> subgroupcount = whole count)
if (group && !group->isRaidGroup() && m_AllySet.size() == (group->GetMembersCount() + 2))
return;
m_AllySet.clear();
m_AllySet.insert(me->GetGUID());
if (group) //add group
{
for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* Target = itr->GetSource();
if (!Target || !Target->IsInMap(owner) || !group->SameSubGroup(owner->ToPlayer(), Target))
continue;
if (Target->GetGUID() == owner->GetGUID())
continue;
m_AllySet.insert(Target->GetGUID());
}
}
else //remove group
m_AllySet.insert(owner->GetGUID());
}
void PetAI::KilledUnit(Unit* victim)
{
// Called from Unit::Kill() in case where pet or owner kills something
// if owner killed this victim, pet may still be attacking something else
if (me->GetVictim() && me->GetVictim() != victim)
return;
// Xinef: if pet is channeling a spell and owner killed something different, dont interrupt it
if (me->HasUnitState(UNIT_STATE_CASTING) && me->GetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT) && me->GetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT) != victim->GetGUID())
return;
// Clear target just in case. May help problem where health / focus / mana
// regen gets stuck. Also resets attack command.
// Can't use _stopAttack() because that activates movement handlers and ignores
// next target selection
me->AttackStop();
me->InterruptNonMeleeSpells(false);
// Before returning to owner, see if there are more things to attack
if (Unit* nextTarget = SelectNextTarget(false))
AttackStart(nextTarget);
else
HandleReturnMovement(); // Return
}
void PetAI::AttackStart(Unit* target)
{
// Overrides Unit::AttackStart to correctly evaluate Pet states
// Check all pet states to decide if we can attack this target
if (!CanAttack(target))
return;
// Only chase if not commanded to stay or if stay but commanded to attack
DoAttack(target, (!me->GetCharmInfo()->HasCommandState(COMMAND_STAY) || me->GetCharmInfo()->IsCommandAttack()));
}
void PetAI::OwnerAttackedBy(Unit* attacker)
{
// Called when owner takes damage. This function helps keep pets from running off
// simply due to owner gaining aggro.
if (!attacker)
return;
// Passive pets don't do anything
if (me->HasReactState(REACT_PASSIVE))
return;
// Prevent pet from disengaging from current target
if (me->GetVictim() && me->GetVictim()->IsAlive())
return;
// Continue to evaluate and attack if necessary
AttackStart(attacker);
}
void PetAI::OwnerAttacked(Unit* target)
{
// Called when owner attacks something. Allows defensive pets to know
// that they need to assist
// Target might be NULL if called from spell with invalid cast targets
if (!target)
return;
// Passive pets don't do anything
if (me->HasReactState(REACT_PASSIVE))
return;
// Prevent pet from disengaging from current target
if (me->GetVictim() && me->GetVictim()->IsAlive())
return;
// Continue to evaluate and attack if necessary
AttackStart(target);
}
Unit* PetAI::SelectNextTarget(bool allowAutoSelect) const
{
// Provides next target selection after current target death.
// This function should only be called internally by the AI
// Targets are not evaluated here for being valid targets, that is done in _CanAttack()
// The parameter: allowAutoSelect lets us disable aggressive pet auto targeting for certain situations
// Passive pets don't do next target selection
if (me->HasReactState(REACT_PASSIVE))
return NULL;
// Check pet attackers first so we don't drag a bunch of targets to the owner
if (Unit* myAttacker = me->getAttackerForHelper())
if (!myAttacker->HasBreakableByDamageCrowdControlAura() && me->_CanDetectFeignDeathOf(myAttacker) && me->CanCreatureAttack(myAttacker) && !me->isTargetNotAcceptableByMMaps(myAttacker->GetGUID(), sWorld->GetGameTime(), myAttacker))
return myAttacker;
// Check pet's attackers first to prevent dragging mobs back to owner
if (me->HasAuraType(SPELL_AURA_MOD_TAUNT))
{
const Unit::AuraEffectList& tauntAuras = me->GetAuraEffectsByType(SPELL_AURA_MOD_TAUNT);
if (!tauntAuras.empty())
for (Unit::AuraEffectList::const_reverse_iterator itr = tauntAuras.rbegin(); itr != tauntAuras.rend(); ++itr)
if (Unit* caster = (*itr)->GetCaster())
if (me->_CanDetectFeignDeathOf(caster) && me->CanCreatureAttack(caster) && !caster->HasAuraTypeWithCaster(SPELL_AURA_IGNORED, me->GetGUID()))
return caster;
}
// Not sure why we wouldn't have an owner but just in case...
Unit* owner = me->GetCharmerOrOwner();
if (!owner)
return NULL;
// Check owner attackers
if (Unit* ownerAttacker = owner->getAttackerForHelper())
if (!ownerAttacker->HasBreakableByDamageCrowdControlAura() && me->_CanDetectFeignDeathOf(ownerAttacker) && me->CanCreatureAttack(ownerAttacker) && !me->isTargetNotAcceptableByMMaps(ownerAttacker->GetGUID(), sWorld->GetGameTime(), ownerAttacker))
return ownerAttacker;
// Check owner victim
// 3.0.2 - Pets now start attacking their owners victim in defensive mode as soon as the hunter does
if (Unit* ownerVictim = owner->GetVictim())
if (me->_CanDetectFeignDeathOf(ownerVictim) && me->CanCreatureAttack(ownerVictim) && !me->isTargetNotAcceptableByMMaps(ownerVictim->GetGUID(), sWorld->GetGameTime(), ownerVictim))
return ownerVictim;
// Neither pet or owner had a target and aggressive pets can pick any target
// To prevent aggressive pets from chain selecting targets and running off, we
// only select a random target if certain conditions are met.
if (allowAutoSelect)
if (!me->GetCharmInfo()->IsReturning() || me->GetCharmInfo()->IsFollowing() || me->GetCharmInfo()->IsAtStay())
if (Unit* nearTarget = me->ToCreature()->SelectNearestTargetInAttackDistance(MAX_AGGRO_RADIUS))
return nearTarget;
// Default - no valid targets
return NULL;
}
void PetAI::HandleReturnMovement()
{
// Handles moving the pet back to stay or owner
// Prevent activating movement when under control of spells
// such as "Eyes of the Beast"
if (me->isPossessed())
return;
if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY))
{
if (!me->GetCharmInfo()->IsAtStay() && !me->GetCharmInfo()->IsReturning())
{
// Return to previous position where stay was clicked
if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) == NULL_MOTION_TYPE)
{
float x, y, z;
me->GetCharmInfo()->GetStayPosition(x, y, z);
ClearCharmInfoFlags();
me->GetCharmInfo()->SetIsReturning(true);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MovePoint(me->GetGUIDLow(), x, y, z);
}
}
}
else // COMMAND_FOLLOW
{
if (!me->GetCharmInfo()->IsFollowing() && !me->GetCharmInfo()->IsReturning())
{
if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) == NULL_MOTION_TYPE)
{
ClearCharmInfoFlags();
me->GetCharmInfo()->SetIsReturning(true);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveFollow(me->GetCharmerOrOwner(), PET_FOLLOW_DIST, me->GetFollowAngle());
}
}
}
me->GetCharmInfo()->SetForcedSpell(0);
me->GetCharmInfo()->SetForcedTargetGUID(0);
// xinef: remember that npcs summoned by npcs can also be pets
me->DeleteThreatList();
me->ClearInPetCombat();
}
void PetAI::SpellHit(Unit* caster, const SpellInfo* spellInfo)
{
// Xinef: taunt behavior code
if (spellInfo->HasAura(SPELL_AURA_MOD_TAUNT) && !me->HasReactState(REACT_PASSIVE))
{
me->GetCharmInfo()->SetForcedSpell(0);
me->GetCharmInfo()->SetForcedTargetGUID(0);
AttackStart(caster);
}
}
void PetAI::DoAttack(Unit* target, bool chase)
{
// Handles attack with or without chase and also resets flags
// for next update / creature kill
if (me->Attack(target, true))
{
// xinef: properly fix fake combat after pet is sent to attack
if (Unit* owner = me->GetOwner())
owner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT);
// Play sound to let the player know the pet is attacking something it picked on its own
if (me->HasReactState(REACT_AGGRESSIVE) && !me->GetCharmInfo()->IsCommandAttack())
me->SendPetAIReaction(me->GetGUID());
if (CharmInfo* ci = me->GetCharmInfo())
{
ci->SetIsAtStay(false);
ci->SetIsCommandFollow(false);
ci->SetIsFollowing(false);
ci->SetIsReturning(false);
}
if (chase)
{
me->GetMotionMaster()->MoveChase(target, !_canMeleeAttack() ? 20.0f: 0.0f, me->GetAngle(target));
}
else // (Stay && ((Aggressive || Defensive) && In Melee Range)))
{
me->GetCharmInfo()->SetIsAtStay(true);
me->GetMotionMaster()->MovementExpiredOnSlot(MOTION_SLOT_ACTIVE, false);
me->GetMotionMaster()->MoveIdle();
}
}
}
void PetAI::MovementInform(uint32 moveType, uint32 data)
{
// Receives notification when pet reaches stay or follow owner
switch (moveType)
{
case POINT_MOTION_TYPE:
{
// Pet is returning to where stay was clicked. data should be
// pet's GUIDLow since we set that as the waypoint ID
if (data == me->GetGUIDLow() && me->GetCharmInfo()->IsReturning())
{
ClearCharmInfoFlags();
me->GetCharmInfo()->SetIsAtStay(true);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
}
break;
}
case FOLLOW_MOTION_TYPE:
{
// If data is owner's GUIDLow then we've reached follow point,
// otherwise we're probably chasing a creature
if (me->GetCharmerOrOwner() && me->GetCharmInfo() && data == me->GetCharmerOrOwner()->GetGUIDLow() && me->GetCharmInfo()->IsReturning())
{
ClearCharmInfoFlags();
me->GetCharmInfo()->SetIsFollowing(true);
}
break;
}
default:
break;
}
}
bool PetAI::CanAttack(Unit* target, const SpellInfo* spellInfo)
{
// Evaluates wether a pet can attack a specific target based on CommandState, ReactState and other flags
// IMPORTANT: The order in which things are checked is important, be careful if you add or remove checks
// Hmmm...
if (!target)
return false;
if (!target->IsAlive())
{
// xinef: if target is invalid, pet should evade automaticly
// Clear target to prevent getting stuck on dead targets
//me->AttackStop();
//me->InterruptNonMeleeSpells(false);
return false;
}
// xinef: check unit states of pet
if (me->HasUnitState(UNIT_STATE_LOST_CONTROL))
return false;
// xinef: pets of mounted players have stunned flag only, check this also
if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED))
return false;
// pussywizard: ZOMG! TEMP!
if (!me->GetCharmInfo())
{
sLog->outMisc("PetAI::CanAttack (A1) - %u, %u", me->GetEntry(), GUID_LOPART(me->GetOwnerGUID()));
return false;
}
// Passive - passive pets can attack if told to
if (me->HasReactState(REACT_PASSIVE))
return me->GetCharmInfo()->IsCommandAttack();
// CC - mobs under crowd control can be attacked if owner commanded
if (target->HasBreakableByDamageCrowdControlAura() && (!spellInfo || !spellInfo->HasAttribute(SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS)))
return me->GetCharmInfo()->IsCommandAttack();
// Returning - pets ignore attacks only if owner clicked follow
if (me->GetCharmInfo()->IsReturning())
return !me->GetCharmInfo()->IsCommandFollow();
// Stay - can attack if target is within range or commanded to
if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY))
return (me->IsWithinMeleeRange(target) || me->GetCharmInfo()->IsCommandAttack());
// Pets attacking something (or chasing) should only switch targets if owner tells them to
if (me->GetVictim() && me->GetVictim() != target)
{
// Check if our owner selected this target and clicked "attack"
Unit* ownerTarget = NULL;
if (Player* owner = me->GetCharmerOrOwner()->ToPlayer())
ownerTarget = owner->GetSelectedUnit();
else
ownerTarget = me->GetCharmerOrOwner()->GetVictim();
if (ownerTarget && me->GetCharmInfo()->IsCommandAttack())
return (target->GetGUID() == ownerTarget->GetGUID());
}
// Follow
if (me->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
return !me->GetCharmInfo()->IsReturning();
// default, though we shouldn't ever get here
return false;
}
void PetAI::ReceiveEmote(Player* player, uint32 emote)
{
if (me->GetOwnerGUID() && me->GetOwnerGUID() == player->GetGUID())
switch (emote)
{
case TEXT_EMOTE_COWER:
if (me->IsPet() && me->ToPet()->IsPetGhoul())
me->HandleEmoteCommand(/*EMOTE_ONESHOT_ROAR*/EMOTE_ONESHOT_OMNICAST_GHOUL);
break;
case TEXT_EMOTE_ANGRY:
if (me->IsPet() && me->ToPet()->IsPetGhoul())
me->HandleEmoteCommand(/*EMOTE_ONESHOT_COWER*/EMOTE_STATE_STUN);
break;
case TEXT_EMOTE_GLARE:
if (me->IsPet() && me->ToPet()->IsPetGhoul())
me->HandleEmoteCommand(EMOTE_STATE_STUN);
break;
case TEXT_EMOTE_SOOTHE:
if (me->IsPet() && me->ToPet()->IsPetGhoul())
me->HandleEmoteCommand(EMOTE_ONESHOT_OMNICAST_GHOUL);
break;
}
}
void PetAI::ClearCharmInfoFlags()
{
// Quick access to set all flags to FALSE
CharmInfo* ci = me->GetCharmInfo();
if (ci)
{
ci->SetIsAtStay(false);
ci->SetIsCommandAttack(false);
ci->SetIsCommandFollow(false);
ci->SetIsFollowing(false);
ci->SetIsReturning(false);
}
}
void PetAI::AttackedBy(Unit* attacker)
{
// Called when pet takes damage. This function helps keep pets from running off
// simply due to gaining aggro.
if (!attacker)
return;
// Passive pets don't do anything
if (me->HasReactState(REACT_PASSIVE))
return;
// Prevent pet from disengaging from current target
if (me->GetVictim() && me->GetVictim()->IsAlive())
return;
// Continue to evaluate and attack if necessary
AttackStart(attacker);
}

View File

@@ -0,0 +1,72 @@
/*
* 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/>.
*/
#ifndef TRINITY_PETAI_H
#define TRINITY_PETAI_H
#include "CreatureAI.h"
#include "Timer.h"
class Creature;
class Spell;
class PetAI : public CreatureAI
{
public:
explicit PetAI(Creature* c);
void UpdateAI(uint32);
static int Permissible(const Creature*);
void KilledUnit(Unit* /*victim*/);
void AttackStart(Unit* target);
void MovementInform(uint32 moveType, uint32 data);
void OwnerAttackedBy(Unit* attacker);
void OwnerAttacked(Unit* target);
void AttackedBy(Unit* attacker);
void ReceiveEmote(Player* player, uint32 textEmote);
// The following aren't used by the PetAI but need to be defined to override
// default CreatureAI functions which interfere with the PetAI
//
void MoveInLineOfSight(Unit* /*who*/) {} // CreatureAI interferes with returning pets
void MoveInLineOfSight_Safe(Unit* /*who*/) {} // CreatureAI interferes with returning pets
void EnterEvadeMode() {} // For fleeing, pets don't use this type of Evade mechanic
void SpellHit(Unit* caster, const SpellInfo* spellInfo);
private:
bool _isVisible(Unit*) const;
bool _needToStop(void);
void _stopAttack(void);
void _doMeleeAttack();
bool _canMeleeAttack() const;
void UpdateAllies();
TimeTracker i_tracker;
std::set<uint64> m_AllySet;
uint32 m_updateAlliesTimer;
Unit* SelectNextTarget(bool allowAutoSelect) const;
void HandleReturnMovement();
void DoAttack(Unit* target, bool chase);
bool CanAttack(Unit* target, const SpellInfo* spellInfo = NULL);
void ClearCharmInfoFlags();
};
#endif

View File

@@ -0,0 +1,40 @@
/*
* 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 "ByteBuffer.h"
#include "ReactorAI.h"
#include "Errors.h"
#include "Log.h"
#include "ObjectAccessor.h"
#include "CreatureAIImpl.h"
int ReactorAI::Permissible(const Creature* creature)
{
if (creature->IsCivilian() || creature->IsNeutralToAll())
return PERMIT_BASE_REACTIVE;
return PERMIT_BASE_NO;
}
void ReactorAI::UpdateAI(uint32 /*diff*/)
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}

View File

@@ -0,0 +1,37 @@
/*
* 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/>.
*/
#ifndef TRINITY_REACTORAI_H
#define TRINITY_REACTORAI_H
#include "CreatureAI.h"
class Unit;
class ReactorAI : public CreatureAI
{
public:
explicit ReactorAI(Creature* c) : CreatureAI(c) {}
void MoveInLineOfSight(Unit*) {}
void UpdateAI(uint32 diff);
static int Permissible(const Creature*);
};
#endif

View File

@@ -0,0 +1,117 @@
/*
* 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 "TotemAI.h"
#include "Totem.h"
#include "Creature.h"
#include "DBCStores.h"
#include "ObjectAccessor.h"
#include "SpellMgr.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "CellImpl.h"
int TotemAI::Permissible(Creature const* creature)
{
if (creature->IsTotem())
return PERMIT_BASE_PROACTIVE;
return PERMIT_BASE_NO;
}
TotemAI::TotemAI(Creature* c) : CreatureAI(c), i_victimGuid(0)
{
ASSERT(c->IsTotem());
}
void TotemAI::SpellHit(Unit* /*caster*/, const SpellInfo* spellInfo)
{
}
void TotemAI::DoAction(int32 param)
{
}
void TotemAI::MoveInLineOfSight(Unit* /*who*/)
{
}
void TotemAI::EnterEvadeMode()
{
me->CombatStop(true);
}
void TotemAI::UpdateAI(uint32 /*diff*/)
{
if (me->ToTotem()->GetTotemType() != TOTEM_ACTIVE)
return;
if (!me->IsAlive() || me->IsNonMeleeSpellCast(false))
return;
// Search spell
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(me->ToTotem()->GetSpell());
if (!spellInfo)
return;
// Get spell range
float max_range = spellInfo->GetMaxRange(false);
// SPELLMOD_RANGE not applied in this place just because not existence range mods for attacking totems
// pointer to appropriate target if found any
Unit* victim = i_victimGuid ? ObjectAccessor::GetUnit(*me, i_victimGuid) : NULL;
// Search victim if no, not attackable, or out of range, or friendly (possible in case duel end)
if (!victim ||
!victim->isTargetableForAttack(true, me) || !me->IsWithinDistInMap(victim, max_range) ||
me->IsFriendlyTo(victim) || !me->CanSeeOrDetect(victim))
{
victim = NULL;
Trinity::NearestAttackableUnitInObjectRangeCheck u_check(me, me, max_range);
Trinity::UnitLastSearcher<Trinity::NearestAttackableUnitInObjectRangeCheck> checker(me, victim, u_check);
me->VisitNearbyObject(max_range, checker);
}
// If have target
if (victim)
{
// remember
i_victimGuid = victim->GetGUID();
// attack
me->SetInFront(victim); // client change orientation by self
me->CastSpell(victim, me->ToTotem()->GetSpell(), false);
}
else
i_victimGuid = 0;
}
void TotemAI::AttackStart(Unit* /*victim*/)
{
// Sentry totem sends ping on attack
if (me->GetEntry() == SENTRY_TOTEM_ENTRY && me->GetOwner()->GetTypeId() == TYPEID_PLAYER)
{
WorldPacket data(MSG_MINIMAP_PING, (8+4+4));
data << me->GetGUID();
data << me->GetPositionX();
data << me->GetPositionY();
me->GetOwner()->ToPlayer()->GetSession()->SendPacket(&data);
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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/>.
*/
#ifndef TRINITY_TOTEMAI_H
#define TRINITY_TOTEMAI_H
#include "CreatureAI.h"
#include "Timer.h"
class Creature;
class Totem;
class TotemAI : public CreatureAI
{
public:
explicit TotemAI(Creature* c);
void MoveInLineOfSight(Unit* who);
void AttackStart(Unit* victim);
void EnterEvadeMode();
void SpellHit(Unit* /*caster*/, const SpellInfo* /*spellInfo*/);
void DoAction(int32 param);
void UpdateAI(uint32 diff);
static int Permissible(Creature const* creature);
private:
uint64 i_victimGuid;
};
class KillMagnetEvent : public BasicEvent
{
public:
KillMagnetEvent(Unit& self) : _self(self) { }
bool Execute(uint64 e_time, uint32 p_time)
{
_self.setDeathState(JUST_DIED);
return true;
}
protected:
Unit& _self;
};
#endif

View File

@@ -0,0 +1,336 @@
/*
* 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 "UnitAI.h"
#include "Player.h"
#include "Creature.h"
#include "SpellAuras.h"
#include "SpellAuraEffects.h"
#include "SpellMgr.h"
#include "SpellInfo.h"
#include "Spell.h"
#include "CreatureAIImpl.h"
void UnitAI::AttackStart(Unit* victim)
{
if (victim && me->Attack(victim, true))
me->GetMotionMaster()->MoveChase(victim);
}
void UnitAI::AttackStartCaster(Unit* victim, float dist)
{
if (victim && me->Attack(victim, false))
me->GetMotionMaster()->MoveChase(victim, dist);
}
void UnitAI::DoMeleeAttackIfReady()
{
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
Unit *victim = me->GetVictim();
if (!victim || !victim->IsInWorld())
return;
if (!me->IsWithinMeleeRange(victim))
return;
//Make sure our attack is ready and we aren't currently casting before checking distance
if (me->isAttackReady())
{
// xinef: prevent base and off attack in same time, delay attack at 0.2 sec
if (me->haveOffhandWeapon())
if (me->getAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY)
me->setAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY);
me->AttackerStateUpdate(victim);
me->resetAttackTimer();
}
if (me->haveOffhandWeapon() && me->isAttackReady(OFF_ATTACK))
{
// xinef: delay main hand attack if both will hit at the same time (players code)
if (me->getAttackTimer(BASE_ATTACK) < ATTACK_DISPLAY_DELAY)
me->setAttackTimer(BASE_ATTACK, ATTACK_DISPLAY_DELAY);
me->AttackerStateUpdate(victim, OFF_ATTACK);
me->resetAttackTimer(OFF_ATTACK);
}
}
bool UnitAI::DoSpellAttackIfReady(uint32 spell)
{
if (me->HasUnitState(UNIT_STATE_CASTING) || !me->isAttackReady())
return true;
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell))
{
if (me->IsWithinCombatRange(me->GetVictim(), spellInfo->GetMaxRange(false)))
{
me->CastSpell(me->GetVictim(), spell, false);
me->resetAttackTimer();
return true;
}
}
return false;
}
Unit* UnitAI::SelectTarget(SelectAggroTarget targetType, uint32 position, float dist, bool playerOnly, int32 aura)
{
return SelectTarget(targetType, position, DefaultTargetSelector(me, dist, playerOnly, aura));
}
void UnitAI::SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectAggroTarget targetType, float dist, bool playerOnly, int32 aura)
{
SelectTargetList(targetList, DefaultTargetSelector(me, dist, playerOnly, aura), num, targetType);
}
float UnitAI::DoGetSpellMaxRange(uint32 spellId, bool positive)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
return spellInfo ? spellInfo->GetMaxRange(positive) : 0;
}
void UnitAI::DoAddAuraToAllHostilePlayers(uint32 spellid)
{
if (me->IsInCombat())
{
ThreatContainer::StorageType threatlist = me->getThreatManager().getThreatList();
for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
{
if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()))
if (unit->GetTypeId() == TYPEID_PLAYER)
me->AddAura(spellid, unit);
}
}
}
void UnitAI::DoCastToAllHostilePlayers(uint32 spellid, bool triggered)
{
if (me->IsInCombat())
{
ThreatContainer::StorageType threatlist = me->getThreatManager().getThreatList();
for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
{
if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()))
if (unit->GetTypeId() == TYPEID_PLAYER)
me->CastSpell(unit, spellid, triggered);
}
}
}
void UnitAI::DoCast(uint32 spellId)
{
Unit* target = NULL;
//sLog->outError("aggre %u %u", spellId, (uint32)AISpellInfo[spellId].target);
switch (AISpellInfo[spellId].target)
{
default:
case AITARGET_SELF: target = me; break;
case AITARGET_VICTIM: target = me->GetVictim(); break;
case AITARGET_ENEMY:
{
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
bool playerOnly = spellInfo->HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS);
//float range = GetSpellMaxRange(spellInfo, false);
target = SelectTarget(SELECT_TARGET_RANDOM, 0, spellInfo->GetMaxRange(false), playerOnly);
break;
}
case AITARGET_ALLY: target = me; break;
case AITARGET_BUFF: target = me; break;
case AITARGET_DEBUFF:
{
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
bool playerOnly = spellInfo->HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS);
float range = spellInfo->GetMaxRange(false);
DefaultTargetSelector targetSelector(me, range, playerOnly, -(int32)spellId);
if (!(spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_VICTIM)
&& targetSelector(me->GetVictim()))
target = me->GetVictim();
else
target = SelectTarget(SELECT_TARGET_RANDOM, 0, targetSelector);
break;
}
}
if (target)
me->CastSpell(target, spellId, false);
}
void UnitAI::DoCast(Unit* victim, uint32 spellId, bool triggered)
{
if (!victim || (me->HasUnitState(UNIT_STATE_CASTING) && !triggered))
return;
me->CastSpell(victim, spellId, triggered);
}
void UnitAI::DoCastVictim(uint32 spellId, bool triggered)
{
if (!me->GetVictim() || (me->HasUnitState(UNIT_STATE_CASTING) && !triggered))
return;
me->CastSpell(me->GetVictim(), spellId, triggered);
}
void UnitAI::DoCastAOE(uint32 spellId, bool triggered)
{
if (!triggered && me->HasUnitState(UNIT_STATE_CASTING))
return;
me->CastSpell((Unit*)NULL, spellId, triggered);
}
#define UPDATE_TARGET(a) {if (AIInfo->target<a) AIInfo->target=a;}
void UnitAI::FillAISpellInfo()
{
AISpellInfo = new AISpellInfoType[sSpellMgr->GetSpellInfoStoreSize()];
AISpellInfoType* AIInfo = AISpellInfo;
const SpellInfo* spellInfo;
for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i, ++AIInfo)
{
spellInfo = sSpellMgr->GetSpellInfo(i);
if (!spellInfo)
continue;
if (spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD))
AIInfo->condition = AICOND_DIE;
else if (spellInfo->IsPassive() || spellInfo->GetDuration() == -1)
AIInfo->condition = AICOND_AGGRO;
else
AIInfo->condition = AICOND_COMBAT;
if (AIInfo->cooldown < spellInfo->RecoveryTime)
AIInfo->cooldown = spellInfo->RecoveryTime;
if (!spellInfo->GetMaxRange(false))
UPDATE_TARGET(AITARGET_SELF)
else
{
for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j)
{
uint32 targetType = spellInfo->Effects[j].TargetA.GetTarget();
if (targetType == TARGET_UNIT_TARGET_ENEMY
|| targetType == TARGET_DEST_TARGET_ENEMY)
UPDATE_TARGET(AITARGET_VICTIM)
else if (targetType == TARGET_UNIT_DEST_AREA_ENEMY)
UPDATE_TARGET(AITARGET_ENEMY)
if (spellInfo->Effects[j].Effect == SPELL_EFFECT_APPLY_AURA)
{
if (targetType == TARGET_UNIT_TARGET_ENEMY)
UPDATE_TARGET(AITARGET_DEBUFF)
else if (spellInfo->IsPositive())
UPDATE_TARGET(AITARGET_BUFF)
}
}
}
AIInfo->realCooldown = spellInfo->RecoveryTime + spellInfo->StartRecoveryTime;
AIInfo->maxRange = spellInfo->GetMaxRange(false) * 3 / 4;
}
}
//Enable PlayerAI when charmed
void PlayerAI::OnCharmed(bool apply)
{
me->IsAIEnabled = apply;
}
void SimpleCharmedAI::UpdateAI(uint32 /*diff*/)
{
Creature* charmer = me->GetCharmer()->ToCreature();
//kill self if charm aura has infinite duration
if (charmer->IsInEvadeMode())
{
Unit::AuraEffectList const& auras = me->GetAuraEffectsByType(SPELL_AURA_MOD_CHARM);
for (Unit::AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter)
if ((*iter)->GetCasterGUID() == charmer->GetGUID() && (*iter)->GetBase()->IsPermanent())
{
Unit::Kill(charmer, me);
return;
}
}
if (!charmer->IsInCombat())
me->GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, me->GetFollowAngle());
Unit* target = me->GetVictim();
if (!target || !charmer->IsValidAttackTarget(target))
AttackStart(charmer->SelectNearestTargetInAttackDistance(ATTACK_DISTANCE));
}
SpellTargetSelector::SpellTargetSelector(Unit* caster, uint32 spellId) :
_caster(caster), _spellInfo(sSpellMgr->GetSpellForDifficultyFromSpell(sSpellMgr->GetSpellInfo(spellId), caster))
{
ASSERT(_spellInfo);
}
bool SpellTargetSelector::operator()(Unit const* target) const
{
if (!target)
return false;
if (_spellInfo->CheckTarget(_caster, target) != SPELL_CAST_OK)
return false;
// copypasta from Spell::CheckRange
uint32 range_type = _spellInfo->RangeEntry ? _spellInfo->RangeEntry->type : 0;
float max_range = _caster->GetSpellMaxRangeForTarget(target, _spellInfo);
float min_range = _caster->GetSpellMinRangeForTarget(target, _spellInfo);
if (target && target != _caster)
{
if (range_type == SPELL_RANGE_MELEE)
{
// Because of lag, we can not check too strictly here.
if (!_caster->IsWithinMeleeRange(target, max_range))
return false;
}
else if (!_caster->IsWithinCombatRange(target, max_range))
return false;
if (range_type == SPELL_RANGE_RANGED)
{
if (_caster->IsWithinMeleeRange(target))
return false;
}
else if (min_range && _caster->IsWithinCombatRange(target, min_range)) // skip this check if min_range = 0
return false;
}
return true;
}
bool NonTankTargetSelector::operator()(Unit const* target) const
{
if (!target)
return false;
if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER)
return false;
return target != _source->GetVictim();
}

View File

@@ -0,0 +1,340 @@
/*
* 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/>.
*/
#ifndef TRINITY_UNITAI_H
#define TRINITY_UNITAI_H
#include "Define.h"
#include "Unit.h"
#include "Containers.h"
#include <list>
class Player;
class Quest;
class Unit;
struct AISpellInfoType;
//Selection method used by SelectTarget
enum SelectAggroTarget
{
SELECT_TARGET_RANDOM = 0, //Just selects a random target
SELECT_TARGET_TOPAGGRO, //Selects targes from top aggro to bottom
SELECT_TARGET_BOTTOMAGGRO, //Selects targets from bottom aggro to top
SELECT_TARGET_NEAREST,
SELECT_TARGET_FARTHEST,
};
// default predicate function to select target based on distance, player and/or aura criteria
struct DefaultTargetSelector : public std::unary_function<Unit*, bool>
{
const Unit* me;
float m_dist;
bool m_playerOnly;
int32 m_aura;
// unit: the reference unit
// dist: if 0: ignored, if > 0: maximum distance to the reference unit, if < 0: minimum distance to the reference unit
// playerOnly: self explaining
// aura: if 0: ignored, if > 0: the target shall have the aura, if < 0, the target shall NOT have the aura
DefaultTargetSelector(Unit const* unit, float dist, bool playerOnly, int32 aura) : me(unit), m_dist(dist), m_playerOnly(playerOnly), m_aura(aura) {}
bool operator()(Unit const* target) const
{
if (!me)
return false;
if (!target)
return false;
if (m_playerOnly && (target->GetTypeId() != TYPEID_PLAYER))
return false;
if (m_dist > 0.0f && !me->IsWithinCombatRange(target, m_dist))
return false;
if (m_dist < 0.0f && me->IsWithinCombatRange(target, -m_dist))
return false;
if (m_aura)
{
if (m_aura > 0)
{
if (!target->HasAura(m_aura))
return false;
}
else
{
if (target->HasAura(-m_aura))
return false;
}
}
return true;
}
};
// Target selector for spell casts checking range, auras and attributes
// TODO: Add more checks from Spell::CheckCast
struct SpellTargetSelector : public std::unary_function<Unit*, bool>
{
public:
SpellTargetSelector(Unit* caster, uint32 spellId);
bool operator()(Unit const* target) const;
private:
Unit const* _caster;
SpellInfo const* _spellInfo;
};
// Very simple target selector, will just skip main target
// NOTE: When passing to UnitAI::SelectTarget remember to use 0 as position for random selection
// because tank will not be in the temporary list
struct NonTankTargetSelector : public std::unary_function<Unit*, bool>
{
public:
NonTankTargetSelector(Creature* source, bool playerOnly = true) : _source(source), _playerOnly(playerOnly) { }
bool operator()(Unit const* target) const;
private:
Creature const* _source;
bool _playerOnly;
};
// Simple selector for units using mana
struct PowerUsersSelector : public std::unary_function<Unit*, bool>
{
Unit const* _me;
float const _dist;
bool const _playerOnly;
Powers const _power;
PowerUsersSelector(Unit const* unit, Powers power, float dist, bool playerOnly) : _me(unit), _power(power), _dist(dist), _playerOnly(playerOnly) { }
bool operator()(Unit const* target) const
{
if (!_me || !target)
return false;
if (target->getPowerType() != _power)
return false;
if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER)
return false;
if (_dist > 0.0f && !_me->IsWithinCombatRange(target, _dist))
return false;
if (_dist < 0.0f && _me->IsWithinCombatRange(target, -_dist))
return false;
return true;
}
};
struct FarthestTargetSelector : public std::unary_function<Unit*, bool>
{
FarthestTargetSelector(Unit const* unit, float dist, bool playerOnly, bool inLos) : _me(unit), _dist(dist), _playerOnly(playerOnly), _inLos(inLos) {}
bool operator()(Unit const* target) const
{
if (!_me || !target)
return false;
if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER)
return false;
if (_dist > 0.0f && !_me->IsWithinCombatRange(target, _dist))
return false;
if (_inLos && !_me->IsWithinLOSInMap(target))
return false;
return true;
}
private:
const Unit* _me;
float _dist;
bool _playerOnly;
bool _inLos;
};
class UnitAI
{
protected:
Unit* const me;
public:
explicit UnitAI(Unit* unit) : me(unit) {}
virtual ~UnitAI() {}
virtual bool CanAIAttack(Unit const* /*target*/) const { return true; }
virtual void AttackStart(Unit* /*target*/);
virtual void UpdateAI(uint32 diff) = 0;
virtual void InitializeAI() { if (!me->isDead()) Reset(); }
virtual void Reset() {};
// Called when unit is charmed
virtual void OnCharmed(bool apply) = 0;
// Pass parameters between AI
virtual void DoAction(int32 /*param*/) {}
virtual uint32 GetData(uint32 /*id = 0*/) const { return 0; }
virtual void SetData(uint32 /*id*/, uint32 /*value*/) {}
virtual void SetGUID(uint64 /*guid*/, int32 /*id*/ = 0) {}
virtual uint64 GetGUID(int32 /*id*/ = 0) const { return 0; }
Unit* SelectTarget(SelectAggroTarget targetType, uint32 position = 0, float dist = 0.0f, bool playerOnly = false, int32 aura = 0);
// Select the targets satifying the predicate.
// predicate shall extend std::unary_function<Unit*, bool>
template <class PREDICATE> Unit* SelectTarget(SelectAggroTarget targetType, uint32 position, PREDICATE const& predicate)
{
ThreatContainer::StorageType const& threatlist = me->getThreatManager().getThreatList();
if (position >= threatlist.size())
return NULL;
std::list<Unit*> targetList;
for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
if (predicate((*itr)->getTarget()))
targetList.push_back((*itr)->getTarget());
if (position >= targetList.size())
return NULL;
if (targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST)
targetList.sort(Trinity::ObjectDistanceOrderPred(me));
switch (targetType)
{
case SELECT_TARGET_NEAREST:
case SELECT_TARGET_TOPAGGRO:
{
std::list<Unit*>::iterator itr = targetList.begin();
std::advance(itr, position);
return *itr;
}
case SELECT_TARGET_FARTHEST:
case SELECT_TARGET_BOTTOMAGGRO:
{
std::list<Unit*>::reverse_iterator ritr = targetList.rbegin();
std::advance(ritr, position);
return *ritr;
}
case SELECT_TARGET_RANDOM:
{
std::list<Unit*>::iterator itr = targetList.begin();
std::advance(itr, urand(position, targetList.size() - 1));
return *itr;
}
default:
break;
}
return NULL;
}
void SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectAggroTarget targetType, float dist = 0.0f, bool playerOnly = false, int32 aura = 0);
// Select the targets satifying the predicate.
// predicate shall extend std::unary_function<Unit*, bool>
template <class PREDICATE> void SelectTargetList(std::list<Unit*>& targetList, PREDICATE const& predicate, uint32 maxTargets, SelectAggroTarget targetType)
{
ThreatContainer::StorageType const& threatlist = me->getThreatManager().getThreatList();
if (threatlist.empty())
return;
for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
if (predicate((*itr)->getTarget()))
targetList.push_back((*itr)->getTarget());
if (targetList.size() < maxTargets)
return;
if (targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST)
targetList.sort(Trinity::ObjectDistanceOrderPred(me));
if (targetType == SELECT_TARGET_FARTHEST || targetType == SELECT_TARGET_BOTTOMAGGRO)
targetList.reverse();
if (targetType == SELECT_TARGET_RANDOM)
Trinity::Containers::RandomResizeList(targetList, maxTargets);
else
targetList.resize(maxTargets);
}
// Called at any Damage to any victim (before damage apply)
virtual void DamageDealt(Unit* /*victim*/, uint32& /*damage*/, DamageEffectType /*damageType*/) { }
// Called at any Damage from any attacker (before damage apply)
// Note: it for recalculation damage or special reaction at damage
// for attack reaction use AttackedBy called for not DOT damage in Unit::DealDamage also
virtual void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/ ) {}
// Called when the creature receives heal
virtual void HealReceived(Unit* /*done_by*/, uint32& /*addhealth*/) {}
// Called when the unit heals
virtual void HealDone(Unit* /*done_to*/, uint32& /*addhealth*/) {}
void AttackStartCaster(Unit* victim, float dist);
void DoAddAuraToAllHostilePlayers(uint32 spellid);
void DoCast(uint32 spellId);
void DoCast(Unit* victim, uint32 spellId, bool triggered = false);
void DoCastToAllHostilePlayers(uint32 spellid, bool triggered = false);
void DoCastVictim(uint32 spellId, bool triggered = false);
void DoCastAOE(uint32 spellId, bool triggered = false);
float DoGetSpellMaxRange(uint32 spellId, bool positive = false);
void DoMeleeAttackIfReady();
bool DoSpellAttackIfReady(uint32 spell);
static AISpellInfoType* AISpellInfo;
static void FillAISpellInfo();
virtual void sGossipHello(Player* /*player*/) {}
virtual void sGossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) {}
virtual void sGossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, char const* /*code*/) {}
virtual void sQuestAccept(Player* /*player*/, Quest const* /*quest*/) {}
virtual void sQuestSelect(Player* /*player*/, Quest const* /*quest*/) {}
virtual void sQuestComplete(Player* /*player*/, Quest const* /*quest*/) {}
virtual void sQuestReward(Player* /*player*/, Quest const* /*quest*/, uint32 /*opt*/) {}
virtual void sOnGameEvent(bool /*start*/, uint16 /*eventId*/) {}
};
class PlayerAI : public UnitAI
{
protected:
Player* const me;
public:
explicit PlayerAI(Player* player) : UnitAI((Unit*)player), me(player) {}
void OnCharmed(bool apply);
};
class SimpleCharmedAI : public PlayerAI
{
public:
void UpdateAI(uint32 diff);
SimpleCharmedAI(Player* player): PlayerAI(player) {}
};
#endif

View File

@@ -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);
}

View File

@@ -0,0 +1,193 @@
/*
* 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/>.
*/
#ifndef TRINITY_CREATUREAI_H
#define TRINITY_CREATUREAI_H
#include "Creature.h"
#include "UnitAI.h"
#include "Common.h"
class WorldObject;
class Unit;
class Creature;
class Player;
class SpellInfo;
#define TIME_INTERVAL_LOOK 5000
#define VISIBILITY_RANGE 10000
//Spell targets used by SelectSpell
enum SelectTargetType
{
SELECT_TARGET_DONTCARE = 0, //All target types allowed
SELECT_TARGET_SELF, //Only Self casting
SELECT_TARGET_SINGLE_ENEMY, //Only Single Enemy
SELECT_TARGET_AOE_ENEMY, //Only AoE Enemy
SELECT_TARGET_ANY_ENEMY, //AoE or Single Enemy
SELECT_TARGET_SINGLE_FRIEND, //Only Single Friend
SELECT_TARGET_AOE_FRIEND, //Only AoE Friend
SELECT_TARGET_ANY_FRIEND, //AoE or Single Friend
};
//Spell Effects used by SelectSpell
enum SelectEffect
{
SELECT_EFFECT_DONTCARE = 0, //All spell effects allowed
SELECT_EFFECT_DAMAGE, //Spell does damage
SELECT_EFFECT_HEALING, //Spell does healing
SELECT_EFFECT_AURA, //Spell applies an aura
};
enum SCEquip
{
EQUIP_NO_CHANGE = -1,
EQUIP_UNEQUIP = 0
};
class CreatureAI : public UnitAI
{
protected:
Creature* const me;
bool UpdateVictim();
bool UpdateVictimWithGaze();
void SetGazeOn(Unit* target);
Creature* DoSummon(uint32 entry, Position const& pos, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
Creature* DoSummon(uint32 entry, WorldObject* obj, float radius = 5.0f, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
Creature* DoSummonFlyer(uint32 entry, WorldObject* obj, float flightZ, float radius = 5.0f, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
public:
void Talk(uint8 id, WorldObject const* whisperTarget = NULL);
explicit CreatureAI(Creature* creature) : UnitAI(creature), me(creature), m_MoveInLineOfSight_locked(false) {}
virtual ~CreatureAI() {}
/// == Reactions At =================================
// Called if IsVisible(Unit* who) is true at each who move, reaction at visibility zone enter
void MoveInLineOfSight_Safe(Unit* who);
// Called in Creature::Update when deathstate = DEAD. Inherited classes may maniuplate the ability to respawn based on scripted events.
virtual bool CanRespawn() { return true; }
// Called for reaction at stopping attack at no attackers or targets
virtual void EnterEvadeMode();
// Called for reaction at enter to combat if not in combat yet (enemy can be NULL)
virtual void EnterCombat(Unit* /*victim*/) {}
// Called when the creature is killed
virtual void JustDied(Unit* /*killer*/) {}
// Called when the creature kills a unit
virtual void KilledUnit(Unit* /*victim*/) {}
// Called when the creature summon successfully other creature
virtual void JustSummoned(Creature* /*summon*/) {}
virtual void IsSummonedBy(Unit* /*summoner*/) {}
virtual void SummonedCreatureDespawn(Creature* /*summon*/) {}
virtual void SummonedCreatureDies(Creature* /*summon*/, Unit* /*killer*/) {}
// Called when hit by a spell
virtual void SpellHit(Unit* /*caster*/, SpellInfo const* /*spell*/) {}
// Called when spell hits a target
virtual void SpellHitTarget(Unit* /*target*/, SpellInfo const* /*spell*/) {}
// Called when the creature is target of hostile action: swing, hostile spell landed, fear/etc)
virtual void AttackedBy(Unit* /*attacker*/) {}
virtual bool IsEscorted() { return false; }
// Called when creature is spawned or respawned (for reseting variables)
virtual void JustRespawned() { Reset(); }
// Called at waypoint reached or point movement finished
virtual void MovementInform(uint32 /*type*/, uint32 /*id*/) {}
void OnCharmed(bool apply);
// Called at reaching home after evade
virtual void JustReachedHome() {}
void DoZoneInCombat(Creature* creature = NULL, float maxRangeToNearestTarget = 50.0f);
// Called at text emote receive from player
virtual void ReceiveEmote(Player* /*player*/, uint32 /*emoteId*/) {}
// Called when owner takes damage
virtual void OwnerAttackedBy(Unit* /*attacker*/) {}
// Called when owner attacks something
virtual void OwnerAttacked(Unit* /*target*/) {}
/// == Triggered Actions Requested ==================
// Called when creature attack expected (if creature can and no have current victim)
// Note: for reaction at hostile action must be called AttackedBy function.
//virtual void AttackStart(Unit*) {}
// Called at World update tick
//virtual void UpdateAI(uint32 /*diff*/) {}
/// == State checks =================================
// Is unit visible for MoveInLineOfSight
//virtual bool IsVisible(Unit*) const { return false; }
// called when the corpse of this creature gets removed
virtual void CorpseRemoved(uint32& /*respawnDelay*/) {}
// Called when victim entered water and creature can not enter water
//virtual bool CanReachByRangeAttack(Unit*) { return false; }
/// == Fields =======================================
virtual void PassengerBoarded(Unit* /*passenger*/, int8 /*seatId*/, bool /*apply*/) {}
virtual void OnSpellClick(Unit* /*clicker*/, bool& /*result*/) { }
virtual bool CanSeeAlways(WorldObject const* /*obj*/) { return false; }
virtual bool CanBeSeen(Player const* /*seer*/) { return true; }
protected:
virtual void MoveInLineOfSight(Unit* /*who*/);
bool _EnterEvadeMode();
private:
bool m_MoveInLineOfSight_locked;
};
enum Permitions
{
PERMIT_BASE_NO = -1,
PERMIT_BASE_IDLE = 1,
PERMIT_BASE_REACTIVE = 100,
PERMIT_BASE_PROACTIVE = 200,
PERMIT_BASE_FACTION_SPECIFIC = 400,
PERMIT_BASE_SPECIAL = 800
};
#endif

View File

@@ -0,0 +1,80 @@
/*
* 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/>.
*/
#ifndef TRINITY_CREATUREAIFACTORY_H
#define TRINITY_CREATUREAIFACTORY_H
//#include "Policies/Singleton.h"
#include "ObjectRegistry.h"
#include "FactoryHolder.h"
#include "GameObjectAI.h"
struct SelectableAI : public FactoryHolder<CreatureAI>, public Permissible<Creature>
{
SelectableAI(const char* id) : FactoryHolder<CreatureAI>(id) {}
};
template<class REAL_AI>
struct CreatureAIFactory : public SelectableAI
{
CreatureAIFactory(const char* name) : SelectableAI(name) {}
CreatureAI* Create(void*) const;
int Permit(const Creature* c) const { return REAL_AI::Permissible(c); }
};
template<class REAL_AI>
inline CreatureAI*
CreatureAIFactory<REAL_AI>::Create(void* data) const
{
Creature* creature = reinterpret_cast<Creature*>(data);
return (new REAL_AI(creature));
}
typedef FactoryHolder<CreatureAI> CreatureAICreator;
typedef FactoryHolder<CreatureAI>::FactoryHolderRegistry CreatureAIRegistry;
typedef FactoryHolder<CreatureAI>::FactoryHolderRepository CreatureAIRepository;
//GO
struct SelectableGameObjectAI : public FactoryHolder<GameObjectAI>, public Permissible<GameObject>
{
SelectableGameObjectAI(const char* id) : FactoryHolder<GameObjectAI>(id) {}
};
template<class REAL_GO_AI>
struct GameObjectAIFactory : public SelectableGameObjectAI
{
GameObjectAIFactory(const char* name) : SelectableGameObjectAI(name) {}
GameObjectAI* Create(void*) const;
int Permit(const GameObject* g) const { return REAL_GO_AI::Permissible(g); }
};
template<class REAL_GO_AI>
inline GameObjectAI*
GameObjectAIFactory<REAL_GO_AI>::Create(void* data) const
{
GameObject* go = reinterpret_cast<GameObject*>(data);
return (new REAL_GO_AI(go));
}
typedef FactoryHolder<GameObjectAI> GameObjectAICreator;
typedef FactoryHolder<GameObjectAI>::FactoryHolderRegistry GameObjectAIRegistry;
typedef FactoryHolder<GameObjectAI>::FactoryHolderRepository GameObjectAIRepository;
#endif

View File

@@ -0,0 +1,347 @@
/*
* 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/>.
*/
#ifndef CREATUREAIIMPL_H
#define CREATUREAIIMPL_H
#include "Common.h"
#include "Define.h"
#include "TemporarySummon.h"
#include "CreatureAI.h"
#include "SpellMgr.h"
template<class T>
inline
const T& RAND(const T& v1, const T& v2)
{
return (urand(0, 1)) ? v1 : v2;
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3)
{
switch (urand(0, 2))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4)
{
switch (urand(0, 3))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5)
{
switch (urand(0, 4))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6)
{
switch (urand(0, 5))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7)
{
switch (urand(0, 6))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8)
{
switch (urand(0, 7))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9)
{
switch (urand(0, 8))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9, const T& v10)
{
switch (urand(0, 9))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
case 9: return v10;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9, const T& v10, const T& v11)
{
switch (urand(0, 10))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
case 9: return v10;
case 10: return v11;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9, const T& v10, const T& v11, const T& v12)
{
switch (urand(0, 11))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
case 9: return v10;
case 10: return v11;
case 11: return v12;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9, const T& v10, const T& v11, const T& v12, const T& v13)
{
switch (urand(0, 12))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
case 9: return v10;
case 10: return v11;
case 11: return v12;
case 12: return v13;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9, const T& v10, const T& v11, const T& v12, const T& v13, const T& v14)
{
switch (urand(0, 13))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
case 9: return v10;
case 10: return v11;
case 11: return v12;
case 12: return v13;
case 13: return v14;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9, const T& v10, const T& v11, const T& v12, const T& v13, const T& v14, const T& v15)
{
switch (urand(0, 14))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
case 9: return v10;
case 10: return v11;
case 11: return v12;
case 12: return v13;
case 13: return v14;
case 14: return v15;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9, const T& v10, const T& v11, const T& v12, const T& v13, const T& v14, const T& v15, const T& v16)
{
switch (urand(0, 15))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
case 9: return v10;
case 10: return v11;
case 11: return v12;
case 12: return v13;
case 13: return v14;
case 14: return v15;
case 15: return v16;
}
}
enum AITarget
{
AITARGET_SELF,
AITARGET_VICTIM,
AITARGET_ENEMY,
AITARGET_ALLY,
AITARGET_BUFF,
AITARGET_DEBUFF,
};
enum AICondition
{
AICOND_AGGRO,
AICOND_COMBAT,
AICOND_DIE,
};
#define AI_DEFAULT_COOLDOWN 5000
struct AISpellInfoType
{
AISpellInfoType() : target(AITARGET_SELF), condition(AICOND_COMBAT)
, cooldown(AI_DEFAULT_COOLDOWN), realCooldown(0), maxRange(0.0f){}
AITarget target;
AICondition condition;
uint32 cooldown;
uint32 realCooldown;
float maxRange;
};
AISpellInfoType* GetAISpellInfo(uint32 i);
#endif

View File

@@ -0,0 +1,59 @@
/*
* 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 "PassiveAI.h"
#include "ReactorAI.h"
#include "CombatAI.h"
#include "GuardAI.h"
#include "PetAI.h"
#include "TotemAI.h"
#include "RandomMovementGenerator.h"
#include "MovementGeneratorImpl.h"
#include "CreatureAIRegistry.h"
#include "WaypointMovementGenerator.h"
#include "CreatureAIFactory.h"
#include "SmartAI.h"
//#include "CreatureAIImpl.h"
namespace AIRegistry
{
void Initialize()
{
(new CreatureAIFactory<NullCreatureAI>("NullCreatureAI"))->RegisterSelf();
(new CreatureAIFactory<TriggerAI>("TriggerAI"))->RegisterSelf();
(new CreatureAIFactory<AggressorAI>("AggressorAI"))->RegisterSelf();
(new CreatureAIFactory<ReactorAI>("ReactorAI"))->RegisterSelf();
(new CreatureAIFactory<PassiveAI>("PassiveAI"))->RegisterSelf();
(new CreatureAIFactory<CritterAI>("CritterAI"))->RegisterSelf();
(new CreatureAIFactory<GuardAI>("GuardAI"))->RegisterSelf();
(new CreatureAIFactory<PetAI>("PetAI"))->RegisterSelf();
(new CreatureAIFactory<TotemAI>("TotemAI"))->RegisterSelf();
(new CreatureAIFactory<CombatAI>("CombatAI"))->RegisterSelf();
(new CreatureAIFactory<ArcherAI>("ArcherAI"))->RegisterSelf();
(new CreatureAIFactory<TurretAI>("TurretAI"))->RegisterSelf();
(new CreatureAIFactory<VehicleAI>("VehicleAI"))->RegisterSelf();
(new CreatureAIFactory<SmartAI>("SmartAI"))->RegisterSelf();
(new GameObjectAIFactory<GameObjectAI>("GameObjectAI"))->RegisterSelf();
(new GameObjectAIFactory<SmartGameObjectAI>("SmartGameObjectAI"))->RegisterSelf();
(new MovementGeneratorFactory<RandomMovementGenerator<Creature> >(RANDOM_MOTION_TYPE))->RegisterSelf();
(new MovementGeneratorFactory<WaypointMovementGenerator<Creature> >(WAYPOINT_MOTION_TYPE))->RegisterSelf();
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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/>.
*/
#ifndef TRINITY_CREATUREAIREGISTRY_H
#define TRINITY_CREATUREAIREGISTRY_H
namespace AIRegistry
{
void Initialize(void);
}
#endif

View File

@@ -0,0 +1,154 @@
/*
* 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 "Creature.h"
#include "CreatureAISelector.h"
#include "PassiveAI.h"
#include "MovementGenerator.h"
#include "Pet.h"
#include "TemporarySummon.h"
#include "CreatureAIFactory.h"
#include "ScriptMgr.h"
namespace FactorySelector
{
CreatureAI* selectAI(Creature* creature)
{
const CreatureAICreator* ai_factory = NULL;
CreatureAIRegistry& ai_registry(*CreatureAIRepository::instance());
// xinef: if we have controlable guardian, define petai for players as they can steer him, otherwise db / normal ai
// xinef: dont remember why i changed this qq commented out as may break some quests
if (creature->IsPet()/* || (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN) && ((Guardian*)creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER)*/)
ai_factory = ai_registry.GetRegistryItem("PetAI");
//scriptname in db
if (!ai_factory)
if (CreatureAI* scriptedAI = sScriptMgr->GetCreatureAI(creature))
return scriptedAI;
// AIname in db
std::string ainame=creature->GetAIName();
if (!ai_factory && !ainame.empty())
ai_factory = ai_registry.GetRegistryItem(ainame);
// select by NPC flags
if (!ai_factory)
{
if (creature->IsVehicle())
ai_factory = ai_registry.GetRegistryItem("VehicleAI");
else if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN) && ((Guardian*)creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER)
ai_factory = ai_registry.GetRegistryItem("PetAI");
else if (creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK))
ai_factory = ai_registry.GetRegistryItem("NullCreatureAI");
else if (creature->IsGuard())
ai_factory = ai_registry.GetRegistryItem("GuardAI");
else if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
ai_factory = ai_registry.GetRegistryItem("PetAI");
else if (creature->IsTotem())
ai_factory = ai_registry.GetRegistryItem("TotemAI");
else if (creature->IsTrigger())
{
if (creature->m_spells[0])
ai_factory = ai_registry.GetRegistryItem("TriggerAI");
else
ai_factory = ai_registry.GetRegistryItem("NullCreatureAI");
}
else if (creature->IsCritter() && !creature->HasUnitTypeMask(UNIT_MASK_GUARDIAN))
ai_factory = ai_registry.GetRegistryItem("CritterAI");
}
// select by permit check
if (!ai_factory)
{
int best_val = -1;
typedef CreatureAIRegistry::RegistryMapType RMT;
RMT const& l = ai_registry.GetRegisteredItems();
for (RMT::const_iterator iter = l.begin(); iter != l.end(); ++iter)
{
const CreatureAICreator* factory = iter->second;
const SelectableAI* p = dynamic_cast<const SelectableAI*>(factory);
ASSERT(p);
int val = p->Permit(creature);
if (val > best_val)
{
best_val = val;
ai_factory = p;
}
}
}
// select NullCreatureAI if not another cases
// xinef: unused
// ainame = (ai_factory == NULL) ? "NullCreatureAI" : ai_factory->key();
;//sLog->outDebug(LOG_FILTER_TSCR, "Creature %u used AI is %s.", creature->GetGUIDLow(), ainame.c_str());
return (ai_factory == NULL ? new NullCreatureAI(creature) : ai_factory->Create(creature));
}
MovementGenerator* selectMovementGenerator(Creature* creature)
{
MovementGeneratorRegistry& mv_registry(*MovementGeneratorRepository::instance());
ASSERT(creature->GetCreatureTemplate());
const MovementGeneratorCreator* mv_factory = mv_registry.GetRegistryItem(creature->GetDefaultMovementType());
/* if (mv_factory == NULL)
{
int best_val = -1;
StringVector l;
mv_registry.GetRegisteredItems(l);
for (StringVector::iterator iter = l.begin(); iter != l.end(); ++iter)
{
const MovementGeneratorCreator *factory = mv_registry.GetRegistryItem((*iter).c_str());
const SelectableMovement *p = dynamic_cast<const SelectableMovement *>(factory);
ASSERT(p != NULL);
int val = p->Permit(creature);
if (val > best_val)
{
best_val = val;
mv_factory = p;
}
}
}*/
return (mv_factory == NULL ? NULL : mv_factory->Create(creature));
}
GameObjectAI* SelectGameObjectAI(GameObject* go)
{
const GameObjectAICreator* ai_factory = NULL;
GameObjectAIRegistry& ai_registry(*GameObjectAIRepository::instance());
if (GameObjectAI* scriptedAI = sScriptMgr->GetGameObjectAI(go))
return scriptedAI;
ai_factory = ai_registry.GetRegistryItem(go->GetAIName());
//future goAI types go here
// xinef: unused
//std::string ainame = (ai_factory == NULL || go->GetScriptId()) ? "NullGameObjectAI" : ai_factory->key();
;//sLog->outDebug(LOG_FILTER_TSCR, "GameObject %u used AI is %s.", go->GetGUIDLow(), ainame.c_str());
return (ai_factory == NULL ? new NullGameObjectAI(go) : ai_factory->Create(go));
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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/>.
*/
#ifndef TRINITY_CREATUREAISELECTOR_H
#define TRINITY_CREATUREAISELECTOR_H
class CreatureAI;
class Creature;
class MovementGenerator;
class GameObjectAI;
class GameObject;
namespace FactorySelector
{
CreatureAI* selectAI(Creature*);
MovementGenerator* selectMovementGenerator(Creature*);
GameObjectAI* SelectGameObjectAI(GameObject*);
}
#endif

View File

@@ -0,0 +1,668 @@
/*
* Copyright (C)
*
*
*
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
#include "ScriptedCreature.h"
#include "Item.h"
#include "Spell.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "Cell.h"
#include "CellImpl.h"
#include "ObjectMgr.h"
#include "TemporarySummon.h"
// Spell summary for ScriptedAI::SelectSpell
struct TSpellSummary
{
uint8 Targets; // set of enum SelectTarget
uint8 Effects; // set of enum SelectEffect
} extern* SpellSummary;
void SummonList::DoZoneInCombat(uint32 entry)
{
for (StorageType::iterator i = storage_.begin(); i != storage_.end();)
{
Creature* summon = ObjectAccessor::GetCreature(*me, *i);
++i;
if (summon && summon->IsAIEnabled
&& (!entry || summon->GetEntry() == entry))
{
summon->AI()->DoZoneInCombat();
}
}
}
void SummonList::DespawnEntry(uint32 entry)
{
for (StorageType::iterator i = storage_.begin(); i != storage_.end();)
{
Creature* summon = ObjectAccessor::GetCreature(*me, *i);
if (!summon)
i = storage_.erase(i);
else if (summon->GetEntry() == entry)
{
i = storage_.erase(i);
summon->DespawnOrUnsummon();
}
else
++i;
}
}
void SummonList::DespawnAll()
{
while (!storage_.empty())
{
Creature* summon = ObjectAccessor::GetCreature(*me, storage_.front());
storage_.pop_front();
if (summon)
summon->DespawnOrUnsummon();
}
}
void SummonList::RemoveNotExisting()
{
for (StorageType::iterator i = storage_.begin(); i != storage_.end();)
{
if (ObjectAccessor::GetCreature(*me, *i))
++i;
else
i = storage_.erase(i);
}
}
bool SummonList::HasEntry(uint32 entry) const
{
for (StorageType::const_iterator i = storage_.begin(); i != storage_.end(); ++i)
{
Creature* summon = ObjectAccessor::GetCreature(*me, *i);
if (summon && summon->GetEntry() == entry)
return true;
}
return false;
}
uint32 SummonList::GetEntryCount(uint32 entry) const
{
uint32 count = 0;
for (StorageType::const_iterator i = storage_.begin(); i != storage_.end(); ++i)
{
Creature* summon = ObjectAccessor::GetCreature(*me, *i);
if (summon && summon->GetEntry() == entry)
++count;
}
return count;
}
void SummonList::Respawn()
{
for (StorageType::iterator i = storage_.begin(); i != storage_.end();)
{
if (Creature* summon = ObjectAccessor::GetCreature(*me, *i))
{
summon->Respawn(true);
++i;
}
else
i = storage_.erase(i);
}
}
Creature* SummonList::GetCreatureWithEntry(uint32 entry) const
{
for (StorageType::const_iterator i = storage_.begin(); i != storage_.end(); ++i)
{
if (Creature* summon = ObjectAccessor::GetCreature(*me, *i))
if (summon->GetEntry() == entry)
return summon;
}
return NULL;
}
ScriptedAI::ScriptedAI(Creature* creature) : CreatureAI(creature),
me(creature),
IsFleeing(false),
_evadeCheckCooldown(2500),
_isCombatMovementAllowed(true)
{
_isHeroic = me->GetMap()->IsHeroic();
_difficulty = Difficulty(me->GetMap()->GetSpawnMode());
}
void ScriptedAI::AttackStartNoMove(Unit* who)
{
if (!who)
return;
if (me->Attack(who, true))
DoStartNoMovement(who);
}
void ScriptedAI::AttackStart(Unit* who)
{
if (IsCombatMovementAllowed())
CreatureAI::AttackStart(who);
else
AttackStartNoMove(who);
}
void ScriptedAI::UpdateAI(uint32 /*diff*/)
{
//Check if we have a current target
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
void ScriptedAI::DoStartMovement(Unit* victim, float distance, float angle)
{
if (victim)
me->GetMotionMaster()->MoveChase(victim, distance, angle);
}
void ScriptedAI::DoStartNoMovement(Unit* victim)
{
if (!victim)
return;
me->GetMotionMaster()->MoveIdle();
}
void ScriptedAI::DoStopAttack()
{
if (me->GetVictim())
me->AttackStop();
}
void ScriptedAI::DoCastSpell(Unit* target, SpellInfo const* spellInfo, bool triggered)
{
if (!target || me->IsNonMeleeSpellCast(false))
return;
me->StopMoving();
me->CastSpell(target, spellInfo, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE);
}
void ScriptedAI::DoPlaySoundToSet(WorldObject* source, uint32 soundId)
{
if (!source)
return;
if (!sSoundEntriesStore.LookupEntry(soundId))
{
sLog->outError("TSCR: Invalid soundId %u used in DoPlaySoundToSet (Source: TypeId %u, GUID %u)", soundId, source->GetTypeId(), source->GetGUIDLow());
return;
}
source->PlayDirectSound(soundId);
}
Creature* ScriptedAI::DoSpawnCreature(uint32 entry, float offsetX, float offsetY, float offsetZ, float angle, uint32 type, uint32 despawntime)
{
return me->SummonCreature(entry, me->GetPositionX() + offsetX, me->GetPositionY() + offsetY, me->GetPositionZ() + offsetZ, angle, TempSummonType(type), despawntime);
}
SpellInfo const* ScriptedAI::SelectSpell(Unit* target, uint32 school, uint32 mechanic, SelectTargetType targets, uint32 powerCostMin, uint32 powerCostMax, float rangeMin, float rangeMax, SelectEffect effects)
{
//No target so we can't cast
if (!target)
return NULL;
//Silenced so we can't cast
if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
return NULL;
//Using the extended script system we first create a list of viable spells
SpellInfo const* apSpell[CREATURE_MAX_SPELLS];
memset(apSpell, 0, CREATURE_MAX_SPELLS * sizeof(SpellInfo*));
uint32 spellCount = 0;
SpellInfo const* tempSpell = NULL;
//Check if each spell is viable(set it to null if not)
for (uint32 i = 0; i < CREATURE_MAX_SPELLS; i++)
{
tempSpell = sSpellMgr->GetSpellInfo(me->m_spells[i]);
//This spell doesn't exist
if (!tempSpell)
continue;
// Targets and Effects checked first as most used restrictions
//Check the spell targets if specified
if (targets && !(SpellSummary[me->m_spells[i]].Targets & (1 << (targets-1))))
continue;
//Check the type of spell if we are looking for a specific spell type
if (effects && !(SpellSummary[me->m_spells[i]].Effects & (1 << (effects-1))))
continue;
//Check for school if specified
if (school && (tempSpell->SchoolMask & school) == 0)
continue;
//Check for spell mechanic if specified
if (mechanic && tempSpell->Mechanic != mechanic)
continue;
//Make sure that the spell uses the requested amount of power
if (powerCostMin && tempSpell->ManaCost < powerCostMin)
continue;
if (powerCostMax && tempSpell->ManaCost > powerCostMax)
continue;
//Continue if we don't have the mana to actually cast this spell
if (tempSpell->ManaCost > me->GetPower(Powers(tempSpell->PowerType)))
continue;
//Check if the spell meets our range requirements
if (rangeMin && me->GetSpellMinRangeForTarget(target, tempSpell) < rangeMin)
continue;
if (rangeMax && me->GetSpellMaxRangeForTarget(target, tempSpell) > rangeMax)
continue;
//Check if our target is in range
if (me->IsWithinDistInMap(target, float(me->GetSpellMinRangeForTarget(target, tempSpell))) || !me->IsWithinDistInMap(target, float(me->GetSpellMaxRangeForTarget(target, tempSpell))))
continue;
//All good so lets add it to the spell list
apSpell[spellCount] = tempSpell;
++spellCount;
}
//We got our usable spells so now lets randomly pick one
if (!spellCount)
return NULL;
return apSpell[urand(0, spellCount - 1)];
}
void ScriptedAI::DoResetThreat()
{
if (!me->CanHaveThreatList() || me->getThreatManager().isThreatListEmpty())
{
sLog->outError("DoResetThreat called for creature that either cannot have threat list or has empty threat list (me entry = %d)", me->GetEntry());
return;
}
me->getThreatManager().resetAllAggro();
}
float ScriptedAI::DoGetThreat(Unit* unit)
{
if (!unit)
return 0.0f;
return me->getThreatManager().getThreat(unit);
}
void ScriptedAI::DoModifyThreatPercent(Unit* unit, int32 pct)
{
if (!unit)
return;
me->getThreatManager().modifyThreatPercent(unit, pct);
}
void ScriptedAI::DoTeleportPlayer(Unit* unit, float x, float y, float z, float o)
{
if (!unit)
return;
if (Player* player = unit->ToPlayer())
player->TeleportTo(unit->GetMapId(), x, y, z, o, TELE_TO_NOT_LEAVE_COMBAT);
else
sLog->outError("TSCR: Creature " UI64FMTD " (Entry: %u) Tried to teleport non-player unit (Type: %u GUID: " UI64FMTD ") to x: %f y:%f z: %f o: %f. Aborted.", me->GetGUID(), me->GetEntry(), unit->GetTypeId(), unit->GetGUID(), x, y, z, o);
}
void ScriptedAI::DoTeleportAll(float x, float y, float z, float o)
{
Map* map = me->GetMap();
if (!map->IsDungeon())
return;
Map::PlayerList const& PlayerList = map->GetPlayers();
for (Map::PlayerList::const_iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr)
if (Player* player = itr->GetSource())
if (player->IsAlive())
player->TeleportTo(me->GetMapId(), x, y, z, o, TELE_TO_NOT_LEAVE_COMBAT);
}
Unit* ScriptedAI::DoSelectLowestHpFriendly(float range, uint32 minHPDiff)
{
Unit* unit = NULL;
Trinity::MostHPMissingInRange u_check(me, range, minHPDiff);
Trinity::UnitLastSearcher<Trinity::MostHPMissingInRange> searcher(me, unit, u_check);
me->VisitNearbyObject(range, searcher);
return unit;
}
std::list<Creature*> ScriptedAI::DoFindFriendlyCC(float range)
{
std::list<Creature*> list;
Trinity::FriendlyCCedInRange u_check(me, range);
Trinity::CreatureListSearcher<Trinity::FriendlyCCedInRange> searcher(me, list, u_check);
me->VisitNearbyObject(range, searcher);
return list;
}
std::list<Creature*> ScriptedAI::DoFindFriendlyMissingBuff(float range, uint32 uiSpellid)
{
std::list<Creature*> list;
Trinity::FriendlyMissingBuffInRange u_check(me, range, uiSpellid);
Trinity::CreatureListSearcher<Trinity::FriendlyMissingBuffInRange> searcher(me, list, u_check);
me->VisitNearbyObject(range, searcher);
return list;
}
Player* ScriptedAI::GetPlayerAtMinimumRange(float minimumRange)
{
Player* player = NULL;
CellCoord pair(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
Cell cell(pair);
cell.SetNoCreate();
Trinity::PlayerAtMinimumRangeAway check(me, minimumRange);
Trinity::PlayerSearcher<Trinity::PlayerAtMinimumRangeAway> searcher(me, player, check);
TypeContainerVisitor<Trinity::PlayerSearcher<Trinity::PlayerAtMinimumRangeAway>, GridTypeMapContainer> visitor(searcher);
cell.Visit(pair, visitor, *me->GetMap(), *me, minimumRange);
return player;
}
void ScriptedAI::SetEquipmentSlots(bool loadDefault, int32 mainHand /*= EQUIP_NO_CHANGE*/, int32 offHand /*= EQUIP_NO_CHANGE*/, int32 ranged /*= EQUIP_NO_CHANGE*/)
{
if (loadDefault)
{
me->LoadEquipment(me->GetOriginalEquipmentId(), true);
return;
}
if (mainHand >= 0)
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(mainHand));
if (offHand >= 0)
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(offHand));
if (ranged >= 0)
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, uint32(ranged));
}
void ScriptedAI::SetCombatMovement(bool allowMovement)
{
_isCombatMovementAllowed = allowMovement;
}
enum eNPCs
{
NPC_BROODLORD = 12017,
NPC_JAN_ALAI = 23578,
NPC_SARTHARION = 28860,
NPC_FREYA = 32906,
};
bool ScriptedAI::EnterEvadeIfOutOfCombatArea()
{
if (me->IsInEvadeMode() || !me->IsInCombat())
return false;
if (_evadeCheckCooldown == time(NULL))
return false;
_evadeCheckCooldown = time(NULL);
if (!CheckEvadeIfOutOfCombatArea())
return false;
EnterEvadeMode();
return true;
}
Player* ScriptedAI::SelectTargetFromPlayerList(float maxdist, uint32 excludeAura, bool mustBeInLOS) const
{
Map::PlayerList const& pList = me->GetMap()->GetPlayers();
std::vector<Player*> tList;
for(Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr)
{
if (me->GetDistance(itr->GetSource()) > maxdist || !itr->GetSource()->IsAlive() || itr->GetSource()->IsGameMaster())
continue;
if (excludeAura && itr->GetSource()->HasAura(excludeAura))
continue;
if (mustBeInLOS && !me->IsWithinLOSInMap(itr->GetSource()))
continue;
tList.push_back(itr->GetSource());
}
if (!tList.empty())
return tList[urand(0,tList.size()-1)];
else
return NULL;
}
// BossAI - for instanced bosses
BossAI::BossAI(Creature* creature, uint32 bossId) : ScriptedAI(creature),
instance(creature->GetInstanceScript()),
summons(creature),
_boundary(instance ? instance->GetBossBoundary(bossId) : NULL),
_bossId(bossId)
{
}
void BossAI::_Reset()
{
if (!me->IsAlive())
return;
me->ResetLootMode();
events.Reset();
summons.DespawnAll();
if (instance)
instance->SetBossState(_bossId, NOT_STARTED);
}
void BossAI::_JustDied()
{
events.Reset();
summons.DespawnAll();
if (instance)
{
instance->SetBossState(_bossId, DONE);
instance->SaveToDB();
}
}
void BossAI::_EnterCombat()
{
me->setActive(true);
DoZoneInCombat();
if (instance)
{
// bosses do not respawn, check only on enter combat
if (!instance->CheckRequiredBosses(_bossId))
{
EnterEvadeMode();
return;
}
instance->SetBossState(_bossId, IN_PROGRESS);
}
}
void BossAI::TeleportCheaters()
{
float x, y, z;
me->GetPosition(x, y, z);
ThreatContainer::StorageType threatList = me->getThreatManager().getThreatList();
for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
if (Unit* target = (*itr)->getTarget())
if (target->GetTypeId() == TYPEID_PLAYER && !CheckBoundary(target))
target->NearTeleportTo(x, y, z, 0);
}
bool BossAI::CheckBoundary(Unit* who)
{
if (!GetBoundary() || !who)
return true;
for (BossBoundaryMap::const_iterator itr = GetBoundary()->begin(); itr != GetBoundary()->end(); ++itr)
{
switch (itr->first)
{
case BOUNDARY_N:
if (who->GetPositionX() > itr->second)
return false;
break;
case BOUNDARY_S:
if (who->GetPositionX() < itr->second)
return false;
break;
case BOUNDARY_E:
if (who->GetPositionY() < itr->second)
return false;
break;
case BOUNDARY_W:
if (who->GetPositionY() > itr->second)
return false;
break;
case BOUNDARY_NW:
if (who->GetPositionX() + who->GetPositionY() > itr->second)
return false;
break;
case BOUNDARY_SE:
if (who->GetPositionX() + who->GetPositionY() < itr->second)
return false;
break;
case BOUNDARY_NE:
if (who->GetPositionX() - who->GetPositionY() > itr->second)
return false;
break;
case BOUNDARY_SW:
if (who->GetPositionX() - who->GetPositionY() < itr->second)
return false;
break;
default:
break;
}
}
return true;
}
void BossAI::JustSummoned(Creature* summon)
{
summons.Summon(summon);
if (me->IsInCombat())
DoZoneInCombat(summon);
}
void BossAI::SummonedCreatureDespawn(Creature* summon)
{
summons.Despawn(summon);
}
void BossAI::UpdateAI(uint32 diff)
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
ExecuteEvent(eventId);
DoMeleeAttackIfReady();
}
// WorldBossAI - for non-instanced bosses
WorldBossAI::WorldBossAI(Creature* creature) :
ScriptedAI(creature),
summons(creature)
{
}
void WorldBossAI::_Reset()
{
if (!me->IsAlive())
return;
events.Reset();
summons.DespawnAll();
}
void WorldBossAI::_JustDied()
{
events.Reset();
summons.DespawnAll();
}
void WorldBossAI::_EnterCombat()
{
Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true);
if (target)
AttackStart(target);
}
void WorldBossAI::JustSummoned(Creature* summon)
{
summons.Summon(summon);
Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true);
if (target)
summon->AI()->AttackStart(target);
}
void WorldBossAI::SummonedCreatureDespawn(Creature* summon)
{
summons.Despawn(summon);
}
void WorldBossAI::UpdateAI(uint32 diff)
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
ExecuteEvent(eventId);
DoMeleeAttackIfReady();
}
// SD2 grid searchers.
Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool alive /*= true*/)
{
return source->FindNearestCreature(entry, maxSearchRange, alive);
}
GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange)
{
return source->FindNearestGameObject(entry, maxSearchRange);
}
void GetCreatureListWithEntryInGrid(std::list<Creature*>& list, WorldObject* source, uint32 entry, float maxSearchRange)
{
source->GetCreatureListWithEntryInGrid(list, entry, maxSearchRange);
}
void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& list, WorldObject* source, uint32 entry, float maxSearchRange)
{
source->GetGameObjectListWithEntryInGrid(list, entry, maxSearchRange);
}

View File

@@ -0,0 +1,457 @@
/*
* 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/>.
*/
#ifndef SCRIPTEDCREATURE_H_
#define SCRIPTEDCREATURE_H_
#include "Creature.h"
#include "CreatureAI.h"
#include "CreatureAIImpl.h"
#include "InstanceScript.h"
#define CAST_AI(a, b) (dynamic_cast<a*>(b))
class InstanceScript;
class SummonList
{
public:
typedef std::list<uint64> StorageType;
typedef StorageType::iterator iterator;
typedef StorageType::const_iterator const_iterator;
typedef StorageType::size_type size_type;
typedef StorageType::value_type value_type;
explicit SummonList(Creature* creature)
: me(creature)
{ }
// And here we see a problem of original inheritance approach. People started
// to exploit presence of std::list members, so I have to provide wrappers
iterator begin()
{
return storage_.begin();
}
const_iterator begin() const
{
return storage_.begin();
}
iterator end()
{
return storage_.end();
}
const_iterator end() const
{
return storage_.end();
}
iterator erase(iterator i)
{
return storage_.erase(i);
}
bool empty() const
{
return storage_.empty();
}
size_type size() const
{
return storage_.size();
}
void clear()
{
storage_.clear();
}
void Summon(Creature const* summon) { storage_.push_back(summon->GetGUID()); }
void Despawn(Creature const* summon) { storage_.remove(summon->GetGUID()); }
void DespawnEntry(uint32 entry);
void DespawnAll();
template <typename T>
void DespawnIf(T const &predicate)
{
storage_.remove_if(predicate);
}
void DoAction(int32 info, uint16 max = 0)
{
if (max)
RemoveNotExisting(); // pussywizard: when max is set, non existing can be chosen and nothing will happen
StorageType listCopy = storage_;
for (StorageType::const_iterator i = listCopy.begin(); i != listCopy.end(); ++i)
{
if (Creature* summon = ObjectAccessor::GetCreature(*me, *i))
if (summon->IsAIEnabled)
summon->AI()->DoAction(info);
}
}
template <class Predicate>
void DoAction(int32 info, Predicate& predicate, uint16 max = 0)
{
if (max)
RemoveNotExisting(); // pussywizard: when max is set, non existing can be chosen and nothing will happen
// We need to use a copy of SummonList here, otherwise original SummonList would be modified
StorageType listCopy = storage_;
Trinity::Containers::RandomResizeList<uint64, Predicate>(listCopy, predicate, max);
for (StorageType::iterator i = listCopy.begin(); i != listCopy.end(); ++i)
{
Creature* summon = ObjectAccessor::GetCreature(*me, *i);
if (summon)
{
if (summon->IsAIEnabled)
summon->AI()->DoAction(info);
}
else
storage_.remove(*i);
}
}
void DoZoneInCombat(uint32 entry = 0);
void RemoveNotExisting();
bool HasEntry(uint32 entry) const;
uint32 GetEntryCount(uint32 entry) const;
void Respawn();
Creature* GetCreatureWithEntry(uint32 entry) const;
private:
Creature* me;
StorageType storage_;
};
class EntryCheckPredicate
{
public:
EntryCheckPredicate(uint32 entry) : _entry(entry) {}
bool operator()(uint64 guid) { return GUID_ENPART(guid) == _entry; }
private:
uint32 _entry;
};
class PlayerOrPetCheck
{
public:
bool operator() (WorldObject* unit) const
{
if (unit->GetTypeId() != TYPEID_PLAYER)
if (!IS_PLAYER_GUID(unit->ToUnit()->GetOwnerGUID()))
return true;
return false;
}
};
struct ScriptedAI : public CreatureAI
{
explicit ScriptedAI(Creature* creature);
virtual ~ScriptedAI() {}
// *************
//CreatureAI Functions
// *************
void AttackStartNoMove(Unit* target);
// Called at any Damage from any attacker (before damage apply)
void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) {}
//Called at World update tick
virtual void UpdateAI(uint32 diff);
//Called at creature death
void JustDied(Unit* /*killer*/) {}
//Called at creature killing another unit
void KilledUnit(Unit* /*victim*/) {}
// Called when the creature summon successfully other creature
void JustSummoned(Creature* /*summon*/) {}
// Called when a summoned creature is despawned
void SummonedCreatureDespawn(Creature* /*summon*/) {}
// Called when hit by a spell
void SpellHit(Unit* /*caster*/, SpellInfo const* /*spell*/) {}
// Called when spell hits a target
void SpellHitTarget(Unit* /*target*/, SpellInfo const* /*spell*/) {}
//Called at waypoint reached or PointMovement end
void MovementInform(uint32 /*type*/, uint32 /*id*/) {}
// Called when AI is temporarily replaced or put back when possess is applied or removed
void OnPossess(bool /*apply*/) {}
// *************
// Variables
// *************
//Pointer to creature we are manipulating
Creature* me;
//For fleeing
bool IsFleeing;
// *************
//Pure virtual functions
// *************
//Called at creature reset either by death or evade
void Reset() {}
//Called at creature aggro either by MoveInLOS or Attack Start
void EnterCombat(Unit* /*victim*/) {}
// Called before EnterCombat even before the creature is in combat.
void AttackStart(Unit* /*target*/);
// *************
//AI Helper Functions
// *************
//Start movement toward victim
void DoStartMovement(Unit* target, float distance = 0.0f, float angle = 0.0f);
//Start no movement on victim
void DoStartNoMovement(Unit* target);
//Stop attack of current victim
void DoStopAttack();
//Cast spell by spell info
void DoCastSpell(Unit* target, SpellInfo const* spellInfo, bool triggered = false);
//Plays a sound to all nearby players
void DoPlaySoundToSet(WorldObject* source, uint32 soundId);
//Drops all threat to 0%. Does not remove players from the threat list
void DoResetThreat();
float DoGetThreat(Unit* unit);
void DoModifyThreatPercent(Unit* unit, int32 pct);
//Teleports a player without dropping threat (only teleports to same map)
void DoTeleportPlayer(Unit* unit, float x, float y, float z, float o);
void DoTeleportAll(float x, float y, float z, float o);
//Returns friendly unit with the most amount of hp missing from max hp
Unit* DoSelectLowestHpFriendly(float range, uint32 minHPDiff = 1);
//Returns a list of friendly CC'd units within range
std::list<Creature*> DoFindFriendlyCC(float range);
//Returns a list of all friendly units missing a specific buff within range
std::list<Creature*> DoFindFriendlyMissingBuff(float range, uint32 spellId);
//Return a player with at least minimumRange from me
Player* GetPlayerAtMinimumRange(float minRange);
//Spawns a creature relative to me
Creature* DoSpawnCreature(uint32 entry, float offsetX, float offsetY, float offsetZ, float angle, uint32 type, uint32 despawntime);
bool HealthBelowPct(uint32 pct) const { return me->HealthBelowPct(pct); }
bool HealthAbovePct(uint32 pct) const { return me->HealthAbovePct(pct); }
//Returns spells that meet the specified criteria from the creatures spell list
SpellInfo const* SelectSpell(Unit* target, uint32 school, uint32 mechanic, SelectTargetType targets, uint32 powerCostMin, uint32 powerCostMax, float rangeMin, float rangeMax, SelectEffect effect);
void SetEquipmentSlots(bool loadDefault, int32 mainHand = EQUIP_NO_CHANGE, int32 offHand = EQUIP_NO_CHANGE, int32 ranged = EQUIP_NO_CHANGE);
// Used to control if MoveChase() is to be used or not in AttackStart(). Some creatures does not chase victims
// NOTE: If you use SetCombatMovement while the creature is in combat, it will do NOTHING - This only affects AttackStart
// You should make the necessary to make it happen so.
// Remember that if you modified _isCombatMovementAllowed (e.g: using SetCombatMovement) it will not be reset at Reset().
// It will keep the last value you set.
void SetCombatMovement(bool allowMovement);
bool IsCombatMovementAllowed() const { return _isCombatMovementAllowed; }
bool EnterEvadeIfOutOfCombatArea();
virtual bool CheckEvadeIfOutOfCombatArea() const { return false; }
// return true for heroic mode. i.e.
// - for dungeon in mode 10-heroic,
// - for raid in mode 10-Heroic
// - for raid in mode 25-heroic
// DO NOT USE to check raid in mode 25-normal.
bool IsHeroic() const { return _isHeroic; }
// return the dungeon or raid difficulty
Difficulty GetDifficulty() const { return _difficulty; }
// return true for 25 man or 25 man heroic mode
bool Is25ManRaid() const { return _difficulty & RAID_DIFFICULTY_MASK_25MAN; }
template<class T> inline
const T& DUNGEON_MODE(const T& normal5, const T& heroic10) const
{
switch (_difficulty)
{
case DUNGEON_DIFFICULTY_NORMAL:
return normal5;
case DUNGEON_DIFFICULTY_HEROIC:
return heroic10;
default:
break;
}
return heroic10;
}
template<class T> inline
const T& RAID_MODE(const T& normal10, const T& normal25) const
{
switch (_difficulty)
{
case RAID_DIFFICULTY_10MAN_NORMAL:
return normal10;
case RAID_DIFFICULTY_25MAN_NORMAL:
return normal25;
default:
break;
}
return normal25;
}
template<class T> inline
const T& RAID_MODE(const T& normal10, const T& normal25, const T& heroic10, const T& heroic25) const
{
switch (_difficulty)
{
case RAID_DIFFICULTY_10MAN_NORMAL:
return normal10;
case RAID_DIFFICULTY_25MAN_NORMAL:
return normal25;
case RAID_DIFFICULTY_10MAN_HEROIC:
return heroic10;
case RAID_DIFFICULTY_25MAN_HEROIC:
return heroic25;
default:
break;
}
return heroic25;
}
Player* SelectTargetFromPlayerList(float maxdist, uint32 excludeAura = 0, bool mustBeInLOS = false) const;
private:
Difficulty _difficulty;
uint32 _evadeCheckCooldown;
bool _isCombatMovementAllowed;
bool _isHeroic;
};
class BossAI : public ScriptedAI
{
public:
BossAI(Creature* creature, uint32 bossId);
virtual ~BossAI() {}
InstanceScript* const instance;
BossBoundaryMap const* GetBoundary() const { return _boundary; }
void JustSummoned(Creature* summon);
void SummonedCreatureDespawn(Creature* summon);
virtual void UpdateAI(uint32 diff);
// Hook used to execute events scheduled into EventMap without the need
// to override UpdateAI
// note: You must re-schedule the event within this method if the event
// is supposed to run more than once
virtual void ExecuteEvent(uint32 /*eventId*/) { }
void Reset() { _Reset(); }
void EnterCombat(Unit* /*who*/) { _EnterCombat(); }
void JustDied(Unit* /*killer*/) { _JustDied(); }
void JustReachedHome() { _JustReachedHome(); }
protected:
void _Reset();
void _EnterCombat();
void _JustDied();
void _JustReachedHome() { me->setActive(false); }
bool CheckInRoom()
{
if (CheckBoundary(me))
return true;
EnterEvadeMode();
return false;
}
bool CheckBoundary(Unit* who);
void TeleportCheaters();
EventMap events;
SummonList summons;
private:
BossBoundaryMap const* const _boundary;
uint32 const _bossId;
};
class WorldBossAI : public ScriptedAI
{
public:
WorldBossAI(Creature* creature);
virtual ~WorldBossAI() {}
void JustSummoned(Creature* summon);
void SummonedCreatureDespawn(Creature* summon);
virtual void UpdateAI(uint32 diff);
// Hook used to execute events scheduled into EventMap without the need
// to override UpdateAI
// note: You must re-schedule the event within this method if the event
// is supposed to run more than once
virtual void ExecuteEvent(uint32 /*eventId*/) { }
void Reset() { _Reset(); }
void EnterCombat(Unit* /*who*/) { _EnterCombat(); }
void JustDied(Unit* /*killer*/) { _JustDied(); }
protected:
void _Reset();
void _EnterCombat();
void _JustDied();
EventMap events;
SummonList summons;
};
// SD2 grid searchers.
Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool alive = true);
GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange);
void GetCreatureListWithEntryInGrid(std::list<Creature*>& list, WorldObject* source, uint32 entry, float maxSearchRange);
void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& list, WorldObject* source, uint32 entry, float maxSearchRange);
#endif // SCRIPTEDCREATURE_H_

View File

@@ -0,0 +1,584 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
/* ScriptData
SDName: Npc_EscortAI
SD%Complete: 100
SDComment:
SDCategory: Npc
EndScriptData */
#include "ScriptedCreature.h"
#include "ScriptedEscortAI.h"
#include "Group.h"
#include "Player.h"
enum ePoints
{
POINT_LAST_POINT = 0xFFFFFF,
POINT_HOME = 0xFFFFFE
};
npc_escortAI::npc_escortAI(Creature* creature) : ScriptedAI(creature),
m_uiPlayerGUID(0),
m_uiWPWaitTimer(1000),
m_uiPlayerCheckTimer(0),
m_uiEscortState(STATE_ESCORT_NONE),
MaxPlayerDistance(DEFAULT_MAX_PLAYER_DISTANCE),
m_pQuestForEscort(NULL),
m_bIsActiveAttacker(true),
m_bIsRunning(false),
m_bCanInstantRespawn(false),
m_bCanReturnToStart(false),
DespawnAtEnd(true),
DespawnAtFar(true),
ScriptWP(false),
HasImmuneToNPCFlags(false)
{}
void npc_escortAI::AttackStart(Unit* who)
{
if (!who)
return;
if (me->Attack(who, true))
{
MovementGeneratorType type = me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE);
if (type == ESCORT_MOTION_TYPE || type == POINT_MOTION_TYPE)
{
me->GetMotionMaster()->MovementExpired();
//me->DisableSpline();
me->StopMoving();
}
if (IsCombatMovementAllowed())
me->GetMotionMaster()->MoveChase(who);
}
}
//see followerAI
bool npc_escortAI::AssistPlayerInCombat(Unit* who)
{
if (!who || !who->GetVictim())
return false;
//experimental (unknown) flag not present
if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS))
return false;
//not a player
if (!who->GetVictim()->GetCharmerOrOwnerPlayerOrPlayerItself())
return false;
//never attack friendly
if (!me->IsValidAttackTarget(who))
return false;
//too far away and no free sight?
if (me->IsWithinDistInMap(who, GetMaxPlayerDistance()) && me->IsWithinLOSInMap(who))
{
AttackStart(who);
return true;
}
return false;
}
void npc_escortAI::MoveInLineOfSight(Unit* who)
{
if (me->GetVictim())
return;
if (!me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack(true, me) && who->isInAccessiblePlaceFor(me))
if (HasEscortState(STATE_ESCORT_ESCORTING) && AssistPlayerInCombat(who))
return;
if (me->CanStartAttack(who))
AttackStart(who);
}
void npc_escortAI::JustDied(Unit* /*killer*/)
{
if (!HasEscortState(STATE_ESCORT_ESCORTING) || !m_uiPlayerGUID || !m_pQuestForEscort)
return;
if (Player* player = GetPlayerForEscort())
{
if (Group* group = player->GetGroup())
{
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
if (Player* member = groupRef->GetSource())
if (member->IsInMap(player) && member->GetQuestStatus(m_pQuestForEscort->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
member->FailQuest(m_pQuestForEscort->GetQuestId());
}
else
{
if (player->GetQuestStatus(m_pQuestForEscort->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
player->FailQuest(m_pQuestForEscort->GetQuestId());
}
}
}
void npc_escortAI::JustRespawned()
{
RemoveEscortState(STATE_ESCORT_ESCORTING|STATE_ESCORT_RETURNING|STATE_ESCORT_PAUSED);
if (!IsCombatMovementAllowed())
SetCombatMovement(true);
//add a small delay before going to first waypoint, normal in near all cases
m_uiWPWaitTimer = 1000;
if (me->getFaction() != me->GetCreatureTemplate()->faction)
me->RestoreFaction();
Reset();
}
void npc_escortAI::ReturnToLastPoint()
{
float x, y, z, o;
me->SetWalk(false);
me->GetHomePosition(x, y, z, o);
me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z);
}
void npc_escortAI::EnterEvadeMode()
{
me->RemoveAllAuras();
me->DeleteThreatList();
me->CombatStop(true);
me->SetLootRecipient(NULL);
if (HasEscortState(STATE_ESCORT_ESCORTING))
{
AddEscortState(STATE_ESCORT_RETURNING);
ReturnToLastPoint();
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI has left combat and is now returning to last point");
}
else
{
me->GetMotionMaster()->MoveTargetedHome();
if (HasImmuneToNPCFlags)
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
Reset();
}
}
bool npc_escortAI::IsPlayerOrGroupInRange()
{
if (Player* player = GetPlayerForEscort())
{
if (Group* group = player->GetGroup())
{
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
if (Player* member = groupRef->GetSource())
if (me->IsWithinDistInMap(member, GetMaxPlayerDistance()))
return true;
}
else if (me->IsWithinDistInMap(player, GetMaxPlayerDistance()))
return true;
}
return false;
}
void npc_escortAI::UpdateAI(uint32 diff)
{
if (HasEscortState(STATE_ESCORT_ESCORTING) && !me->GetVictim() && m_uiWPWaitTimer && !HasEscortState(STATE_ESCORT_RETURNING))
{
if (m_uiWPWaitTimer <= diff)
{
if (CurrentWP == WaypointList.end())
{
if (DespawnAtEnd)
{
if (m_bCanReturnToStart)
{
float fRetX, fRetY, fRetZ;
me->GetRespawnPosition(fRetX, fRetY, fRetZ);
me->GetMotionMaster()->MovePoint(POINT_HOME, fRetX, fRetY, fRetZ);
m_uiWPWaitTimer = 0;
return;
}
if (m_bCanInstantRespawn)
{
me->setDeathState(JUST_DIED);
me->Respawn();
}
else
me->DespawnOrUnsummon();
}
// xinef: remove escort state, escort was finished (lack of this line resulted in skipping UpdateEscortAI calls after finished escort)
RemoveEscortState(STATE_ESCORT_ESCORTING);
return;
}
if (!HasEscortState(STATE_ESCORT_PAUSED))
{
// xinef, start escort if there is no spline active
if (me->movespline->Finalized())
{
Movement::PointsArray pathPoints;
GenerateWaypointArray(&pathPoints);
me->GetMotionMaster()->MoveSplinePath(&pathPoints);
}
WaypointStart(CurrentWP->id);
m_uiWPWaitTimer = 0;
}
}
else
m_uiWPWaitTimer -= diff;
}
//Check if player or any member of his group is within range
if (HasEscortState(STATE_ESCORT_ESCORTING) && m_uiPlayerGUID && !me->GetVictim() && !HasEscortState(STATE_ESCORT_RETURNING))
{
m_uiPlayerCheckTimer += diff;
if (m_uiPlayerCheckTimer > 1000)
{
if (DespawnAtFar && !IsPlayerOrGroupInRange())
{
if (m_bCanInstantRespawn)
{
me->setDeathState(JUST_DIED);
me->Respawn();
}
else
me->DespawnOrUnsummon();
return;
}
m_uiPlayerCheckTimer = 0;
}
}
UpdateEscortAI(diff);
}
void npc_escortAI::UpdateEscortAI(uint32 /*diff*/)
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
void npc_escortAI::MovementInform(uint32 moveType, uint32 pointId)
{
// xinef: no action allowed if there is no escort
if (!HasEscortState(STATE_ESCORT_ESCORTING))
return;
if (moveType == POINT_MOTION_TYPE)
{
//Combat start position reached, continue waypoint movement
if (pointId == POINT_LAST_POINT)
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI has returned to original position before combat");
me->SetWalk(!m_bIsRunning);
RemoveEscortState(STATE_ESCORT_RETURNING);
if (!m_uiWPWaitTimer)
m_uiWPWaitTimer = 1;
}
else if (pointId == POINT_HOME)
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI has returned to original home location and will continue from beginning of waypoint list.");
CurrentWP = WaypointList.begin();
m_uiWPWaitTimer = 1;
}
}
else if (moveType == ESCORT_MOTION_TYPE)
{
if (m_uiWPWaitTimer <= 1 && !HasEscortState(STATE_ESCORT_PAUSED) && CurrentWP != WaypointList.end())
{
//Call WP function
me->SetPosition(CurrentWP->x, CurrentWP->y, CurrentWP->z, me->GetOrientation());
me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
WaypointReached(CurrentWP->id);
m_uiWPWaitTimer = CurrentWP->WaitTimeMs + 1;
++CurrentWP;
if (m_uiWPWaitTimer > 1 || HasEscortState(STATE_ESCORT_PAUSED))
{
if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == ESCORT_MOTION_TYPE)
me->GetMotionMaster()->MovementExpired();
me->StopMovingOnCurrentPos();
me->GetMotionMaster()->MoveIdle();
}
}
}
}
/*
void npc_escortAI::OnPossess(bool apply)
{
// We got possessed in the middle of being escorted, store the point
// where we left off to come back to when possess is removed
if (HasEscortState(STATE_ESCORT_ESCORTING))
{
if (apply)
me->GetPosition(LastPos.x, LastPos.y, LastPos.z);
else
{
Returning = true;
me->GetMotionMaster()->MovementExpired();
me->GetMotionMaster()->MovePoint(WP_LAST_POINT, LastPos.x, LastPos.y, LastPos.z);
}
}
}
*/
void npc_escortAI::AddWaypoint(uint32 id, float x, float y, float z, uint32 waitTime)
{
Escort_Waypoint t(id, x, y, z, waitTime);
WaypointList.push_back(t);
// i think SD2 no longer uses this function
ScriptWP = true;
/*PointMovement wp;
wp.m_uiCreatureEntry = me->GetEntry();
wp.m_uiPointId = id;
wp.m_fX = x;
wp.m_fY = y;
wp.m_fZ = z;
wp.m_uiWaitTime = WaitTimeMs;
PointMovementMap[wp.m_uiCreatureEntry].push_back(wp);*/
}
void npc_escortAI::FillPointMovementListForCreature()
{
ScriptPointVector const& movePoints = sScriptSystemMgr->GetPointMoveList(me->GetEntry());
if (movePoints.empty())
return;
ScriptPointVector::const_iterator itrEnd = movePoints.end();
for (ScriptPointVector::const_iterator itr = movePoints.begin(); itr != itrEnd; ++itr)
{
Escort_Waypoint point(itr->uiPointId, itr->fX, itr->fY, itr->fZ, itr->uiWaitTime);
WaypointList.push_back(point);
}
}
void npc_escortAI::SetRun(bool on)
{
if (on)
{
if (!m_bIsRunning)
me->SetWalk(false);
else
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI attempt to set run mode, but is already running.");
}
else
{
if (m_bIsRunning)
me->SetWalk(true);
else
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI attempt to set walk mode, but is already walking.");
}
m_bIsRunning = on;
}
//TODO: get rid of this many variables passed in function.
void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false */, uint64 playerGUID /* = 0 */, Quest const* quest /* = NULL */, bool instantRespawn /* = false */, bool canLoopPath /* = false */, bool resetWaypoints /* = true */)
{
if (me->GetVictim())
{
sLog->outError("TSCR ERROR: EscortAI (script: %s, creature entry: %u) attempts to Start while in combat", me->GetScriptName().c_str(), me->GetEntry());
return;
}
if (HasEscortState(STATE_ESCORT_ESCORTING))
{
sLog->outError("TSCR: EscortAI (script: %s, creature entry: %u) attempts to Start while already escorting", me->GetScriptName().c_str(), me->GetEntry());
return;
}
if (!ScriptWP && resetWaypoints) // sd2 never adds wp in script, but tc does
{
if (!WaypointList.empty())
WaypointList.clear();
FillPointMovementListForCreature();
}
if (WaypointList.empty())
{
sLog->outErrorDb("TSCR: EscortAI (script: %s, creature entry: %u) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: %u).",
me->GetScriptName().c_str(), me->GetEntry(), quest ? quest->GetQuestId() : 0);
return;
}
//set variables
m_bIsActiveAttacker = isActiveAttacker;
m_bIsRunning = run;
m_uiPlayerGUID = playerGUID;
m_pQuestForEscort = quest;
m_bCanInstantRespawn = instantRespawn;
m_bCanReturnToStart = canLoopPath;
//if (m_bCanReturnToStart && m_bCanInstantRespawn)
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn.");
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
{
me->GetMotionMaster()->MovementExpired();
me->GetMotionMaster()->MoveIdle();
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI start with WAYPOINT_MOTION_TYPE, changed to MoveIdle.");
}
//disable npcflags
me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC))
{
HasImmuneToNPCFlags = true;
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
}
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI started with " UI64FMTD " waypoints. ActiveAttacker = %d, Run = %d, PlayerGUID = " UI64FMTD "", uint64(WaypointList.size()), m_bIsActiveAttacker, m_bIsRunning, m_uiPlayerGUID);
CurrentWP = WaypointList.begin();
//Set initial speed
if (m_bIsRunning)
me->SetWalk(false);
else
me->SetWalk(true);
AddEscortState(STATE_ESCORT_ESCORTING);
if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == ESCORT_MOTION_TYPE)
me->GetMotionMaster()->MovementExpired();
me->DisableSpline();
me->GetMotionMaster()->MoveIdle();
}
void npc_escortAI::SetEscortPaused(bool on)
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
return;
if (on)
AddEscortState(STATE_ESCORT_PAUSED);
else
RemoveEscortState(STATE_ESCORT_PAUSED);
}
bool npc_escortAI::SetNextWaypoint(uint32 pointId, float x, float y, float z, float orientation)
{
me->UpdatePosition(x, y, z, orientation);
return SetNextWaypoint(pointId, false);
}
bool npc_escortAI::SetNextWaypoint(uint32 pointId, bool setPosition)
{
if (!WaypointList.empty())
WaypointList.clear();
FillPointMovementListForCreature();
if (WaypointList.empty())
return false;
size_t const size = WaypointList.size();
Escort_Waypoint waypoint(0, 0, 0, 0, 0);
for (CurrentWP = WaypointList.begin(); CurrentWP != WaypointList.end(); ++CurrentWP)
{
if (CurrentWP->id == pointId)
{
if (setPosition)
me->UpdatePosition(CurrentWP->x, CurrentWP->y, CurrentWP->z, me->GetOrientation());
return true;
}
}
return false;
}
bool npc_escortAI::GetWaypointPosition(uint32 pointId, float& x, float& y, float& z)
{
ScriptPointVector const& waypoints = sScriptSystemMgr->GetPointMoveList(me->GetEntry());
if (waypoints.empty())
return false;
for (ScriptPointVector::const_iterator itr = waypoints.begin(); itr != waypoints.end(); ++itr)
{
if (itr->uiPointId == pointId)
{
x = itr->fX;
y = itr->fY;
z = itr->fZ;
return true;
}
}
return false;
}
void npc_escortAI::GenerateWaypointArray(Movement::PointsArray* points)
{
if (WaypointList.empty())
return;
uint32 startingWaypointId = CurrentWP->id;
// Flying unit, just fill array
if (me->m_movementInfo.HasMovementFlag((MovementFlags)(MOVEMENTFLAG_CAN_FLY|MOVEMENTFLAG_DISABLE_GRAVITY)))
{
// xinef: first point in vector is unit real position
points->clear();
points->push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()));
for (std::list<Escort_Waypoint>::const_iterator itr = CurrentWP; itr != WaypointList.end(); ++itr)
points->push_back(G3D::Vector3(itr->x, itr->y, itr->z));
}
else
{
for (float size = 1.0f; size; size *= 0.5f)
{
std::vector<G3D::Vector3> pVector;
// xinef: first point in vector is unit real position
pVector.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()));
uint32 length = (WaypointList.size() - startingWaypointId)*size;
uint32 cnt = 0;
for (std::list<Escort_Waypoint>::const_iterator itr = CurrentWP; itr != WaypointList.end() && cnt <= length; ++itr, ++cnt)
pVector.push_back(G3D::Vector3(itr->x, itr->y, itr->z));
if (pVector.size() > 2) // more than source + dest
{
G3D::Vector3 middle = (pVector[0] + pVector[pVector.size()-1]) / 2.f;
G3D::Vector3 offset;
bool continueLoop = false;
for (uint32 i = 1; i < pVector.size()-1; ++i)
{
offset = middle - pVector[i];
if (fabs(offset.x) >= 0xFF || fabs(offset.y) >= 0xFF || fabs(offset.z) >= 0x7F)
{
// offset is too big, split points
continueLoop = true;
break;
}
}
if (continueLoop)
continue;
}
// everything ok
*points = pVector;
break;
}
}
}

View File

@@ -0,0 +1,128 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
#ifndef SC_ESCORTAI_H
#define SC_ESCORTAI_H
#include "ScriptSystem.h"
#define DEFAULT_MAX_PLAYER_DISTANCE 50
struct Escort_Waypoint
{
Escort_Waypoint(uint32 _id, float _x, float _y, float _z, uint32 _w)
{
id = _id;
x = _x;
y = _y;
z = _z;
WaitTimeMs = _w;
}
uint32 id;
float x;
float y;
float z;
uint32 WaitTimeMs;
};
enum eEscortState
{
STATE_ESCORT_NONE = 0x000, //nothing in progress
STATE_ESCORT_ESCORTING = 0x001, //escort are in progress
STATE_ESCORT_RETURNING = 0x002, //escort is returning after being in combat
STATE_ESCORT_PAUSED = 0x004 //will not proceed with waypoints before state is removed
};
struct npc_escortAI : public ScriptedAI
{
public:
explicit npc_escortAI(Creature* creature);
~npc_escortAI() {}
// CreatureAI functions
void AttackStart(Unit* who);
void MoveInLineOfSight(Unit* who);
void JustDied(Unit*);
void JustRespawned();
void ReturnToLastPoint();
void EnterEvadeMode();
void UpdateAI(uint32 diff); //the "internal" update, calls UpdateEscortAI()
virtual void UpdateEscortAI(uint32 diff); //used when it's needed to add code in update (abilities, scripted events, etc)
void MovementInform(uint32, uint32);
// EscortAI functions
void AddWaypoint(uint32 id, float x, float y, float z, uint32 waitTime = 0); // waitTime is in ms
//this will set the current position to x/y/z/o, and the current WP to pointId.
bool SetNextWaypoint(uint32 pointId, float x, float y, float z, float orientation);
//this will set the current position to WP start position (if setPosition == true),
//and the current WP to pointId
bool SetNextWaypoint(uint32 pointId, bool setPosition = true);
bool GetWaypointPosition(uint32 pointId, float& x, float& y, float& z);
void GenerateWaypointArray(Movement::PointsArray* points);
virtual void WaypointReached(uint32 pointId) = 0;
virtual void WaypointStart(uint32 /*pointId*/) {}
void Start(bool isActiveAttacker = true, bool run = false, uint64 playerGUID = 0, Quest const* quest = NULL, bool instantRespawn = false, bool canLoopPath = false, bool resetWaypoints = true);
void SetRun(bool on = true);
void SetEscortPaused(bool on);
bool HasEscortState(uint32 escortState) { return (m_uiEscortState & escortState); }
virtual bool IsEscorted() { return (m_uiEscortState & STATE_ESCORT_ESCORTING); }
void SetMaxPlayerDistance(float newMax) { MaxPlayerDistance = newMax; }
float GetMaxPlayerDistance() { return MaxPlayerDistance; }
void SetDespawnAtEnd(bool despawn) { DespawnAtEnd = despawn; }
void SetDespawnAtFar(bool despawn) { DespawnAtFar = despawn; }
bool GetAttack() { return m_bIsActiveAttacker; }//used in EnterEvadeMode override
void SetCanAttack(bool attack) { m_bIsActiveAttacker = attack; }
uint64 GetEventStarterGUID() { return m_uiPlayerGUID; }
void AddEscortState(uint32 escortState) { m_uiEscortState |= escortState; }
void RemoveEscortState(uint32 escortState) { m_uiEscortState &= ~escortState; }
protected:
Player* GetPlayerForEscort() { return ObjectAccessor::GetPlayer(*me, m_uiPlayerGUID); }
private:
bool AssistPlayerInCombat(Unit* who);
bool IsPlayerOrGroupInRange();
void FillPointMovementListForCreature();
uint64 m_uiPlayerGUID;
uint32 m_uiWPWaitTimer;
uint32 m_uiPlayerCheckTimer;
uint32 m_uiEscortState;
float MaxPlayerDistance;
Quest const* m_pQuestForEscort; //generally passed in Start() when regular escort script.
std::list<Escort_Waypoint> WaypointList;
std::list<Escort_Waypoint>::iterator CurrentWP;
bool m_bIsActiveAttacker; //obsolete, determined by faction.
bool m_bIsRunning; //all creatures are walking by default (has flag MOVEMENTFLAG_WALK)
bool m_bCanInstantRespawn; //if creature should respawn instantly after escort over (if not, database respawntime are used)
bool m_bCanReturnToStart; //if creature can walk same path (loop) without despawn. Not for regular escort quests.
bool DespawnAtEnd;
bool DespawnAtFar;
bool ScriptWP;
bool HasImmuneToNPCFlags;
};
#endif

View File

@@ -0,0 +1,362 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
/* ScriptData
SDName: FollowerAI
SD%Complete: 50
SDComment: This AI is under development
SDCategory: Npc
EndScriptData */
#include "ScriptedCreature.h"
#include "ScriptedFollowerAI.h"
#include "Group.h"
#include "Player.h"
const float MAX_PLAYER_DISTANCE = 100.0f;
enum ePoints
{
POINT_COMBAT_START = 0xFFFFFF
};
FollowerAI::FollowerAI(Creature* creature) : ScriptedAI(creature),
m_uiLeaderGUID(0),
m_uiUpdateFollowTimer(2500),
m_uiFollowState(STATE_FOLLOW_NONE),
m_pQuestForFollow(NULL)
{}
void FollowerAI::AttackStart(Unit* who)
{
if (!who)
return;
if (me->Attack(who, true))
{
// This is done in Unit::Attack function which wont bug npcs by not adding threat upon combat start...
//me->AddThreat(who, 0.0f);
//me->SetInCombatWith(who);
//who->SetInCombatWith(me);
if (me->HasUnitState(UNIT_STATE_FOLLOW))
me->ClearUnitState(UNIT_STATE_FOLLOW);
if (IsCombatMovementAllowed())
me->GetMotionMaster()->MoveChase(who);
}
}
//This part provides assistance to a player that are attacked by who, even if out of normal aggro range
//It will cause me to attack who that are attacking _any_ player (which has been confirmed may happen also on offi)
//The flag (type_flag) is unconfirmed, but used here for further research and is a good candidate.
bool FollowerAI::AssistPlayerInCombat(Unit* who)
{
if (!who || !who->GetVictim())
return false;
//experimental (unknown) flag not present
if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS))
return false;
//not a player
if (!who->GetVictim()->GetCharmerOrOwnerPlayerOrPlayerItself())
return false;
//never attack friendly
if (me->IsFriendlyTo(who))
return false;
//too far away and no free sight?
if (me->IsWithinDistInMap(who, MAX_PLAYER_DISTANCE) && me->IsWithinLOSInMap(who))
{
AttackStart(who);
return true;
}
return false;
}
void FollowerAI::MoveInLineOfSight(Unit* who)
{
if (me->GetVictim())
return;
if (!me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack(true, me) && who->isInAccessiblePlaceFor(me))
if (HasFollowState(STATE_FOLLOW_INPROGRESS) && AssistPlayerInCombat(who))
return;
if (me->CanStartAttack(who))
AttackStart(who);
}
void FollowerAI::JustDied(Unit* /*pKiller*/)
{
if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || !m_uiLeaderGUID || !m_pQuestForFollow)
return;
//TODO: need a better check for quests with time limit.
if (Player* player = GetLeaderForFollower())
{
if (Group* group = player->GetGroup())
{
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
{
if (Player* member = groupRef->GetSource())
{
if (member->IsInMap(player) && member->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
member->FailQuest(m_pQuestForFollow->GetQuestId());
}
}
}
else
{
if (player->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
player->FailQuest(m_pQuestForFollow->GetQuestId());
}
}
}
void FollowerAI::JustRespawned()
{
m_uiFollowState = STATE_FOLLOW_NONE;
if (!IsCombatMovementAllowed())
SetCombatMovement(true);
if (me->getFaction() != me->GetCreatureTemplate()->faction)
me->setFaction(me->GetCreatureTemplate()->faction);
Reset();
}
void FollowerAI::EnterEvadeMode()
{
me->RemoveAllAuras();
me->DeleteThreatList();
me->CombatStop(true);
me->SetLootRecipient(NULL);
if (HasFollowState(STATE_FOLLOW_INPROGRESS))
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI left combat, returning to CombatStartPosition.");
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
{
float fPosX, fPosY, fPosZ;
me->GetPosition(fPosX, fPosY, fPosZ);
me->GetMotionMaster()->MovePoint(POINT_COMBAT_START, fPosX, fPosY, fPosZ);
}
}
else
{
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
me->GetMotionMaster()->MoveTargetedHome();
}
Reset();
}
void FollowerAI::UpdateAI(uint32 uiDiff)
{
if (HasFollowState(STATE_FOLLOW_INPROGRESS) && !me->GetVictim())
{
if (m_uiUpdateFollowTimer <= uiDiff)
{
if (HasFollowState(STATE_FOLLOW_COMPLETE) && !HasFollowState(STATE_FOLLOW_POSTEVENT))
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI is set completed, despawns.");
me->DespawnOrUnsummon();
return;
}
bool bIsMaxRangeExceeded = true;
if (Player* player = GetLeaderForFollower())
{
if (HasFollowState(STATE_FOLLOW_RETURNING))
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI is returning to leader.");
RemoveFollowState(STATE_FOLLOW_RETURNING);
me->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
return;
}
if (Group* group = player->GetGroup())
{
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
{
Player* member = groupRef->GetSource();
if (member && me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE))
{
bIsMaxRangeExceeded = false;
break;
}
}
}
else
{
if (me->IsWithinDistInMap(player, MAX_PLAYER_DISTANCE))
bIsMaxRangeExceeded = false;
}
}
if (bIsMaxRangeExceeded)
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI failed because player/group was to far away or not found");
me->DespawnOrUnsummon();
return;
}
m_uiUpdateFollowTimer = 1000;
}
else
m_uiUpdateFollowTimer -= uiDiff;
}
UpdateFollowerAI(uiDiff);
}
void FollowerAI::UpdateFollowerAI(uint32 /*uiDiff*/)
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
void FollowerAI::MovementInform(uint32 motionType, uint32 pointId)
{
if (motionType != POINT_MOTION_TYPE || !HasFollowState(STATE_FOLLOW_INPROGRESS))
return;
if (pointId == POINT_COMBAT_START)
{
if (GetLeaderForFollower())
{
if (!HasFollowState(STATE_FOLLOW_PAUSED))
AddFollowState(STATE_FOLLOW_RETURNING);
}
else
me->DespawnOrUnsummon();
}
}
void FollowerAI::StartFollow(Player* player, uint32 factionForFollower, const Quest* quest)
{
if (me->GetVictim())
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI attempt to StartFollow while in combat.");
return;
}
if (HasFollowState(STATE_FOLLOW_INPROGRESS))
{
sLog->outError("TSCR: FollowerAI attempt to StartFollow while already following.");
return;
}
//set variables
m_uiLeaderGUID = player->GetGUID();
if (factionForFollower)
me->setFaction(factionForFollower);
m_pQuestForFollow = quest;
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
{
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI start with WAYPOINT_MOTION_TYPE, set to MoveIdle.");
}
me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
AddFollowState(STATE_FOLLOW_INPROGRESS);
me->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI start follow %s (GUID " UI64FMTD ")", player->GetName().c_str(), m_uiLeaderGUID);
}
Player* FollowerAI::GetLeaderForFollower()
{
if (Player* player = ObjectAccessor::GetPlayer(*me, m_uiLeaderGUID))
{
if (player->IsAlive())
return player;
else
{
if (Group* group = player->GetGroup())
{
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
{
Player* member = groupRef->GetSource();
if (member && me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE) && member->IsAlive())
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI GetLeader changed and returned new leader.");
m_uiLeaderGUID = member->GetGUID();
return member;
}
}
}
}
}
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI GetLeader can not find suitable leader.");
return NULL;
}
void FollowerAI::SetFollowComplete(bool bWithEndEvent)
{
if (me->HasUnitState(UNIT_STATE_FOLLOW))
{
me->ClearUnitState(UNIT_STATE_FOLLOW);
me->StopMoving();
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
}
if (bWithEndEvent)
AddFollowState(STATE_FOLLOW_POSTEVENT);
else
{
if (HasFollowState(STATE_FOLLOW_POSTEVENT))
RemoveFollowState(STATE_FOLLOW_POSTEVENT);
}
AddFollowState(STATE_FOLLOW_COMPLETE);
}
void FollowerAI::SetFollowPaused(bool paused)
{
if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || HasFollowState(STATE_FOLLOW_COMPLETE))
return;
if (paused)
{
AddFollowState(STATE_FOLLOW_PAUSED);
if (me->HasUnitState(UNIT_STATE_FOLLOW))
{
me->ClearUnitState(UNIT_STATE_FOLLOW);
me->StopMoving();
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
}
}
else
{
RemoveFollowState(STATE_FOLLOW_PAUSED);
if (Player* leader = GetLeaderForFollower())
me->GetMotionMaster()->MoveFollow(leader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
}
}

View File

@@ -0,0 +1,67 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
#ifndef SC_FOLLOWERAI_H
#define SC_FOLLOWERAI_H
#include "ScriptSystem.h"
enum eFollowState
{
STATE_FOLLOW_NONE = 0x000,
STATE_FOLLOW_INPROGRESS = 0x001, //must always have this state for any follow
STATE_FOLLOW_RETURNING = 0x002, //when returning to combat start after being in combat
STATE_FOLLOW_PAUSED = 0x004, //disables following
STATE_FOLLOW_COMPLETE = 0x008, //follow is completed and may end
STATE_FOLLOW_PREEVENT = 0x010, //not implemented (allow pre event to run, before follow is initiated)
STATE_FOLLOW_POSTEVENT = 0x020 //can be set at complete and allow post event to run
};
class FollowerAI : public ScriptedAI
{
public:
explicit FollowerAI(Creature* creature);
~FollowerAI() {}
//virtual void WaypointReached(uint32 uiPointId) = 0;
void MovementInform(uint32 motionType, uint32 pointId);
void AttackStart(Unit*);
void MoveInLineOfSight(Unit*);
void EnterEvadeMode();
void JustDied(Unit*);
void JustRespawned();
void UpdateAI(uint32); //the "internal" update, calls UpdateFollowerAI()
virtual void UpdateFollowerAI(uint32); //used when it's needed to add code in update (abilities, scripted events, etc)
void StartFollow(Player* player, uint32 factionForFollower = 0, const Quest* quest = NULL);
void SetFollowPaused(bool bPaused); //if special event require follow mode to hold/resume during the follow
void SetFollowComplete(bool bWithEndEvent = false);
bool HasFollowState(uint32 uiFollowState) { return (m_uiFollowState & uiFollowState); }
protected:
Player* GetLeaderForFollower();
private:
void AddFollowState(uint32 uiFollowState) { m_uiFollowState |= uiFollowState; }
void RemoveFollowState(uint32 uiFollowState) { m_uiFollowState &= ~uiFollowState; }
bool AssistPlayerInCombat(Unit* who);
uint64 m_uiLeaderGUID;
uint32 m_uiUpdateFollowTimer;
uint32 m_uiFollowState;
const Quest* m_pQuestForFollow; //normally we have a quest
};
#endif

View File

@@ -0,0 +1,90 @@
/* Copyright (C)
*
*
*
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
#ifndef SC_GOSSIP_H
#define SC_GOSSIP_H
#include "GossipDef.h"
#include "QuestDef.h"
// Gossip Item Text
#define GOSSIP_TEXT_BROWSE_GOODS "I'd like to browse your goods."
#define GOSSIP_TEXT_TRAIN "Train me!"
enum eTradeskill
{
// Skill defines
TRADESKILL_ALCHEMY = 1,
TRADESKILL_BLACKSMITHING = 2,
TRADESKILL_COOKING = 3,
TRADESKILL_ENCHANTING = 4,
TRADESKILL_ENGINEERING = 5,
TRADESKILL_FIRSTAID = 6,
TRADESKILL_HERBALISM = 7,
TRADESKILL_LEATHERWORKING = 8,
TRADESKILL_POISONS = 9,
TRADESKILL_TAILORING = 10,
TRADESKILL_MINING = 11,
TRADESKILL_FISHING = 12,
TRADESKILL_SKINNING = 13,
TRADESKILL_JEWLCRAFTING = 14,
TRADESKILL_INSCRIPTION = 15,
TRADESKILL_LEVEL_NONE = 0,
TRADESKILL_LEVEL_APPRENTICE = 1,
TRADESKILL_LEVEL_JOURNEYMAN = 2,
TRADESKILL_LEVEL_EXPERT = 3,
TRADESKILL_LEVEL_ARTISAN = 4,
TRADESKILL_LEVEL_MASTER = 5,
TRADESKILL_LEVEL_GRAND_MASTER = 6,
// Gossip defines
GOSSIP_ACTION_TRADE = 1,
GOSSIP_ACTION_TRAIN = 2,
GOSSIP_ACTION_TAXI = 3,
GOSSIP_ACTION_GUILD = 4,
GOSSIP_ACTION_BATTLE = 5,
GOSSIP_ACTION_BANK = 6,
GOSSIP_ACTION_INN = 7,
GOSSIP_ACTION_HEAL = 8,
GOSSIP_ACTION_TABARD = 9,
GOSSIP_ACTION_AUCTION = 10,
GOSSIP_ACTION_INN_INFO = 11,
GOSSIP_ACTION_UNLEARN = 12,
GOSSIP_ACTION_INFO_DEF = 1000,
GOSSIP_SENDER_MAIN = 1,
GOSSIP_SENDER_INN_INFO = 2,
GOSSIP_SENDER_INFO = 3,
GOSSIP_SENDER_SEC_PROFTRAIN = 4,
GOSSIP_SENDER_SEC_CLASSTRAIN = 5,
GOSSIP_SENDER_SEC_BATTLEINFO = 6,
GOSSIP_SENDER_SEC_BANK = 7,
GOSSIP_SENDER_SEC_INN = 8,
GOSSIP_SENDER_SEC_MAILBOX = 9,
GOSSIP_SENDER_SEC_STABLEMASTER = 10
};
// Defined fuctions to use with player.
// This fuction add's a menu item,
// a - Icon Id
// b - Text
// c - Sender(this is to identify the current Menu with this item)
// d - Action (identifys this Menu Item)
// e - Text to be displayed in pop up box
// f - Money value in pop up box
#define ADD_GOSSIP_ITEM(a, b, c, d) PlayerTalkClass->GetGossipMenu().AddMenuItem(-1, a, b, c, d, "", 0)
#define ADD_GOSSIP_ITEM_EXTENDED(a, b, c, d, e, f, g) PlayerTalkClass->GetGossipMenu().AddMenuItem(-1, a, b, c, d, e, f, g)
// This fuction Sends the current menu to show to client, a - NPCTEXTID(uint32), b - npc guid(uint64)
#define SEND_GOSSIP_MENU(a, b) PlayerTalkClass->SendGossipMenu(a, b)
// Closes the Menu
#define CLOSE_GOSSIP_MENU() PlayerTalkClass->SendCloseGossip()
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,274 @@
/*
* 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/>.
*/
#ifndef TRINITY_SMARTAI_H
#define TRINITY_SMARTAI_H
#include "Common.h"
#include "Creature.h"
#include "CreatureAI.h"
#include "Unit.h"
#include "Spell.h"
#include "SmartScript.h"
#include "SmartScriptMgr.h"
#include "GameObjectAI.h"
enum SmartEscortState
{
SMART_ESCORT_NONE = 0x000, //nothing in progress
SMART_ESCORT_ESCORTING = 0x001, //escort is in progress
SMART_ESCORT_RETURNING = 0x002, //escort is returning after being in combat
SMART_ESCORT_PAUSED = 0x004 //will not proceed with waypoints before state is removed
};
enum SmartEscortVars
{
SMART_ESCORT_MAX_PLAYER_DIST = 60,
SMART_MAX_AID_DIST = SMART_ESCORT_MAX_PLAYER_DIST / 2,
};
class SmartAI : public CreatureAI
{
public:
~SmartAI(){};
explicit SmartAI(Creature* c);
// Start moving to the desired MovePoint
void StartPath(bool run = false, uint32 path = 0, bool repeat = false, Unit* invoker = NULL);
bool LoadPath(uint32 entry);
void PausePath(uint32 delay, bool forced = false);
void StopPath(uint32 DespawnTime = 0, uint32 quest = 0, bool fail = false);
void EndPath(bool fail = false);
void ResumePath();
WayPoint* GetNextWayPoint();
void GenerateWayPointArray(Movement::PointsArray* points);
bool HasEscortState(uint32 uiEscortState) { return (mEscortState & uiEscortState); }
void AddEscortState(uint32 uiEscortState) { mEscortState |= uiEscortState; }
virtual bool IsEscorted() { return (mEscortState & SMART_ESCORT_ESCORTING); }
void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; }
void SetAutoAttack(bool on) { mCanAutoAttack = on; }
void SetCombatMove(bool on);
bool CanCombatMove() { return mCanCombatMove; }
void SetFollow(Unit* target, float dist = 0.0f, float angle = 0.0f, uint32 credit = 0, uint32 end = 0, uint32 creditType = 0, bool aliveState = true);
void StopFollow(bool complete);
void SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker);
SmartScript* GetScript() { return &mScript; }
bool IsEscortInvokerInRange();
// Called when creature is spawned or respawned
void JustRespawned();
// Called at reaching home after evade, InitializeAI(), EnterEvadeMode() for resetting variables
void JustReachedHome();
// Called for reaction at enter to combat if not in combat yet (enemy can be NULL)
void EnterCombat(Unit* enemy);
// Called for reaction at stopping attack at no attackers or targets
void EnterEvadeMode();
// Called when the creature is killed
void JustDied(Unit* killer);
// Called when the creature kills a unit
void KilledUnit(Unit* victim);
// Called when the creature summon successfully other creature
void JustSummoned(Creature* creature);
// Tell creature to attack and follow the victim
void AttackStart(Unit* who);
// Called if IsVisible(Unit* who) is true at each *who move, reaction at visibility zone enter
void MoveInLineOfSight(Unit* who);
// Called when hit by a spell
void SpellHit(Unit* unit, const SpellInfo* spellInfo);
// Called when spell hits a target
void SpellHitTarget(Unit* target, const SpellInfo* spellInfo);
// Called at any Damage from any attacker (before damage apply)
void DamageTaken(Unit* done_by, uint32 &damage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask);
// Called when the creature receives heal
void HealReceived(Unit* doneBy, uint32& addhealth);
// Called at World update tick
void UpdateAI(uint32 diff);
// Called at text emote receive from player
void ReceiveEmote(Player* player, uint32 textEmote);
// Called at waypoint reached or point movement finished
void MovementInform(uint32 MovementType, uint32 Data);
// Called when creature is summoned by another unit
void IsSummonedBy(Unit* summoner);
// Called at any Damage to any victim (before damage apply)
void DamageDealt(Unit* doneTo, uint32& damage, DamageEffectType damagetyp);
// Called when a summoned creature dissapears (UnSommoned)
void SummonedCreatureDespawn(Creature* unit);
// called when the corpse of this creature gets removed
void CorpseRemoved(uint32& respawnDelay);
// Called at World update tick if creature is charmed
void UpdateAIWhileCharmed(const uint32 diff);
// Called when a Player/Creature enters the creature (vehicle)
void PassengerBoarded(Unit* who, int8 seatId, bool apply);
// Called when gets initialized, when creature is added to world
void InitializeAI();
// Called when creature gets charmed by another unit
void OnCharmed(bool apply);
// Called when victim is in line of sight
bool CanAIAttack(const Unit* who) const;
// Used in scripts to share variables
void DoAction(int32 param = 0);
// Used in scripts to share variables
uint32 GetData(uint32 id = 0) const;
// Used in scripts to share variables
void SetData(uint32 id, uint32 value);
// Used in scripts to share variables
void SetGUID(uint64 guid, int32 id = 0);
// Used in scripts to share variables
uint64 GetGUID(int32 id = 0) const;
//core related
static int32 Permissible(const Creature*);
// Called at movepoint reached
void MovepointReached(uint32 id);
// Makes the creature run/walk
void SetRun(bool run = true);
void SetFly(bool fly = true);
void SetSwim(bool swim = true);
void SetInvincibilityHpLevel(uint32 level) { mInvincibilityHpLevel = level; }
void sGossipHello(Player* player);
void sGossipSelect(Player* player, uint32 sender, uint32 action);
void sGossipSelectCode(Player* player, uint32 sender, uint32 action, const char* code);
void sQuestAccept(Player* player, Quest const* quest);
//void sQuestSelect(Player* player, Quest const* quest);
//void sQuestComplete(Player* player, Quest const* quest);
void sQuestReward(Player* player, Quest const* quest, uint32 opt);
void sOnGameEvent(bool start, uint16 eventId);
uint32 mEscortQuestID;
void SetDespawnTime (uint32 t)
{
mDespawnTime = t;
mDespawnState = t ? 1 : 0;
}
void StartDespawn() { mDespawnState = 2; }
void OnSpellClick(Unit* clicker, bool& result);
// Xinef
void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; }
void SetForcedCombatMove(float dist);
private:
uint32 mFollowCreditType;
uint32 mFollowArrivedTimer;
uint32 mFollowCredit;
uint32 mFollowArrivedEntry;
bool mFollowArrivedAlive;
uint64 mFollowGuid;
float mFollowDist;
float mFollowAngle;
void ReturnToLastOOCPos();
void UpdatePath(const uint32 diff);
SmartScript mScript;
WPPath* mWayPoints;
uint32 mEscortState;
uint32 mCurrentWPID;
bool mWPReached;
bool mOOCReached;
uint32 mWPPauseTimer;
WayPoint* mLastWP;
uint32 mEscortNPCFlags;
uint32 GetWPCount() { return mWayPoints ? mWayPoints->size() : 0; }
bool mCanRepeatPath;
bool mRun;
bool mCanAutoAttack;
bool mCanCombatMove;
bool mForcedPaused;
uint32 mInvincibilityHpLevel;
bool AssistPlayerInCombat(Unit* who);
uint32 mDespawnTime;
uint32 mDespawnState;
void UpdateDespawn(const uint32 diff);
uint32 mEscortInvokerCheckTimer;
bool mJustReset;
// Xinef: Vehicle conditions
void CheckConditions(const uint32 diff);
ConditionList conditions;
uint32 m_ConditionsTimer;
};
class SmartGameObjectAI : public GameObjectAI
{
public:
SmartGameObjectAI(GameObject* g) : GameObjectAI(g) {}
~SmartGameObjectAI() {}
void UpdateAI(uint32 diff);
void InitializeAI();
void Reset();
SmartScript* GetScript() { return &mScript; }
static int32 Permissible(const GameObject* g);
bool GossipHello(Player* player, bool reportUse);
bool GossipSelect(Player* player, uint32 sender, uint32 action);
bool GossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, const char* /*code*/);
bool QuestAccept(Player* player, Quest const* quest);
bool QuestReward(Player* player, Quest const* quest, uint32 opt);
void Destroyed(Player* player, uint32 eventId);
void SetData(uint32 id, uint32 value);
void SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker);
void OnGameEvent(bool start, uint16 eventId);
void OnStateChanged(uint32 state, Unit* unit);
void EventInform(uint32 eventId);
void SpellHit(Unit* unit, const SpellInfo* spellInfo);
protected:
SmartScript mScript;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,343 @@
/*
* 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/>.
*/
#ifndef TRINITY_SMARTSCRIPT_H
#define TRINITY_SMARTSCRIPT_H
#include "Common.h"
#include "Creature.h"
#include "CreatureAI.h"
#include "Unit.h"
#include "Spell.h"
#include "GridNotifiers.h"
#include "SmartScriptMgr.h"
//#include "SmartAI.h"
class SmartScript
{
public:
SmartScript();
~SmartScript();
void OnInitialize(WorldObject* obj, AreaTriggerEntry const* at = NULL);
void GetScript();
void FillScript(SmartAIEventList e, WorldObject* obj, AreaTriggerEntry const* at);
void ProcessEventsFor(SMART_EVENT e, Unit* unit = NULL, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, const SpellInfo* spell = NULL, GameObject* gob = NULL);
void ProcessEvent(SmartScriptHolder& e, Unit* unit = NULL, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, const SpellInfo* spell = NULL, GameObject* gob = NULL);
bool CheckTimer(SmartScriptHolder const& e) const;
void RecalcTimer(SmartScriptHolder& e, uint32 min, uint32 max);
void UpdateTimer(SmartScriptHolder& e, uint32 const diff);
void InitTimer(SmartScriptHolder& e);
void ProcessAction(SmartScriptHolder& e, Unit* unit = NULL, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, const SpellInfo* spell = NULL, GameObject* gob = NULL);
void ProcessTimedAction(SmartScriptHolder& e, uint32 const& min, uint32 const& max, Unit* unit = NULL, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, const SpellInfo* spell = NULL, GameObject* gob = NULL);
ObjectList* GetTargets(SmartScriptHolder const& e, Unit* invoker = NULL);
ObjectList* GetWorldObjectsInDist(float dist);
void InstallTemplate(SmartScriptHolder const& e);
SmartScriptHolder CreateEvent(SMART_EVENT e, uint32 event_flags, uint32 event_param1, uint32 event_param2, uint32 event_param3, uint32 event_param4, SMART_ACTION action, uint32 action_param1, uint32 action_param2, uint32 action_param3, uint32 action_param4, uint32 action_param5, uint32 action_param6, SMARTAI_TARGETS t, uint32 target_param1, uint32 target_param2, uint32 target_param3, uint32 phaseMask = 0);
void AddEvent(SMART_EVENT e, uint32 event_flags, uint32 event_param1, uint32 event_param2, uint32 event_param3, uint32 event_param4, SMART_ACTION action, uint32 action_param1, uint32 action_param2, uint32 action_param3, uint32 action_param4, uint32 action_param5, uint32 action_param6, SMARTAI_TARGETS t, uint32 target_param1, uint32 target_param2, uint32 target_param3, uint32 phaseMask = 0);
void SetPathId(uint32 id) { mPathId = id; }
uint32 GetPathId() const { return mPathId; }
WorldObject* GetBaseObject()
{
WorldObject* obj = NULL;
if (me)
obj = me;
else if (go)
obj = go;
return obj;
}
bool IsUnit(WorldObject* obj)
{
return obj && obj->IsInWorld() && (obj->GetTypeId() == TYPEID_UNIT || obj->GetTypeId() == TYPEID_PLAYER);
}
bool IsPlayer(WorldObject* obj)
{
return obj && obj->IsInWorld() && obj->GetTypeId() == TYPEID_PLAYER;
}
bool IsCreature(WorldObject* obj)
{
return obj && obj->IsInWorld() && obj->GetTypeId() == TYPEID_UNIT;
}
bool IsGameObject(WorldObject* obj)
{
return obj && obj->IsInWorld() && obj->GetTypeId() == TYPEID_GAMEOBJECT;
}
void OnUpdate(const uint32 diff);
void OnMoveInLineOfSight(Unit* who);
Unit* DoSelectLowestHpFriendly(float range, uint32 MinHPDiff);
void DoFindFriendlyCC(std::list<Creature*>& _list, float range);
void DoFindFriendlyMissingBuff(std::list<Creature*>& list, float range, uint32 spellid);
Unit* DoFindClosestFriendlyInRange(float range, bool playerOnly);
void StoreTargetList(ObjectList* targets, uint32 id)
{
if (!targets)
return;
if (mTargetStorage->find(id) != mTargetStorage->end())
{
// check if already stored
if ((*mTargetStorage)[id]->Equals(targets))
return;
delete (*mTargetStorage)[id];
}
(*mTargetStorage)[id] = new ObjectGuidList(targets, GetBaseObject());
}
bool IsSmart(Creature* c = NULL)
{
bool smart = true;
if (c && c->GetAIName() != "SmartAI")
smart = false;
if (!me || me->GetAIName() != "SmartAI")
smart = false;
if (!smart)
sLog->outErrorDb("SmartScript: Action target Creature(entry: %u) is not using SmartAI, action skipped to prevent crash.", c ? c->GetEntry() : (me ? me->GetEntry() : 0));
return smart;
}
bool IsSmartGO(GameObject* g = NULL)
{
bool smart = true;
if (g && g->GetAIName() != "SmartGameObjectAI")
smart = false;
if (!go || go->GetAIName() != "SmartGameObjectAI")
smart = false;
if (!smart)
sLog->outErrorDb("SmartScript: Action target GameObject(entry: %u) is not using SmartGameObjectAI, action skipped to prevent crash.", g ? g->GetEntry() : (go ? go->GetEntry() : 0));
return smart;
}
ObjectList* GetTargetList(uint32 id)
{
ObjectListMap::iterator itr = mTargetStorage->find(id);
if (itr != mTargetStorage->end())
return (*itr).second->GetObjectList();
return NULL;
}
void StoreCounter(uint32 id, uint32 value, uint32 reset)
{
CounterMap::iterator itr = mCounterList.find(id);
if (itr != mCounterList.end())
{
if (reset == 0)
itr->second += value;
else
itr->second = value;
}
else
mCounterList.insert(std::make_pair(id, value));
ProcessEventsFor(SMART_EVENT_COUNTER_SET, NULL, id);
}
uint32 GetCounterValue(uint32 id)
{
CounterMap::iterator itr = mCounterList.find(id);
if (itr != mCounterList.end())
return itr->second;
return 0;
}
GameObject* FindGameObjectNear(WorldObject* searchObject, uint32 guid) const
{
GameObject* gameObject = NULL;
CellCoord p(Trinity::ComputeCellCoord(searchObject->GetPositionX(), searchObject->GetPositionY()));
Cell cell(p);
Trinity::GameObjectWithDbGUIDCheck goCheck(*searchObject, guid);
Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck> checker(searchObject, gameObject, goCheck);
TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > objectChecker(checker);
cell.Visit(p, objectChecker, *searchObject->GetMap(), *searchObject, searchObject->GetVisibilityRange());
return gameObject;
}
Creature* FindCreatureNear(WorldObject* searchObject, uint32 guid) const
{
Creature* creature = NULL;
CellCoord p(Trinity::ComputeCellCoord(searchObject->GetPositionX(), searchObject->GetPositionY()));
Cell cell(p);
Trinity::CreatureWithDbGUIDCheck target_check(searchObject, guid);
Trinity::CreatureSearcher<Trinity::CreatureWithDbGUIDCheck> checker(searchObject, creature, target_check);
TypeContainerVisitor<Trinity::CreatureSearcher <Trinity::CreatureWithDbGUIDCheck>, GridTypeMapContainer > unit_checker(checker);
cell.Visit(p, unit_checker, *searchObject->GetMap(), *searchObject, searchObject->GetVisibilityRange());
return creature;
}
ObjectListMap* mTargetStorage;
void OnReset();
void ResetBaseObject()
{
if (meOrigGUID)
{
if (Creature* m = HashMapHolder<Creature>::Find(meOrigGUID))
{
me = m;
go = NULL;
}
}
if (goOrigGUID)
{
if (GameObject* o = HashMapHolder<GameObject>::Find(goOrigGUID))
{
me = NULL;
go = o;
}
}
goOrigGUID = 0;
meOrigGUID = 0;
}
//TIMED_ACTIONLIST (script type 9 aka script9)
void SetScript9(SmartScriptHolder& e, uint32 entry);
Unit* GetLastInvoker(Unit* invoker = NULL);
uint64 mLastInvoker;
typedef UNORDERED_MAP<uint32, uint32> CounterMap;
CounterMap mCounterList;
// Xinef: Fix Combat Movement
void SetActualCombatDist(uint32 dist) { mActualCombatDist = dist; }
void RestoreMaxCombatDist() { mActualCombatDist = mMaxCombatDist; }
uint32 GetActualCombatDist() const { return mActualCombatDist; }
uint32 GetMaxCombatDist() const { return mMaxCombatDist; }
// Xinef: SmartCasterAI, replace above
void SetCasterActualDist(float dist) { smartCasterActualDist = dist; }
void RestoreCasterMaxDist() { smartCasterActualDist = smartCasterMaxDist; }
Powers GetCasterPowerType() const { return smartCasterPowerType; }
float GetCasterActualDist() const { return smartCasterActualDist; }
float GetCasterMaxDist() const { return smartCasterMaxDist; }
bool AllowPhaseReset() const { return _allowPhaseReset; }
void SetPhaseReset(bool allow) { _allowPhaseReset = allow; }
private:
void IncPhase(uint32 p)
{
// Xinef: protect phase from overflowing
mEventPhase = std::min<uint32>(SMART_EVENT_PHASE_12, mEventPhase + p);
}
void DecPhase(uint32 p)
{
if (p >= mEventPhase)
mEventPhase = 0;
else
mEventPhase -= p;
}
bool IsInPhase(uint32 p) const
{
if (mEventPhase == 0)
return false;
return (1 << (mEventPhase - 1)) & p;
}
void SetPhase(uint32 p = 0) { mEventPhase = p; }
SmartAIEventList mEvents;
SmartAIEventList mInstallEvents;
SmartAIEventList mTimedActionList;
bool isProcessingTimedActionList;
Creature* me;
uint64 meOrigGUID;
GameObject* go;
uint64 goOrigGUID;
AreaTriggerEntry const* trigger;
SmartScriptType mScriptType;
uint32 mEventPhase;
UNORDERED_MAP<int32, int32> mStoredDecimals;
uint32 mPathId;
SmartAIEventStoredList mStoredEvents;
std::list<uint32> mRemIDs;
uint32 mTextTimer;
uint32 mLastTextID;
uint32 mTalkerEntry;
bool mUseTextTimer;
// Xinef: Fix Combat Movement
uint32 mActualCombatDist;
uint32 mMaxCombatDist;
// Xinef: SmartCasterAI, replace above in future
uint32 smartCasterActualDist;
uint32 smartCasterMaxDist;
Powers smartCasterPowerType;
// Xinef: misc
bool _allowPhaseReset;
SMARTAI_TEMPLATE mTemplate;
void InstallEvents();
void RemoveStoredEvent (uint32 id)
{
if (!mStoredEvents.empty())
{
for (SmartAIEventStoredList::iterator i = mStoredEvents.begin(); i != mStoredEvents.end(); ++i)
{
if (i->event_id == id)
{
mStoredEvents.erase(i);
return;
}
}
}
}
SmartScriptHolder FindLinkedEvent (uint32 link)
{
if (!mEvents.empty())
{
for (SmartAIEventList::iterator i = mEvents.begin(); i != mEvents.end(); ++i)
{
if (i->event_id == link)
{
return (*i);
}
}
}
SmartScriptHolder s;
return s;
}
};
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff