aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bindings/scripts/scripts/zone/vault_of_archavon/boss_emalon.cpp285
-rw-r--r--src/bindings/scripts/scripts/zone/vault_of_archavon/def_vault_of_archavon.h13
-rw-r--r--src/bindings/scripts/scripts/zone/vault_of_archavon/instance_vault_of_archavon.cpp105
-rw-r--r--src/game/Creature.cpp30
-rw-r--r--src/game/Creature.h2
-rw-r--r--src/game/Item.cpp3
-rw-r--r--src/game/Player.cpp2
-rw-r--r--src/game/TemporarySummon.cpp2
-rw-r--r--src/game/ThreatManager.cpp2
-rw-r--r--src/game/Unit.cpp5
-rw-r--r--src/game/World.cpp2
-rw-r--r--src/game/World.h1
-rw-r--r--src/trinitycore/CMakeLists.txt2
-rw-r--r--src/trinitycore/trinitycore.conf.dist5
14 files changed, 227 insertions, 232 deletions
diff --git a/src/bindings/scripts/scripts/zone/vault_of_archavon/boss_emalon.cpp b/src/bindings/scripts/scripts/zone/vault_of_archavon/boss_emalon.cpp
index 7738b790abf..ba999ca766c 100644
--- a/src/bindings/scripts/scripts/zone/vault_of_archavon/boss_emalon.cpp
+++ b/src/bindings/scripts/scripts/zone/vault_of_archavon/boss_emalon.cpp
@@ -1,108 +1,93 @@
#include "precompiled.h"
#include "def_vault_of_archavon.h"
-#define EMOTE_OVERCHARGE_TEMPEST_MINION -1590000
-#define EMOTE_MINION_RESPAWN -1590001
-
-//Spells Emalon
-#define SPELL_CHAIN_LIGHTNING HEROIC(64213,64215)
-#define SPELL_LIGHTNING_NOVA HEROIC(64216,65279)
-#define SPELL_OVERCHARGE 64218
-#define SPELL_BERSERK 47008
-
-//Spells Tempest Minions
-#define SPELL_OVERCHARGED 64217
-#define SPELL_OVERCHARGED_BLAST 64219
-#define SPELL_SHOCK 64363
-
-//4 Warders spawned
-#define TEMPEST_MINIONS 33998
-
-#define EVENT_CHAIN_LIGHTNING 1 //5s cd
-#define EVENT_OVERCHARGE 2 //45s cd
-#define EVENT_LIGHTNING_NOVA 3 //40s cd
-#define EVENT_BERSERK 4 //360s cd
-#define EVENT_SHOCK 5 //20s cd
-
-struct Location
+//Emalon spells
+#define SPELL_CHAIN_LIGHTNING HEROIC(64213, 64215)
+#define SPELL_LIGHTNING_NOVA HEROIC(64216, 65279)
+#define SPELL_OVERCHARGE 64218 //Casted every 45 sec on a random Tempest Minion
+#define SPELL_BERSERK 26662
+
+//Tempest Minion spells
+#define SPELL_SHOCK 64363
+#define SPELL_OVERCHARGED 64217
+#define SPELL_OVERCHARGED_BLAST 64219 //Casted when Overcharged reaches 10 stacks. Mob dies after that
+
+//Emotes
+#define EMOTE_OVERCHARGE -1590000
+#define EMOTE_MINION_RESPAWN -1590001
+#define EMOTE_BERSERK -1590002
+
+//Events
+#define EVENT_CHAIN_LIGHTNING 1
+#define EVENT_LIGHTNING_NOVA 2
+#define EVENT_OVERCHARGE 3
+#define EVENT_BERSERK 4
+#define EVENT_SHOCK 5
+
+//Creatures
+#define MOB_TEMPEST_MINION 33998
+
+float TempestMinions[4][5] =
{
- float x, y, z, o;
-};
-
-static Location MinionLocation[]=
-{
- -231.713,-281.96,91.466,1.53213,
- -205.585,-281.549,91.4661,1.75204,
- -205.651,-296.394,91.4661,1.69549,
- -232.235,-296.433,91.4998,1.44417
+ {33998, -203.980103, -281.287720, 91.650223, 1.598807},
+ {33998, -233.489410, -281.139282, 91.652412, 1.598807},
+ {33998, -233.267578, -297.104645, 91.681915, 1.598807},
+ {33998, -203.842529, -297.097015, 91.745163, 1.598807}
};
+/*######
+## Emalon the Storm Watcher
+######*/
struct TRINITY_DLL_DECL boss_emalonAI : public ScriptedAI
{
- boss_emalonAI(Creature *c) : ScriptedAI(c)
+ boss_emalonAI(Creature *c) : ScriptedAI(c), summons(m_creature)
{
pInstance = c->GetInstanceData();
}
ScriptedInstance* pInstance;
+
EventMap events;
std::list<uint64> MinionList;
+ SummonList summons;
void Reset()
- {
+ {
events.Reset();
- DespawnAllMinions();
- SummonAllMinions();
-
- if (pInstance)
- pInstance->SetData(DATA_EMALON_EVENT, NOT_STARTED);
- }
+ summons.DespawnAll();
+ MinionList.clear();
- void DespawnAllMinions()
- {
- if (!MinionList.empty())
+ Creature* Minion;
+ for (uint32 i = 0; i < 4; ++i)
{
- for(std::list<uint64>::const_iterator itr = MinionList.begin(); itr != MinionList.end(); itr++)
- {
- if (Creature *Minion = Unit::GetCreature(*m_creature, (*itr)))
- Minion->DisappearAndDie();
- }
+ Minion = m_creature->SummonCreature(((uint32)TempestMinions[i][0]),TempestMinions[i][1],TempestMinions[i][2],TempestMinions[i][3],TempestMinions[i][4],TEMPSUMMON_DEAD_DESPAWN, 0);
+ MinionList.push_back(Minion->GetGUID());
+ if(Unit* target = m_creature->getVictim())
+ Minion->AI()->AttackStart(target);
}
-
- MinionList.clear();
- }
- void JustSummoned(Creature *Summoned)
- {
- MinionList.push_back(Summoned->GetGUID());
- if (Unit* target = m_creature->getVictim())
- Summoned->AI()->AttackStart(target);
+ if (pInstance)
+ pInstance->SetData(DATA_EMALON_EVENT, NOT_STARTED);
}
- void SummonAllMinions()
+ void JustSummoned(Creature* summoned)
{
- if (MinionList.empty())
- for(uint8 i = 0; i < 4; ++i)
- SummonMinion(m_creature, MinionLocation[i].x, MinionLocation[i].y, MinionLocation[i].z, MinionLocation[i].o);
+ summons.Summon(summoned);
}
- static void SummonMinion(Creature *Summoner, float x, float y, float z, float o)
- {
- Summoner->SummonCreature(TEMPEST_MINIONS, x, y, z, o, TEMPSUMMON_DEAD_DESPAWN, 0);
- }
-
- void KilledUnit(Unit* Victim){}
+ void SummonedCreatureDespawn(Creature *summon) {summons.Despawn(summon);}
void JustDied(Unit* Killer)
{
- DespawnAllMinions();
+ summons.DespawnAll();
+
if (pInstance)
pInstance->SetData(DATA_EMALON_EVENT, DONE);
}
void EnterCombat(Unit *who)
{
- if (!MinionList.empty())
+ if(!MinionList.empty())
{
for(std::list<uint64>::const_iterator itr = MinionList.begin(); itr != MinionList.end(); ++itr)
{
@@ -113,69 +98,51 @@ struct TRINITY_DLL_DECL boss_emalonAI : public ScriptedAI
DoZoneInCombat();
events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 5000);
- events.ScheduleEvent(EVENT_OVERCHARGE, 45000);
- events.ScheduleEvent(EVENT_LIGHTNING_NOVA, 30000);
+ events.ScheduleEvent(EVENT_LIGHTNING_NOVA, 40000);
events.ScheduleEvent(EVENT_BERSERK, 360000);
- events.ScheduleEvent(EVENT_SHOCK, 20000);
+ events.ScheduleEvent(EVENT_OVERCHARGE, 45000);
if (pInstance)
- pInstance->SetData(DATA_EMALON_EVENT, IN_PROGRESS);
+ pInstance->SetData(DATA_EMALON_EVENT, IN_PROGRESS);
}
void UpdateAI(const uint32 diff)
{
//Return since we have no target
- if (!UpdateVictim())
+ if(!UpdateVictim())
return;
events.Update(diff);
- if (me->hasUnitState(UNIT_STAT_CASTING))
- return;
+ if(me->hasUnitState(UNIT_STAT_CASTING))
+ return;
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
{
- case EVENT_CHAIN_LIGHTNING:
- if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
- DoCast(target, SPELL_CHAIN_LIGHTNING);
- events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 20000);
- return;
- case EVENT_OVERCHARGE:
- for(std::list<uint64>::const_iterator itr = MinionList.begin(); itr != MinionList.end(); ++itr)
- {
- Creature* Minion = (Unit::GetCreature(*m_creature, *itr));
- if (Minion && Minion->isAlive() && !Minion->HasAura(SPELL_OVERCHARGED))
- {
- Minion->CastSpell(me, SPELL_OVERCHARGED, true);
- Minion->SetHealth(Minion->GetMaxHealth());
- DoScriptText(EMOTE_OVERCHARGE_TEMPEST_MINION, m_creature);
- events.ScheduleEvent(EVENT_OVERCHARGE, 45000);
- return;
- }
- }
- case EVENT_LIGHTNING_NOVA:
- DoCast(me->getVictim(), SPELL_LIGHTNING_NOVA);
- events.ScheduleEvent(EVENT_LIGHTNING_NOVA, 25000);
- return;
- case EVENT_SHOCK:
- for(std::list<uint64>::const_iterator itr = MinionList.begin(); itr != MinionList.end(); ++itr)
- {
- Creature* Minion = (Unit::GetCreature(*m_creature, *itr));
- if (Minion && Minion->isAlive())
- {
- if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
- {
- Minion->CastSpell(target, SPELL_SHOCK, true);
- events.ScheduleEvent(EVENT_SHOCK, 20000);
- return;
- }
- }
- }
- case EVENT_BERSERK:
- DoCast(m_creature, SPELL_BERSERK);
- return;
+ case EVENT_CHAIN_LIGHTNING:
+ if(Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
+ DoCast(target, SPELL_CHAIN_LIGHTNING);
+ events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 25000);
+ return;
+ case EVENT_LIGHTNING_NOVA:
+ DoCastAOE(SPELL_LIGHTNING_NOVA, false);
+ events.ScheduleEvent(EVENT_LIGHTNING_NOVA, 40000);
+ return;
+ case EVENT_OVERCHARGE:
+ if(Creature* Minion = GetClosestCreatureWithEntry(me, MOB_TEMPEST_MINION, 1000.0f))
+ {
+ Minion->CastSpell(me, SPELL_OVERCHARGED, true);
+ Minion->SetHealth(Minion->GetMaxHealth());
+ DoScriptText(EMOTE_OVERCHARGE, m_creature);
+ }
+ events.ScheduleEvent(EVENT_OVERCHARGE, 45000);
+ return;
+ case EVENT_BERSERK:
+ DoCast(m_creature, SPELL_BERSERK);
+ DoScriptText(EMOTE_BERSERK, m_creature);
+ return;
}
}
@@ -184,11 +151,11 @@ struct TRINITY_DLL_DECL boss_emalonAI : public ScriptedAI
};
/*######
-## Mob Tempest Minions
+## Tempest Minion
######*/
struct TRINITY_DLL_DECL mob_tempest_minionAI : public ScriptedAI
{
- mob_tempest_minionAI(Creature *c) : ScriptedAI(c)
+ mob_tempest_minionAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
EmalonGUID = pInstance ? pInstance->GetData64(DATA_EMALON) : 0;
@@ -196,74 +163,100 @@ struct TRINITY_DLL_DECL mob_tempest_minionAI : public ScriptedAI
}
ScriptedInstance* pInstance;
+
EventMap events;
+
uint64 EmalonGUID;
- std::list<Creature*> MinionList;
Creature* Emalon;
- uint32 SPELL_OVERCHARGED_Timer;
- bool AlreadyOvercharged;
+
+ uint32 OverchargedTimer;
void Reset()
{
events.Reset();
- AlreadyOvercharged = false;
- SPELL_OVERCHARGED_Timer = 0;
+
+ OverchargedTimer = 0;
}
- void EnterCombat(Unit *who)
+ void JustDied(Unit* Killer)
{
- DoZoneInCombat();
- if (Emalon)
- Emalon->AI()->AttackStart(who);
+ Creature* Emalon;
+ Emalon = Unit::GetCreature(*m_creature, EmalonGUID);
+ float x,y,z;
+ Emalon->GetPosition(x,y,z);
+ Emalon->SummonCreature(MOB_TEMPEST_MINION,x,y,z,m_creature->GetOrientation(),TEMPSUMMON_DEAD_DESPAWN,0);
+ DoScriptText(EMOTE_MINION_RESPAWN, m_creature);
}
- void JustDied(Unit* Killer)
+ void EnterCombat(Unit *who)
{
- m_creature->DisappearAndDie();
- if (Emalon)
- {
- boss_emalonAI::SummonMinion(Emalon, Emalon->GetPositionX(), Emalon->GetPositionY(), Emalon->GetPositionZ(), Emalon->GetOrientation());
- DoScriptText(EMOTE_MINION_RESPAWN, m_creature);
- }
+ DoZoneInCombat();
+ events.ScheduleEvent(EVENT_SHOCK, 20000);
+
+ if(Emalon)
+ Emalon->AI()->AttackStart(who);
}
void UpdateAI(const uint32 diff)
{
//Return since we have no target
- if (!UpdateVictim())
- return;
+ if(!UpdateVictim())
+ return;
+
+ events.Update(diff);
- if (Aura *OverchargedAura = m_creature->GetAura(SPELL_OVERCHARGED))
+ if(me->hasUnitState(UNIT_STAT_CASTING))
+ return;
+
+ if(Aura *OverchargedAura = m_creature->GetAura(SPELL_OVERCHARGED))
{
- if (OverchargedAura->GetStackAmount() < 10)
+ if(OverchargedAura->GetStackAmount() < 10)
{
- if (SPELL_OVERCHARGED_Timer < diff)
+ if(OverchargedTimer < diff)
{
DoCast(me, SPELL_OVERCHARGED);
- SPELL_OVERCHARGED_Timer = 2000;
- }else SPELL_OVERCHARGED_Timer -=diff;
+ OverchargedTimer = 2000;
+ }else OverchargedTimer -=diff;
}
else
{
- if (OverchargedAura->GetStackAmount() == 10 && (AlreadyOvercharged == false))
+ if(OverchargedAura->GetStackAmount() == 10)
{
DoCast(me,SPELL_OVERCHARGED_BLAST);
- AlreadyOvercharged = true;
+ m_creature->setDeathState(JUST_DIED);
+ Creature* Emalon;
+ Emalon = Unit::GetCreature(*m_creature, EmalonGUID);
+ float x,y,z;
+ Emalon->GetPosition(x,y,z);
+ Emalon->SummonCreature(MOB_TEMPEST_MINION,x,y,z,m_creature->GetOrientation(),TEMPSUMMON_DEAD_DESPAWN,0);
+ DoScriptText(EMOTE_MINION_RESPAWN, m_creature);
}
}
}
+
+ while(uint32 eventId = events.ExecuteEvent())
+ {
+ switch(eventId)
+ {
+ case EVENT_SHOCK:
+ DoCast(me->getVictim(), SPELL_SHOCK);
+ events.ScheduleEvent(EVENT_SHOCK, 20000);
+ return;
+ }
+ }
+
DoMeleeAttackIfReady();
}
};
-CreatureAI* GetAI_mob_tempest_minion(Creature* pCreature)
+CreatureAI* GetAI_mob_tempest_minion(Creature *_Creature)
{
- return new mob_tempest_minionAI (pCreature);
+ return new mob_tempest_minionAI (_Creature);
}
-CreatureAI* GetAI_boss_emalon(Creature* pCreature)
+CreatureAI* GetAI_boss_emalon(Creature *_Creature)
{
- return new boss_emalonAI (pCreature);
+ return new boss_emalonAI (_Creature);
}
void AddSC_boss_emalon()
diff --git a/src/bindings/scripts/scripts/zone/vault_of_archavon/def_vault_of_archavon.h b/src/bindings/scripts/scripts/zone/vault_of_archavon/def_vault_of_archavon.h
index 5b11d5729eb..0ebadb214d8 100644
--- a/src/bindings/scripts/scripts/zone/vault_of_archavon/def_vault_of_archavon.h
+++ b/src/bindings/scripts/scripts/zone/vault_of_archavon/def_vault_of_archavon.h
@@ -1,13 +1,8 @@
#ifndef DEF_ARCHAVON_H
#define DEF_ARCHAVON_H
-enum
-{
- DATA_ARCHAVON = 1,
- DATA_EMALON = 2,
-
- DATA_ARCHAVON_EVENT = 3,
- DATA_EMALON_EVENT = 4
-};
-
+#define DATA_ARCHAVON_EVENT 1
+#define DATA_EMALON_EVENT 2
+#define DATA_EMALON 3
+#define DATA_ARCHAVON 4
#endif
diff --git a/src/bindings/scripts/scripts/zone/vault_of_archavon/instance_vault_of_archavon.cpp b/src/bindings/scripts/scripts/zone/vault_of_archavon/instance_vault_of_archavon.cpp
index 1db6e7b0c10..4b5c8f371af 100644
--- a/src/bindings/scripts/scripts/zone/vault_of_archavon/instance_vault_of_archavon.cpp
+++ b/src/bindings/scripts/scripts/zone/vault_of_archavon/instance_vault_of_archavon.cpp
@@ -1,116 +1,117 @@
#include "precompiled.h"
#include "def_vault_of_archavon.h"
-#define NUMBER_OF_ENCOUNTERS 2
+#define ENCOUNTERS 2
+
+/* Vault of Archavon encounters:
+1 - Archavon the Stone Watcher event
+2 - Emalon the Storm Watcher event
+*/
struct TRINITY_DLL_DECL instance_archavon : public ScriptedInstance
{
- instance_archavon(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+ instance_archavon(Map *Map) : ScriptedInstance(Map) {Initialize();};
+
+ uint32 Encounters[ENCOUNTERS];
- std::string strInstData;
uint64 Archavon;
uint64 Emalon;
- uint32 m_auiEncounter[NUMBER_OF_ENCOUNTERS];
void Initialize()
- {
- memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
-
+ {
Archavon = 0;
Emalon = 0;
+
+ for(uint8 i = 0; i < ENCOUNTERS; i++)
+ Encounters[i] = NOT_STARTED;
}
bool IsEncounterInProgress() const
{
- for(uint8 i = 0; i < NUMBER_OF_ENCOUNTERS; ++i)
- if (m_auiEncounter[i] == IN_PROGRESS)
- return true;
+ for(uint8 i = 0; i < ENCOUNTERS; i++)
+ if(Encounters[i] == IN_PROGRESS) return true;
return false;
}
- void OnCreatureCreate(Creature* pCreature, bool add)
+ void OnCreatureCreate(Creature *creature, bool add)
{
- switch(pCreature->GetEntry())
+ switch(creature->GetEntry())
{
- case 31125: Archavon = pCreature->GetGUID(); break;
- case 33993: Emalon = pCreature->GetGUID(); break;
+ case 31125: Archavon = creature->GetGUID(); break;
+ case 33993: Emalon = creature->GetGUID(); break;
}
}
- uint64 GetData64(uint32 identifier)
+ uint32 GetData(uint32 type)
{
- switch(identifier)
+ switch(type)
{
- case DATA_ARCHAVON: return Archavon;
- case DATA_EMALON: return Emalon;
+ case DATA_ARCHAVON_EVENT: return Encounters[0];
+ case DATA_EMALON_EVENT: return Encounters[1];
}
return 0;
}
- uint32 GetData(uint32 identifier)
+ uint64 GetData64(uint32 identifier)
{
switch(identifier)
{
- case DATA_ARCHAVON_EVENT: return m_auiEncounter[0];
- case DATA_EMALON_EVENT: return m_auiEncounter[1];
+ case DATA_ARCHAVON: return Archavon;
+ case DATA_EMALON: return Emalon;
}
return 0;
}
- void SetData(uint32 identifier, uint32 data)
+ void SetData(uint32 type, uint32 data)
{
- switch(identifier)
+ switch(type)
{
- case DATA_ARCHAVON_EVENT: m_auiEncounter[0] = data; break;
- case DATA_EMALON_EVENT: m_auiEncounter[1] = data; break;
+ case DATA_ARCHAVON_EVENT: Encounters[0] = data; break;
+ case DATA_EMALON_EVENT: Encounters[1] = data; break;
}
- if (data == DONE)
- {
- OUT_SAVE_INST_DATA;
-
- std::ostringstream saveStream;
- saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1];
-
- strInstData = saveStream.str();
-
+ if(data == DONE)
SaveToDB();
- OUT_SAVE_INST_DATA_COMPLETE;
- }
}
std::string GetSaveData()
{
- return strInstData;
+ OUT_SAVE_INST_DATA;
+ std::ostringstream stream;
+ stream << Encounters[0] << " " << Encounters[1];
+ char* out = new char[stream.str().length() + 1];
+ strcpy(out, stream.str().c_str());
+ if(out)
+ {
+ OUT_SAVE_INST_DATA_COMPLETE;
+ return out;
+ }
+
+ return NULL;
}
- void Load(const char* chrIn)
+ void Load(const char* in)
{
- if (!chrIn)
+ if(!in)
{
OUT_LOAD_INST_DATA_FAIL;
return;
}
- OUT_LOAD_INST_DATA(chrIn);
-
- std::istringstream loadStream(chrIn);
- loadStream >> m_auiEncounter[0] >> m_auiEncounter[1];
-
- for(uint8 i = 1; i < NUMBER_OF_ENCOUNTERS; ++i)
- {
- if (m_auiEncounter[i] == IN_PROGRESS)
- m_auiEncounter[i] = NOT_STARTED;
- }
-
+ OUT_LOAD_INST_DATA(in);
+ std::istringstream stream(in);
+ stream >> Encounters[0] >> Encounters[1];
+ for(uint8 i = 0; i < ENCOUNTERS; ++i)
+ if(Encounters[i] == IN_PROGRESS)
+ Encounters[i] = NOT_STARTED;
OUT_LOAD_INST_DATA_COMPLETE;
}
};
-InstanceData* GetInstanceData_instance_archavon(Map* pMap)
+InstanceData* GetInstanceData_instance_archavon(Map* map)
{
- return new instance_archavon(pMap);
+ return new instance_archavon(map);
}
void AddSC_instance_archavon()
diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp
index 19eae315e9c..1fc31e8a93a 100644
--- a/src/game/Creature.cpp
+++ b/src/game/Creature.cpp
@@ -923,7 +923,7 @@ void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid )
cantalking=false;
break;
case GOSSIP_OPTION_LEARNDUALSPEC:
- if(!(pPlayer->GetSpecsCount() == 1 && isCanTrainingAndResetTalentsOf(pPlayer) && !(pPlayer->getLevel() < 40)))
+ if(!(pPlayer->GetSpecsCount() == 1 && isCanTrainingAndResetTalentsOf(pPlayer) && !(pPlayer->getLevel() < sWorld.getConfig(CONFIG_MIN_DUALSPEC_LEVEL))))
cantalking=false;
break;
case GOSSIP_OPTION_UNLEARNTALENTS:
@@ -1074,7 +1074,7 @@ void Creature::OnGossipSelect(Player* player, uint32 option)
player->SendTalentWipeConfirm(guid);
break;
case GOSSIP_OPTION_LEARNDUALSPEC:
- if(player->GetSpecsCount() == 1 && !(player->getLevel() < 40))
+ if(player->GetSpecsCount() == 1 && !(player->getLevel() < sWorld.getConfig(CONFIG_MIN_DUALSPEC_LEVEL)))
{
if (player->GetMoney() < 10000000)
{
@@ -1704,7 +1704,7 @@ bool Creature::IsWithinSightDist(Unit const* u) const
bool Creature::canStartAttack(Unit const* who, bool force) const
{
- if(isCivilian() || !who->isInAccessiblePlaceFor(this))
+ if(isCivilian())
return false;
if(!canFly() && (GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE))
@@ -1716,7 +1716,7 @@ bool Creature::canStartAttack(Unit const* who, bool force) const
if(!force && (IsNeutralToAll() || !IsWithinDistInMap(who, GetAttackDistance(who))))
return false;
- if(!canAttack(who, force))
+ if(!canCreatureAttack(who, force))
return false;
return IsWithinLOSInMap(who);
@@ -2180,29 +2180,29 @@ void Creature::SaveRespawnTime()
objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),time(NULL)+m_respawnDelay+m_deathTimer/IN_MILISECONDS);
}
-bool Creature::IsOutOfThreatArea(Unit* pVictim) const
+// this should not be called by petAI or
+bool Creature::canCreatureAttack(Unit const *pVictim, bool force) const
{
- if(!pVictim)
- return true;
-
if(!pVictim->IsInMap(this))
- return true;
+ return false;
- if(!canAttack(pVictim))
- return true;
+ if(!canAttack(pVictim, force))
+ return false;
if(!pVictim->isInAccessiblePlaceFor(this))
- return true;
+ return false;
if(sMapStore.LookupEntry(GetMapId())->IsDungeon())
- return false;
+ return true;
float AttackDist = GetAttackDistance(pVictim);
uint32 ThreatRadius = sWorld.getConfig(CONFIG_THREAT_RADIUS);
//Use AttackDistance in distance check if threat radius is lower. This prevents creature bounce in and out of combat every update tick.
- return !pVictim->IsWithinDist3d(mHome_X,mHome_Y,mHome_Z,
- ThreatRadius > AttackDist ? ThreatRadius : AttackDist);
+ if(Unit *unit = GetCharmerOrOwner())
+ return pVictim->IsWithinDist(unit, ThreatRadius > AttackDist ? ThreatRadius : AttackDist);
+ else
+ return pVictim->IsWithinDist3d(mHome_X, mHome_Y, mHome_Z, ThreatRadius > AttackDist ? ThreatRadius : AttackDist);
}
CreatureDataAddon const* Creature::GetCreatureAddon() const
diff --git a/src/game/Creature.h b/src/game/Creature.h
index cec6a123579..fdab5157bd4 100644
--- a/src/game/Creature.h
+++ b/src/game/Creature.h
@@ -527,7 +527,7 @@ class TRINITY_DLL_SPEC Creature : public Unit
bool isCanTrainingOf(Player* player, bool msg) const;
bool isCanInteractWithBattleMaster(Player* player, bool msg) const;
bool isCanTrainingAndResetTalentsOf(Player* pPlayer) const;
- bool IsOutOfThreatArea(Unit* pVictim) const;
+ bool canCreatureAttack(Unit const *pVictim, bool force = true) const;
bool IsImmunedToSpell(SpellEntry const* spellInfo);
// redefine Unit::IsImmunedToSpell
bool IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const;
diff --git a/src/game/Item.cpp b/src/game/Item.cpp
index 484fa766da0..dac3ca8b0c8 100644
--- a/src/game/Item.cpp
+++ b/src/game/Item.cpp
@@ -286,7 +286,8 @@ void Item::UpdateDuration(Player* owner, uint32 diff)
if (GetUInt32Value(ITEM_FIELD_DURATION)<=diff)
{
owner->DestroyItem(GetBagSlot(), GetSlot(), true);
- Script->ItemExpire(owner, GetProto());
+ if(const ItemPrototype *proto = GetProto())
+ Script->ItemExpire(owner, proto);
return;
}
diff --git a/src/game/Player.cpp b/src/game/Player.cpp
index 8fc7a170f70..96a48225d56 100644
--- a/src/game/Player.cpp
+++ b/src/game/Player.cpp
@@ -2547,7 +2547,7 @@ void Player::InitTalentForLevel()
}
else
{
- if (level < 40 || m_specsCount == 0)
+ if (level < sWorld.getConfig(CONFIG_MIN_DUALSPEC_LEVEL) || m_specsCount == 0)
{
m_specsCount = 1;
m_activeSpec = 0;
diff --git a/src/game/TemporarySummon.cpp b/src/game/TemporarySummon.cpp
index 40c6d79c919..434750c8744 100644
--- a/src/game/TemporarySummon.cpp
+++ b/src/game/TemporarySummon.cpp
@@ -325,7 +325,7 @@ void Guardian::InitStats(uint32 duration)
if(m_owner->GetTypeId() == TYPEID_PLAYER && HasSummonMask(SUMMON_MASK_CONTROLABLE_GUARDIAN))
m_charmInfo->InitCharmCreateSpells();
- SetReactState(REACT_DEFENSIVE);
+ SetReactState(REACT_AGGRESSIVE);
}
Puppet::Puppet(SummonPropertiesEntry const *properties, Unit *owner) : Minion(properties, owner)
diff --git a/src/game/ThreatManager.cpp b/src/game/ThreatManager.cpp
index 60c46c2307d..5db2a99440d 100644
--- a/src/game/ThreatManager.cpp
+++ b/src/game/ThreatManager.cpp
@@ -306,7 +306,7 @@ HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilRe
}
}
- if(!pAttacker->IsOutOfThreatArea(target)) // skip non attackable currently targets
+ if(pAttacker->canCreatureAttack(target)) // skip non attackable currently targets
{
if(pCurrentVictim) // select 1.3/1.1 better target in comparison current target
{
diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp
index da55ce24576..567a3628c9c 100644
--- a/src/game/Unit.cpp
+++ b/src/game/Unit.cpp
@@ -11187,11 +11187,8 @@ Unit* Creature::SelectVictim()
// search nearby enemy before enter evade mode
if(HasReactState(REACT_AGGRESSIVE))
- {
- target = SelectNearestTarget();
- if(target && !IsOutOfThreatArea(target))
+ if(target = SelectNearestTarget())
return target;
- }
if(m_invisibilityMask)
{
diff --git a/src/game/World.cpp b/src/game/World.cpp
index e09aab24e44..a5e6a66f491 100644
--- a/src/game/World.cpp
+++ b/src/game/World.cpp
@@ -729,6 +729,8 @@ void World::LoadConfigSettings(bool reload)
m_configs[CONFIG_MAX_PLAYER_LEVEL] = MAX_LEVEL;
}
+ m_configs[CONFIG_MIN_DUALSPEC_LEVEL] = sConfig.GetIntDefault("MinDualSpecLevel", 40);
+
m_configs[CONFIG_START_PLAYER_LEVEL] = sConfig.GetIntDefault("StartPlayerLevel", 1);
if(m_configs[CONFIG_START_PLAYER_LEVEL] < 1)
{
diff --git a/src/game/World.h b/src/game/World.h
index 9d1813f9752..bfc2947201a 100644
--- a/src/game/World.h
+++ b/src/game/World.h
@@ -124,6 +124,7 @@ enum WorldConfigs
CONFIG_MIN_LEVEL_FOR_HEROIC_CHARACTER_CREATING,
CONFIG_SKIP_CINEMATICS,
CONFIG_MAX_PLAYER_LEVEL,
+ CONFIG_MIN_DUALSPEC_LEVEL,
CONFIG_START_PLAYER_LEVEL,
CONFIG_START_HEROIC_PLAYER_LEVEL,
CONFIG_START_PLAYER_MONEY,
diff --git a/src/trinitycore/CMakeLists.txt b/src/trinitycore/CMakeLists.txt
index ffd8e6431c4..b8a51158665 100644
--- a/src/trinitycore/CMakeLists.txt
+++ b/src/trinitycore/CMakeLists.txt
@@ -47,7 +47,7 @@ trinityconfig
vmaps
ZThread
g3dlite
-readline
+${READLINE_LIBRARY}
gomp
${SCRIPT_LIB}
${MYSQL_LIBRARIES}
diff --git a/src/trinitycore/trinitycore.conf.dist b/src/trinitycore/trinitycore.conf.dist
index 242f1f52933..f8aa1eccb63 100644
--- a/src/trinitycore/trinitycore.conf.dist
+++ b/src/trinitycore/trinitycore.conf.dist
@@ -593,6 +593,10 @@ ChatLogTimestamp = 0
# Change not recommended
# Default: 80
#
+# MinDualSpecLevel
+# Min level at which players can use Dual Spec functionality
+# Default: 40
+#
# StartPlayerLevel
# Staring level that have character at creating (in range 1 to MaxPlayerLevel)
# Default: 1
@@ -755,6 +759,7 @@ HeroicCharactersPerRealm = 1
MinLevelForHeroicCharacterCreating = 55
SkipCinematics = 0
MaxPlayerLevel = 80
+MinDualSpecLevel = 40
StartPlayerLevel = 1
StartHeroicPlayerLevel = 55
StartPlayerMoney = 0