*Add a new class for event based scripts. Two examples are given.

--HG--
branch : trunk
This commit is contained in:
megamage
2009-04-22 23:03:49 -05:00
parent 92d8abea95
commit f5fff5b186
5 changed files with 253 additions and 217 deletions

View File

@@ -16,52 +16,49 @@ struct TSpellSummary {
uint8 Effects; // set of enum SelectEffect
} *SpellSummary;
void SummonList::Despawn(Creature *summon)
void SummonList::DoAction(uint32 entry, uint32 info)
{
uint64 guid = summon->GetGUID();
for(iterator i = begin(); i != end(); ++i)
for(iterator i = begin(); i != end();)
{
if(*i == guid)
{
erase(i);
return;
}
Creature *summon = Unit::GetCreature(*m_creature, *i);
++i;
if(summon && summon->IsAIEnabled)
summon->AI()->DoAction(info);
}
}
void SummonList::DespawnEntry(uint32 entry)
{
for(iterator i = begin(); i != end(); ++i)
for(iterator i = begin(); i != end();)
{
if(Creature *summon = Unit::GetCreature(*m_creature, *i))
Creature *summon = Unit::GetCreature(*m_creature, *i);
if(!summon)
erase(i++);
else if(summon->GetEntry() == entry)
{
if(summon->GetEntry() == entry)
{
summon->setDeathState(JUST_DIED);
summon->RemoveCorpse();
i = erase(i);
--i;
}
erase(i++);
summon->setDeathState(JUST_DIED);
summon->RemoveCorpse();
}
else
{
i = erase(i);
--i;
}
++i;
}
}
void SummonList::DespawnAll()
{
for(iterator i = begin(); i != end(); ++i)
while(!empty())
{
if(Creature *summon = Unit::GetCreature(*m_creature, *i))
Creature *summon = Unit::GetCreature(*m_creature, *begin());
if(!summon)
erase(begin());
else
{
erase(begin());
summon->setDeathState(JUST_DIED);
summon->RemoveCorpse();
}
}
clear();
}
void ScriptedAI::AttackStart(Unit* who, bool melee)

View File

@@ -13,14 +13,15 @@
float GetSpellMaxRangeForHostile(uint32 id);
class SummonList : std::list<uint64>
class SummonList : private std::list<uint64>
{
public:
SummonList(Creature* creature) : m_creature(creature) {}
void Summon(Creature *summon) {push_back(summon->GetGUID());}
void Despawn(Creature *summon);
explicit SummonList(Creature* creature) : m_creature(creature) {}
void Summon(Creature *summon) { push_back(summon->GetGUID()); }
void Despawn(Creature *summon) { remove(summon->GetGUID()); }
void DespawnEntry(uint32 entry);
void DespawnAll();
void DoAction(uint32 entry, uint32 info);
private:
Creature *m_creature;
};

View File

