Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2023-06-02 15:38:15 +08:00
19 changed files with 244 additions and 165 deletions

View File

@@ -63,9 +63,9 @@ enum Events
EVENT_PYROBLAST = 6,
// Hack due to trigger spell not in dbc
EVENT_FIRE_SHIELD = 7,
// Make sure all players have aura from altar
EVENT_PLAYER_CHECK = 8,
EVENT_ENTER_COMBAT = 9
EVENT_PRE_ENTER_COMBAT_1 = 8,
EVENT_PRE_ENTER_COMBAT_2 = 9,
EVENT_ENTER_COMBAT = 10
};
class boss_pyroguard_emberseer : public CreatureScript
@@ -101,7 +101,8 @@ public:
switch (data)
{
case 1:
events.ScheduleEvent(EVENT_PLAYER_CHECK, 5s);
events.ScheduleEvent(EVENT_PRE_FIGHT_1, 2s);
instance->SetBossState(DATA_PYROGAURD_EMBERSEER, IN_PROGRESS);
break;
case 2:
// Close these two doors on Blackhand Incarcerators aggro
@@ -153,14 +154,12 @@ public:
if (me->GetAuraCount(SPELL_EMBERSEER_GROWING_TRIGGER) == 20)
{
me->RemoveAura(SPELL_ENCAGED_EMBERSEER);
me->RemoveAura(SPELL_FREEZE_ANIM);
me->CastSpell(me, SPELL_EMBERSEER_FULL_STRENGTH);
Talk(EMOTE_FREE_OF_BONDS);
Talk(YELL_FREE_OF_BONDS);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetImmuneToPC(false);
events.ScheduleEvent(EVENT_ENTER_COMBAT, 2s);
events.CancelEvent(EVENT_FIRE_SHIELD); // temporarily cancel fire shield to keep it from interrupting combat start
// Schedule out the pre-combat scene
events.ScheduleEvent(EVENT_PRE_ENTER_COMBAT_1, 0s);
events.ScheduleEvent(EVENT_PRE_ENTER_COMBAT_2, 2s);
events.ScheduleEvent(EVENT_ENTER_COMBAT, 5s);
}
}
}
@@ -235,7 +234,7 @@ public:
if (Creature* creature = *itr)
creature->AI()->SetData(1, 1);
}
events.ScheduleEvent(EVENT_PRE_FIGHT_2, 32s);
events.ScheduleEvent(EVENT_PRE_FIGHT_2, 2s);
break;
}
case EVENT_PRE_FIGHT_2:
@@ -248,25 +247,21 @@ public:
DoCast(me, SPELL_FIRE_SHIELD);
events.ScheduleEvent(EVENT_FIRE_SHIELD, 3s);
break;
case EVENT_PLAYER_CHECK:
{
// Check to see if all players in instance have aura SPELL_EMBERSEER_START before starting event
bool _hasAura = false;
Map::PlayerList const& players = me->GetMap()->GetPlayers();
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
if (Player* player = itr->GetSource()->ToPlayer())
if (player->HasAura(SPELL_EMBERSEER_OBJECT_VISUAL))
_hasAura = true;
if (_hasAura)
{
events.ScheduleEvent(EVENT_PRE_FIGHT_1, 1s);
instance->SetBossState(DATA_PYROGAURD_EMBERSEER, IN_PROGRESS);
}
break;
}
case EVENT_PRE_ENTER_COMBAT_1:
me->RemoveAura(SPELL_ENCAGED_EMBERSEER);
me->RemoveAura(SPELL_FREEZE_ANIM);
me->CastSpell(me, SPELL_EMBERSEER_FULL_STRENGTH);
Talk(EMOTE_FREE_OF_BONDS);
break;
case EVENT_PRE_ENTER_COMBAT_2:
Talk(YELL_FREE_OF_BONDS);
break;
case EVENT_ENTER_COMBAT:
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetImmuneToPC(false);
DoZoneInCombat();
// re-enable fire shield
events.ScheduleEvent(EVENT_FIRE_SHIELD, 0s);
break;
default:
break;
@@ -347,11 +342,6 @@ public:
_fleedForAssistance = false;
}
void JustDied(Unit* /*killer*/) override
{
me->DespawnOrUnsummon(10000);
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*type*/, SpellSchoolMask /*school*/) override
{
if (!_fleedForAssistance && me->HealthBelowPctDamaged(30, damage))
@@ -392,7 +382,6 @@ public:
}
_events.ScheduleEvent(EVENT_STRIKE, 8s, 16s);
_events.ScheduleEvent(EVENT_ENCAGE, 10s, 20s);
}
void UpdateAI(uint32 diff) override

