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

@@ -1139,7 +1139,22 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
case SMART_ACTION_DIE:
{
if (me && !me->isDead())
if (e.action.die.milliseconds)
{
if (me && !me->isDead())
{
me->m_Events.AddEventAtOffset([&]
{
// We need to check again to see if we didn't die in the process.
if (me && !me->isDead())
{
me->KillSelf();
LOG_DEBUG("sql.sql", "SmartScript::ProcessAction: SMART_ACTION_DIE: Creature {}", me->GetGUID().ToString());
}
}, Milliseconds(e.action.die.milliseconds));
}
}
else if (me && !me->isDead())
{
me->KillSelf();
LOG_DEBUG("sql.sql", "SmartScript::ProcessAction: SMART_ACTION_DIE: Creature {}", me->GetGUID().ToString());

View File

@@ -641,7 +641,7 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e)
case SMART_ACTION_SET_INST_DATA: return sizeof(SmartAction::setInstanceData);
case SMART_ACTION_SET_INST_DATA64: return sizeof(SmartAction::setInstanceData64);
case SMART_ACTION_UPDATE_TEMPLATE: return sizeof(SmartAction::updateTemplate);
case SMART_ACTION_DIE: return NO_PARAMS;
case SMART_ACTION_DIE: return sizeof(SmartAction::die);
case SMART_ACTION_SET_IN_COMBAT_WITH_ZONE: return NO_PARAMS;
case SMART_ACTION_CALL_FOR_HELP: return sizeof(SmartAction::callHelp);
case SMART_ACTION_SET_SHEATH: return sizeof(SmartAction::setSheath);

View File

@@ -559,7 +559,7 @@ enum SMART_ACTION
SMART_ACTION_SET_INST_DATA = 34, // Field, Data
SMART_ACTION_SET_INST_DATA64 = 35, // Field,
SMART_ACTION_UPDATE_TEMPLATE = 36, // Entry, UpdateLevel
SMART_ACTION_DIE = 37, // No Params
SMART_ACTION_DIE = 37, // Milliseconds
SMART_ACTION_SET_IN_COMBAT_WITH_ZONE = 38, // Range (if outside of dungeon)
SMART_ACTION_CALL_FOR_HELP = 39, // Radius, With Emote
SMART_ACTION_SET_SHEATH = 40, // Sheath (0-unarmed, 1-melee, 2-ranged)
@@ -925,6 +925,11 @@ struct SmartAction
SAIBool updateLevel;
} updateTemplate;
struct
{
uint32 milliseconds;
} die;
struct
{
uint32 range;

View File

@@ -12482,32 +12482,6 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui
switch (spellProto->SpellFamilyName)
{
case SPELLFAMILY_DRUID:
{
// Nourish vs Idol of the Flourishing Life
if (spellProto->SpellFamilyFlags[1] & 0x02000000)
{
if (AuraEffect const* relicAurEff = GetAuraEffect(64949, EFFECT_0))
{
DoneAdvertisedBenefit += relicAurEff->GetAmount();
}
}
// Lifebloom vs Idol of Lush Moss/Increased Lifebloom Periodic
if (spellProto->SpellFamilyFlags[1] & 00000010)
{
if (AuraEffect const* relicAurEff = GetAuraEffect(60779, EFFECT_0))
{
DoneAdvertisedBenefit += relicAurEff->GetAmount();
}
if (AuraEffect const* relicAurEff = GetAuraEffect(34246, EFFECT_0))
{
DoneAdvertisedBenefit += relicAurEff->GetAmount();
}
}
break;
}
case SPELLFAMILY_DEATHKNIGHT:
{
// Impurity

View File

@@ -439,6 +439,13 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->Effects[EFFECT_0].SpellClassMask = flag96(0x00000040, 0x00000000, 0x00000000);
});
// Idol of the Flourishing Life
ApplySpellFix({ 64949 }, [](SpellInfo* spellInfo)
{
spellInfo->Effects[EFFECT_0].SpellClassMask = flag96(0x00000000, 0x02000000, 0x00000000);
spellInfo->Effects[EFFECT_0].ApplyAuraName = SPELL_AURA_ADD_FLAT_MODIFIER;
});
ApplySpellFix({
34231, // Libram of the Lightbringer
60792, // Libram of Tolerance

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;