@@ -39,6 +39,18 @@ EndScriptData */
#define CREATURE_VOLCANO 23085
#define CREATURE_STALKER 23095
#define PHASE_STRIKE 1
#define PHASE_CHASE 2
#define EVENT_BERSERK 1
#define EVENT_SWITCH_PHASE 2
#define EVENT_FLAME 3
#define EVENT_VOLCANO 4
#define EVENT_SWITCH_TARGET 5
#define EVENT_HATEFUL_STRIKE 6
#define GCD_CAST 1
struct TRINITY_DLL_DECL molten_flameAI : public NullCreatureAI
{
molten_flameAI(Creature *c) : NullCreatureAI(c)
@@ -57,17 +69,9 @@ struct TRINITY_DLL_DECL boss_supremusAI : public ScriptedAI
}
ScriptedInstance* pInstance;
uint32 SummonFlameTimer;
uint32 SwitchTargetTimer;
uint32 PhaseSwitchTimer;
uint32 SummonVolcanoTimer;
uint32 HatefulStrikeTimer;
uint32 BerserkTimer;
bool Phase1;
EventMap events;
SummonList summons;
uint32 phase;
void Reset()
{
@@ -81,26 +85,20 @@ struct TRINITY_DLL_DECL boss_supremusAI : public ScriptedAI
//else ToggleDoors(false);
}
HatefulStrikeTimer = 5000;
SummonFlameTimer = 20000;
SwitchTargetTimer = 90000;
PhaseSwitchTimer = 60000;
SummonVolcanoTimer = 5000;
BerserkTimer = 900000; // 15 minute enrage
phase = 0;
Phase1 = true;
events.Reset();
summons.DespawnAll();
m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false);
m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, false);
}
void Aggro(Unit *who)
{
DoZoneInCombat();
if(pInstance)
pInstance->SetData(DATA_SUPREMUSEVENT, IN_PROGRESS);
ChangePhase();
events.ScheduleEvent(900000, EVENT_BERSERK, GCD_CAST);
events.ScheduleEvent(20000, EVENT_FLAME, GCD_CAST);
}
void ToggleDoors(bool close)
@@ -112,6 +110,32 @@ struct TRINITY_DLL_DECL boss_supremusAI : public ScriptedAI
}
}
void ChangePhase()
{
if(!phase || phase == PHASE_CHASE)
{
phase = PHASE_STRIKE;
summons.DoAction(EVENT_VOLCANO, 0);
events.ScheduleEvent(5000, EVENT_HATEFUL_STRIKE, GCD_CAST, PHASE_STRIKE);
m_creature->SetSpeed(MOVE_RUN, 1.2f);
m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false);
m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, false);
}
else
{
phase = PHASE_CHASE;
events.ScheduleEvent(5000, EVENT_VOLCANO, GCD_CAST, PHASE_CHASE);
events.ScheduleEvent(10000, EVENT_SWITCH_TARGET, 0, PHASE_CHASE);
m_creature->SetSpeed(MOVE_RUN, 0.9f);
m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, true);
}
DoResetThreat();
DoZoneInCombat();
events.SetPhase(phase);
events.ScheduleEvent(60000, EVENT_SWITCH_PHASE, GCD_CAST);
}
void JustDied(Unit *killer)
{
if(pInstance)
@@ -153,83 +177,53 @@ struct TRINITY_DLL_DECL boss_supremusAI : public ScriptedAI
if (!UpdateVictim())
return;
if(!m_creature->HasAura(SPELL_BERSERK))
{
if(BerserkTimer < diff)
DoCast(m_creature, SPELL_BERSERK);
else BerserkTimer -= diff;
}
events.Update(diff);
if(SummonFlameTimer < diff)
while(uint32 eventId = events.ExecuteEvent())
{
DoCast(m_creature, SPELL_MOLTEN_PUNCH);
SummonFlameTimer = 20000;
}else SummonFlameTimer -= diff;
if(Phase1)
{
if(HatefulStrikeTimer < diff)
switch(eventId)
{
if(Unit* target = CalculateHatefulStrikeTarget())
case EVENT_BERSERK:
m_creature->CastSpell(m_creature, SPELL_BERSERK, true);
break;
case EVENT_FLAME:
DoCast(m_creature, SPELL_MOLTEN_PUNCH);
events.DelayEvents(1500, GCD_CAST);
events.ScheduleEvent(20000, EVENT_FLAME, GCD_CAST);
break;
case EVENT_HATEFUL_STRIKE:
if(Unit* target = CalculateHatefulStrikeTarget())
DoCast(target, SPELL_HATEFUL_STRIKE);
events.DelayEvents(1000, GCD_CAST);
events.ScheduleEvent(5000, EVENT_HATEFUL_STRIKE, GCD_CAST, PHASE_STRIKE);
break;
case EVENT_SWITCH_TARGET:
if(Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1, 100, true))
{
DoResetThreat();
m_creature->AddThreat(target, 5000000.0f);
DoScriptText(EMOTE_NEW_TARGET, m_creature);
}
events.ScheduleEvent(10000, EVENT_SWITCH_TARGET, 0, PHASE_CHASE);
break;
case EVENT_VOLCANO:
{
DoCast(target, SPELL_HATEFUL_STRIKE);
HatefulStrikeTimer = 5000;
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0, 999, true);
if(!target) target = m_creature->getVictim();
if(target)
{
DoCast(target, SPELL_VOLCANIC_SUMMON);
DoScriptText(EMOTE_GROUND_CRACK, m_creature);
events.DelayEvents(1500, GCD_CAST);
}
events.ScheduleEvent(10000, EVENT_VOLCANO, GCD_CAST, PHASE_CHASE);
return;
}
}else HatefulStrikeTimer -= diff;
}
if(!Phase1)
{
if(SwitchTargetTimer < diff)
{
if(Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1, 100, true))
{
DoResetThreat();
m_creature->AddThreat(target, 5000000.0f);
DoScriptText(EMOTE_NEW_TARGET, m_creature);
SwitchTargetTimer = 10000;
}
}else SwitchTargetTimer -= diff;
if(SummonVolcanoTimer < diff)
{
if(Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0, 999, true))
{
if(!target)
target = m_creature->getVictim();
DoCast(target, SPELL_VOLCANIC_SUMMON);
DoScriptText(EMOTE_GROUND_CRACK, m_creature);
SummonVolcanoTimer = 10000;
}
}else SummonVolcanoTimer -= diff;
}
if(PhaseSwitchTimer < diff)
{
if(!Phase1)
{
Phase1 = true;
DoResetThreat();
PhaseSwitchTimer = 60000;
m_creature->SetSpeed(MOVE_RUN, 1.2f);
DoZoneInCombat();
m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false);
m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, false);
case EVENT_SWITCH_PHASE:
ChangePhase();
break;
}
else
{
Phase1 = false;
DoResetThreat();
SwitchTargetTimer = 10000;
SummonVolcanoTimer = 2000;
PhaseSwitchTimer = 60000;
m_creature->SetSpeed(MOVE_RUN, 0.9f);
DoZoneInCombat();
m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, true);
}
}else PhaseSwitchTimer -= diff;
}
DoMeleeAttackIfReady();
}
@@ -237,50 +231,25 @@ struct TRINITY_DLL_DECL boss_supremusAI : public ScriptedAI
struct TRINITY_DLL_DECL npc_volcanoAI : public ScriptedAI
{
npc_volcanoAI(Creature *c) : ScriptedAI(c)
{
pInstance = ((ScriptedInstance*)c->GetInstanceData());
}
ScriptedInstance *pInstance;
uint32 CheckTimer;
bool Eruption;
npc_volcanoAI(Creature *c) : ScriptedAI(c) {}
void Reset()
{
CheckTimer = 1500;
Eruption = false;
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
DoCast(m_creature, SPELL_VOLCANIC_ERUPTION);
}
void Aggro(Unit *who) {}
void MoveInLineOfSight(Unit *who)
void MoveInLineOfSight(Unit *who) {}
void DoAction(const uint32 info)
{
return; // paralyze the npc
m_creature->RemoveAura(SPELL_VOLCANIC_ERUPTION);
}
void UpdateAI(const uint32 diff)
{
if(CheckTimer < diff)
{
uint64 SupremusGUID = pInstance->GetData64(DATA_SUPREMUS);
Creature* Supremus = (Unit::GetCreature((*m_creature), SupremusGUID));
if(!Eruption && !((boss_supremusAI*)Supremus->AI())->Phase1)
{
Eruption = true;
DoCast(m_creature, SPELL_VOLCANIC_ERUPTION);
}
else if(Eruption && ((boss_supremusAI*)Supremus->AI())->Phase1)
{
m_creature->RemoveAura(SPELL_VOLCANIC_ERUPTION);
}
CheckTimer = 1500;
}else CheckTimer -= diff;
}
void UpdateAI(const uint32 diff) {}
};
CreatureAI* GetAI_boss_supremus(Creature *_Creature)