View File

@@ -34,107 +34,70 @@ enum Spells
SPELL_DARK_SHELL = 32358
};
enum Events
enum Groups
{
EVENT_VOID_BLAST = 1,
EVENT_DARK_SHELL
GROUP_VOID_BLAST = 1
};
class boss_pandemonius : public CreatureScript
{
public:
boss_pandemonius() : CreatureScript("boss_pandemonius") { }
constexpr uint8 MAX_VOID_BLAST = 5;
CreatureAI* GetAI(Creature* creature) const override
struct boss_pandemonius : public BossAI
{
boss_pandemonius(Creature* creature) : BossAI(creature, DATA_PANDEMONIUS)
{
return GetManaTombsAI<boss_pandemoniusAI>(creature);
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
struct boss_pandemoniusAI : public ScriptedAI
void JustEngagedWith(Unit* who) override
{
boss_pandemoniusAI(Creature* creature) : ScriptedAI(creature) { }
Talk(SAY_AGGRO);
EventMap events;
void Reset() override
{
events.Reset();
VoidBlastCounter = 0;
}
void JustEngagedWith(Unit*) override
{
me->SetInCombatWithZone();
Talk(SAY_AGGRO);
events.ScheduleEvent(EVENT_DARK_SHELL, 20000);
events.ScheduleEvent(EVENT_VOID_BLAST, urand(8000, 23000));
}
void KilledUnit(Unit* victim) override
{
if (victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_KILL);
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
scheduler.
Schedule(20s, GROUP_VOID_BLAST, [this](TaskContext context)
{
case EVENT_VOID_BLAST:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
{
DoCast(target, SPELL_VOID_BLAST);
++VoidBlastCounter;
}
if (me->IsNonMeleeSpellCast(false))
{
me->InterruptNonMeleeSpells(true);
}
if (VoidBlastCounter == 5)
Talk(EMOTE_DARK_SHELL);
DoCastSelf(SPELL_DARK_SHELL);
context.Repeat();
})
.Schedule(8s, 23s, [this](TaskContext context)
{
if (!(context.GetRepeatCounter() % (MAX_VOID_BLAST + 1)))
{
VoidBlastCounter = 0;
events.RescheduleEvent(EVENT_VOID_BLAST, urand(15000, 25000));
context.Repeat(15s, 25s);
}
else
{
events.RescheduleEvent(EVENT_VOID_BLAST, 500);
events.DelayEvents(EVENT_DARK_SHELL, 500);
}
break;
case EVENT_DARK_SHELL:
if (me->IsNonMeleeSpellCast(false))
{
me->InterruptNonMeleeSpells(true);
DoCastRandomTarget(SPELL_VOID_BLAST);
context.Repeat(500ms);
context.DelayGroup(GROUP_VOID_BLAST, 500ms);
}
});
Talk(EMOTE_DARK_SHELL);
DoCast(me, SPELL_DARK_SHELL);
events.RescheduleEvent(EVENT_DARK_SHELL, 20000);
break;
default:
break;
}
BossAI::JustEngagedWith(who);
}
DoMeleeAttackIfReady();
}
void KilledUnit(Unit* victim) override
{
if (victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_KILL);
}
private:
uint32 VoidBlastCounter;
};
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
BossAI::JustDied(killer);
}
};
void AddSC_boss_pandemonius()
{
new boss_pandemonius();
RegisterManaTombsCreatureAI(boss_pandemonius);
}

View File

@@ -104,7 +104,14 @@ struct boss_talon_king_ikiss : public BossAI
context.Repeat(7s, 12s);
}).Schedule(8s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_POLYMORPH);
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_POLYMORPH);
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, [&](Unit* target) -> bool
{
return target && !target->IsImmunedToSpell(spellInfo);
}))
{
DoCast(target, SPELL_POLYMORPH);
}
context.Repeat(15s, 17500ms);
});

View File

@@ -114,7 +114,7 @@ struct boss_grand_warlock_nethekurse : public BossAI
if (!_canAggro)
{
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetImmuneToAll(true);
}
}
@@ -235,11 +235,13 @@ struct boss_grand_warlock_nethekurse : public BossAI
me->SetInCombatWithZone();
return;
}
else if (action == ACTION_START_INTRO)
else if (action == ACTION_START_INTRO && !_introStarted)
{
// Hack: Prevent from pulling from behind door
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetImmuneToAll(false);
_canAggro = true;
// Bit of a hack to make sure it can't be started with the areatrigger AND the door opening
_introStarted = true;
std::list<Creature*> creatureList;
GetCreatureListWithEntryInGrid(creatureList, me, NPC_PEON, 60.0f);
@@ -247,7 +249,7 @@ struct boss_grand_warlock_nethekurse : public BossAI
{
if (creature)
{
creature->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
creature->SetImmuneToAll(false);
}
}
IntroRP();
@@ -258,6 +260,21 @@ struct boss_grand_warlock_nethekurse : public BossAI
{
scheduler.Update(diff);
// this should never be called if the action to start intro has been called
if (!_introStarted)
{
// find the door that is nearest to the entrance
if (GameObject* nethekursedoor = GetClosestGameObjectWithEntry(me, GO_GRAND_WARLOCK_CHAMBER_DOOR_1, 100.0f))
{
// check if door is openened
//this should only happen before the intro, if the door is picked by someone
if(nethekursedoor->GetGoState() == 0)
{
DoAction(ACTION_START_INTRO);
}
}
}
if (!UpdateVictim())
return;
@@ -272,6 +289,7 @@ private:
uint8 PeonEngagedCount = 0;
uint8 PeonKilledCount = 0;
bool _canAggro = false;
bool _introStarted = false;
};
class spell_tsh_shadow_bolt : public SpellScript

View File

@@ -54,6 +54,24 @@ struct boss_nethermancer_sepethrea : public BossAI
});
}
bool CanAIAttack(Unit const* target) const override
{
if (me->GetThreatMgr().GetThreatListSize() > 1)
{
ThreatContainer::StorageType::const_iterator lastRef = me->GetThreatMgr().GetOnlineContainer().GetThreatList().end();
--lastRef;
if (Unit* lastTarget = (*lastRef)->getTarget())
{
if (lastTarget != target)
{
return !target->HasAura(SPELL_DRAGONS_BREATH);
}
}
}
return true;
}
void JustEngagedWith(Unit* /*who*/) override
{
_JustEngagedWith();

View File

@@ -500,6 +500,31 @@ class spell_dru_glyph_of_starfire : public SpellScript
}
};
// 34246 - Idol of the Emerald Queen
// 60779 - Idol of Lush Moss
class spell_dru_idol_lifebloom : public AuraScript
{
PrepareAuraScript(spell_dru_idol_lifebloom);
void HandleEffectCalcSpellMod(AuraEffect const* aurEff, SpellModifier*& spellMod)
{
if (!spellMod)
{
spellMod = new SpellModifier(GetAura());
spellMod->op = SPELLMOD_DOT;
spellMod->type = SPELLMOD_FLAT;
spellMod->spellId = GetId();
spellMod->mask = aurEff->GetSpellInfo()->Effects[aurEff->GetEffIndex()].SpellClassMask;
}
spellMod->value = aurEff->GetAmount() / 7;
}
void Register() override
{
DoEffectCalcSpellMod += AuraEffectCalcSpellModFn(spell_dru_idol_lifebloom::HandleEffectCalcSpellMod, EFFECT_0, SPELL_AURA_DUMMY);
}
};
// 29166 - Innervate
class spell_dru_innervate : public AuraScript
{
@@ -1158,6 +1183,7 @@ void AddSC_druid_spell_scripts()
RegisterSpellScript(spell_dru_dash);
RegisterSpellScript(spell_dru_enrage);
RegisterSpellScript(spell_dru_glyph_of_starfire);
RegisterSpellScript(spell_dru_idol_lifebloom);
RegisterSpellScript(spell_dru_innervate);
RegisterSpellScript(spell_dru_lifebloom);
RegisterSpellScript(spell_dru_living_seed);

View File

@@ -990,7 +990,7 @@ public:
Reset();
}
void PatientSaved(Creature* /*soldier*/, Player* player, Location* point)
void PatientSaved(Creature* savedPatient, Player* player, Location* point)
{
if (player && PlayerGUID == player->GetGUID())
{
@@ -1004,8 +1004,9 @@ public:
{
for (ObjectGuid const& guid : Patients)
{
if (Creature* patient = ObjectAccessor::GetCreature(*me, guid))
patient->setDeathState(JUST_DIED);
if (guid != savedPatient->GetGUID()) // Don't kill the last guy we just saved
if (Creature* patient = ObjectAccessor::GetCreature(*me, guid))
patient->setDeathState(JUST_DIED);
}
}
@@ -1069,6 +1070,9 @@ public:
//no regen health
me->SetUnitFlag(UNIT_FLAG_IN_COMBAT);
//prevent using normal bandages
me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_BANDAGE, true);
//to make them lay with face down
me->SetUInt32Value(UNIT_FIELD_BYTES_1, UNIT_STAND_STATE_DEAD);
@@ -1078,18 +1082,26 @@ public:
{
//lower max health
case 12923:
case 12938: //Injured Soldier
me->SetHealth(me->CountPctFromMaxHealth(75));
case 12938: //Injured Soldier, 65 seconds to die
me->SetHealth(me->CountPctFromMaxHealth(65));
break;
case 12924:
case 12936: //Badly injured Soldier
me->SetHealth(me->CountPctFromMaxHealth(50));
case 12936: //Badly injured Soldier, 35 seconds to die
me->SetHealth(me->CountPctFromMaxHealth(35));
break;
case 12925:
case 12937: //Critically injured Soldier
case 12937: //Critically injured Soldier, 25 seconds to die
me->SetHealth(me->CountPctFromMaxHealth(25));
break;
}
// Schedule health reduction every 1 second
_scheduler.Schedule(1s, [this](TaskContext context)
{
// Reduction of 1% per second, matching WotLK Classic timing
me->ModifyHealth(me->CountPctFromMaxHealth(1) * -1);
context.Repeat(1s);
});
}
void JustEngagedWith(Unit* /*who*/) override { }
@@ -1134,13 +1146,12 @@ public:
}
}
void UpdateAI(uint32 /*diff*/) override
void UpdateAI(uint32 diff) override
{
//lower HP on every world tick makes it a useful counter, not officlone though
if (me->IsAlive() && me->GetHealth() > 6)
me->ModifyHealth(-5);
if (me->IsAlive() && me->GetHealth() <= 6)
_scheduler.Update(diff);
if (me->IsAlive() && me->GetHealth() < me->CountPctFromMaxHealth(1))
{
me->RemoveUnitFlag(UNIT_FLAG_IN_COMBAT);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
@@ -1152,6 +1163,10 @@ public:
CAST_AI(npc_doctor::npc_doctorAI, doctor->AI())->PatientDied(Coord);
}
}
private:
TaskScheduler _scheduler;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -1162,7 +1177,7 @@ public:
void npc_doctor::npc_doctorAI::UpdateAI(uint32 diff)
{
if (Event && SummonPatientCount >= 20)
if (Event && SummonPatientCount >= 24) // Need to keep the event going long enough to save the last few patients
{
Reset();
return;
@@ -1170,7 +1185,7 @@ void npc_doctor::npc_doctorAI::UpdateAI(uint32 diff)
if (Event)
{
if (SummonPatientTimer <= diff)
if (SummonPatientTimer <= diff || SummonPatientCount < 6) // Starts with 6 beds filled for both factions
{
if (Coordinates.empty())
return;