View File

@@ -46,6 +46,15 @@ EndScriptData */
#define GOBJECT_SPINE 185584
#define EVENT_BERSERK 1
#define EVENT_YELL 2
#define EVENT_NEEDLE 3
#define EVENT_SPINE 4
#define EVENT_SHIELD 5
#define GCD_CAST 1
#define GCD_YELL 2
struct TRINITY_DLL_DECL boss_najentusAI : public ScriptedAI
{
boss_najentusAI(Creature *c) : ScriptedAI(c)
@@ -54,21 +63,13 @@ struct TRINITY_DLL_DECL boss_najentusAI : public ScriptedAI
}
ScriptedInstance* pInstance;
uint32 NeedleSpineTimer;
uint32 EnrageTimer;
uint32 SpecialYellTimer;
uint32 TidalShieldTimer;
uint32 ImpalingSpineTimer;
EventMap events;
uint64 SpineTargetGUID;
void Reset()
{
EnrageTimer = 480000;
SpecialYellTimer = 45000 + (rand()%76)*1000;
ResetTimer();
events.Reset();
SpineTargetGUID = 0;
@@ -78,11 +79,8 @@ struct TRINITY_DLL_DECL boss_najentusAI : public ScriptedAI
void KilledUnit(Unit *victim)
{
switch(rand()%2)
{
case 0: DoScriptText(SAY_SLAY1, m_creature); break;
case 1: DoScriptText(SAY_SLAY2, m_creature); break;
}
DoScriptText(rand()%2 ? SAY_SLAY1 : SAY_SLAY2, m_creature);
events.DelayEvents(5000, GCD_YELL);
}
void JustDied(Unit *victim)
@@ -110,6 +108,9 @@ struct TRINITY_DLL_DECL boss_najentusAI : public ScriptedAI
DoScriptText(SAY_AGGRO, m_creature);
DoZoneInCombat();
events.ScheduleEvent(480000, EVENT_BERSERK, GCD_CAST);
events.ScheduleEvent(45000 + (rand()%76)*1000, EVENT_YELL, GCD_YELL);
ResetTimer();
}
bool RemoveImpalingSpine()
@@ -124,9 +125,9 @@ struct TRINITY_DLL_DECL boss_najentusAI : public ScriptedAI
void ResetTimer(uint32 inc = 0)
{
NeedleSpineTimer = 10000 + inc;
TidalShieldTimer = 60000 + inc;
ImpalingSpineTimer = 20000 + inc;
events.ScheduleEvent(10000 + inc, EVENT_NEEDLE, GCD_CAST);
events.ScheduleEvent(20000 + inc, EVENT_SPINE, GCD_CAST);
events.ScheduleEvent(60000 + inc, EVENT_SHIELD);
}
void UpdateAI(const uint32 diff)
@@ -134,58 +135,56 @@ struct TRINITY_DLL_DECL boss_najentusAI : public ScriptedAI
if (!UpdateVictim())
return;
if(TidalShieldTimer < diff)
{
m_creature->CastSpell(m_creature, SPELL_TIDAL_SHIELD, true);
ResetTimer(45000);
}else TidalShieldTimer -= diff;
events.Update(diff);
if(EnrageTimer < diff)
while(uint32 eventId = events.ExecuteEvent())
{
DoScriptText(SAY_ENRAGE2, m_creature);
m_creature->CastSpell(m_creature, SPELL_BERSERK, true);
EnrageTimer = 600000;
}else EnrageTimer -= diff;
if(NeedleSpineTimer < diff)
{
//m_creature->CastSpell(m_creature, SPELL_NEEDLE_SPINE, true);
std::list<Unit*> target;
SelectUnitList(target, 3, SELECT_TARGET_RANDOM, 80, true);
for(std::list<Unit*>::iterator i = target.begin(); i != target.end(); ++i)
m_creature->CastSpell(*i, 39835, true);
NeedleSpineTimer = 2000+rand()%1000;
}else NeedleSpineTimer -= diff;
if(SpecialYellTimer < diff)
{
switch(rand()%2)
switch(eventId)
{
case 0: DoScriptText(SAY_SPECIAL1, m_creature); break;
case 1: DoScriptText(SAY_SPECIAL2, m_creature); break;
}
SpecialYellTimer = 25000 + (rand()%76)*1000;
}else SpecialYellTimer -= diff;
if(ImpalingSpineTimer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1);
if(!target) target = m_creature->getVictim();
if(target)
{
m_creature->CastSpell(target, SPELL_IMPALING_SPINE, true);
SpineTargetGUID = target->GetGUID();
//must let target summon, otherwise you cannot click the spine
target->SummonGameObject(GOBJECT_SPINE, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), m_creature->GetOrientation(), 0, 0, 0, 0, 30);
switch(rand()%2)
case EVENT_SHIELD:
m_creature->CastSpell(m_creature, SPELL_TIDAL_SHIELD, true);
ResetTimer(45000);
break;
case EVENT_BERSERK:
DoScriptText(SAY_ENRAGE2, m_creature);
m_creature->CastSpell(m_creature, SPELL_BERSERK, true);
events.DelayEvents(15000, GCD_YELL);
break;
case EVENT_SPINE:
{
case 0: DoScriptText(SAY_NEEDLE1, m_creature); break;
case 1: DoScriptText(SAY_NEEDLE2, m_creature); break;
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1);
if(!target) target = m_creature->getVictim();
if(target)
{
m_creature->CastSpell(target, SPELL_IMPALING_SPINE, true);
SpineTargetGUID = target->GetGUID();
//must let target summon, otherwise you cannot click the spine
target->SummonGameObject(GOBJECT_SPINE, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), m_creature->GetOrientation(), 0, 0, 0, 0, 30);
DoScriptText(rand()%2 ? SAY_NEEDLE1 : SAY_NEEDLE2, m_creature);
events.DelayEvents(1500, GCD_CAST);
events.DelayEvents(15000, GCD_YELL);
}
events.ScheduleEvent(21000, EVENT_SPINE, GCD_CAST);
return;
}
ImpalingSpineTimer = 21000;
case EVENT_NEEDLE:
{
//m_creature->CastSpell(m_creature, SPELL_NEEDLE_SPINE, true);
std::list<Unit*> target;
SelectUnitList(target, 3, SELECT_TARGET_RANDOM, 80, true);
for(std::list<Unit*>::iterator i = target.begin(); i != target.end(); ++i)
m_creature->CastSpell(*i, 39835, true);
events.ScheduleEvent(15000+rand()%10000, EVENT_NEEDLE, GCD_CAST);
events.DelayEvents(1500, GCD_CAST);
return;
}
case EVENT_YELL:
DoScriptText(rand()%2 ? SAY_SPECIAL1 : SAY_SPECIAL2, m_creature);
events.ScheduleEvent(25000 + (rand()%76)*1000, EVENT_YELL, GCD_YELL);
events.DelayEvents(15000, GCD_YELL);
break;
}
}else ImpalingSpineTimer -= diff;
}
DoMeleeAttackIfReady();
}

View File

@@ -77,6 +77,76 @@ enum SCEquip
EQUIP_UNEQUIP = 0
};
class EventMap : private std::map<uint32, uint32>
{
private:
uint32 m_time, m_phase;
public:
explicit EventMap() : m_phase(0), m_time(0) {}
void Reset() { clear(); m_time = 0; m_phase = 0; }
void Update(uint32 time) { m_time += time; }
void SetPhase(uint32 phase)
{
if(phase && phase < 9)
m_phase = (1 << (phase + 24));
}
void ScheduleEvent(uint32 time, uint32 eventId, uint32 gcd = 0, uint32 phase = 0)
{
time += m_time;
if(gcd && gcd < 9)
eventId |= (1 << (gcd + 16));
if(phase && phase < 9)
eventId |= (1 << (phase + 24));
iterator itr = find(time);
while(itr != end())
{
++time;
itr = find(time);
}
insert(std::make_pair(time, eventId));
}
uint32 ExecuteEvent()
{
while(!empty())
{
if(begin()->first > m_time)
return 0;
else if(m_phase && (begin()->second & 0xFF000000) && !(begin()->second & m_phase))
erase(begin());
else
{
uint32 eventId = (begin()->second & 0x0000FFFF);
erase(begin());
return eventId;
}
}
return 0;
}
void DelayEvents(uint32 time, uint32 gcd)
{
time += m_time;
gcd = (1 << (gcd + 16));
for(iterator itr = begin(); itr != end();)
{
if(itr->first >= time)
break;
if(itr->second & gcd)
{
ScheduleEvent(time, itr->second);
erase(itr++);
}
else
++itr;
}
}
};
class TRINITY_DLL_SPEC UnitAI
{
protected: