diff --git a/data/sql/updates/pending_db_world/rev_1638053041040101600.sql b/data/sql/updates/pending_db_world/rev_1638053041040101600.sql
new file mode 100644
index 000000000..fac8e1b3a
--- /dev/null
+++ b/data/sql/updates/pending_db_world/rev_1638053041040101600.sql
@@ -0,0 +1,24 @@
+INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1638053041040101600');
+
+UPDATE `creature_template` SET `AIName` = '', `ScriptName` = 'boss_jarien' WHERE `entry` = 16101;
+UPDATE `creature_template` SET `AIName` = '', `ScriptName` = 'boss_sothos' WHERE `entry` = 16102;
+
+DELETE FROM `creature_text` WHERE `CreatureID` IN (16101, 16102, 16103, 16104);
+INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
+(16101, 0, 0, 'Hello, brother.', 14, 0, 100, 0, 0, 0, 11971, 0, 'Jarien - ON SUMMON'),
+(16101, 1, 0, 'Would-be interlopers, I\'m afraid.', 14, 0, 100, 0, 0, 0, 11973, 0, 'Jarien - ON SUMMON - 1'),
+(16101, 2, 0, 'Yes, we shall!', 14, 0, 100, 0, 0, 0, 11975, 0, 'Jarien - ON SUMMON - 2'),
+(16101, 3, 0, '%s goes into a rage after seeing $n fall in battle!', 16, 0, 100, 0, 0, 0, 11939, 0, 'Jarien - ON SOTHOS DEATH'),
+(16102, 0, 0, 'Hello, sister. What have we here?', 14, 0, 100, 0, 0, 0, 11972, 0, 'Sothos - ON SUMMON'),
+(16102, 1, 0, 'Shall we slay them for the impertinence of disturbing our sleep?', 14, 0, 100, 0, 0, 0, 11974, 0, 'Sothos - ON SUMMON - 1'),
+(16102, 2, 0, '%s goes into a rage after seeing $n fall in battle!', 16, 0, 100, 0, 0, 0, 11939, 0, 'Sothos - ON JARIEN DEATH'),
+(16103, 0, 0, 'Thank you for freeing me, my brother, and all of you! We can finally rest in peace now.', 14, 0, 100, 0, 0, 0, 11819, 0, 'Spirit of Jarien - ON BOTH DEATHS'),
+(16104, 0, 0, 'Thank you for freeing me, my sister, and all of you! We can finally rest in peace now.', 14, 0, 100, 0, 0, 0, 11859, 0, 'Spirit of Sothos - ON BOTH DEATHS');
+
+DELETE FROM `smart_scripts` WHERE `source_type` = 0 AND `entryorguid` IN (16101, 16102);
+
+-- Fix Jarien and Sothos spawn position
+DELETE FROM `smart_scripts` WHERE `entryorguid` = 16046 AND `source_type` = 0;
+INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
+(16046, 0, 0, 1, 38, 0, 100, 1, 1, 1, 0, 0, 0, 12, 16101, 8, 0, 0, 0, 0, 8, 0, 0, 0, 0, 3420.13452, -3056.939697, 136.4981, 0.208907, 'Jarien and Sothos Trigger - On Data Set - Spawn Jarien'),
+(16046, 0, 1, 0, 61, 0, 100, 1, 1, 1, 0, 0, 0, 12, 16102, 8, 0, 0, 0, 0, 8, 0, 0, 0, 0, 3427.82251, -3055.582764, 136.4981, 3.319085, 'Jarien and Sothos Trigger - Linked with Previous Event - Spawn Sothos');
diff --git a/src/server/scripts/EasternKingdoms/Stratholme/boss_jarien_and_sothos.cpp b/src/server/scripts/EasternKingdoms/Stratholme/boss_jarien_and_sothos.cpp
new file mode 100644
index 000000000..e3658a3c6
--- /dev/null
+++ b/src/server/scripts/EasternKingdoms/Stratholme/boss_jarien_and_sothos.cpp
@@ -0,0 +1,355 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 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 Affero 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 .
+ */
+
+#include "ScriptMgr.h"
+#include "ScriptedCreature.h"
+#include "stratholme.h"
+#include "TaskScheduler.h"
+
+enum Texts
+{
+ // Sothos
+ SAY_SOTHOS_ON_SUMMON_0 = 0,
+ SAY_SOTHOS_ON_SUMMON_1 = 1,
+ EMOTE_SOTHOS_VENGEANCE = 2,
+
+ // Jarien
+ SAY_JARIEN_ON_SUMMON_0 = 0,
+ SAY_JARIEN_ON_SUMMON_1 = 1,
+ SAY_JARIEN_ON_SUMMON_2 = 2,
+ EMOTE_JARIEN_VENGEANCE = 3,
+
+ // Spirit
+ SAY_SPIRIT_BOTH_DEAD = 0
+};
+
+enum Spells
+{
+ // Jarien
+ SPELL_MORTAL_STRIKE = 16856,
+ SPELL_SHADOW_SHOCK = 22575,
+ SPELL_CRIPPLE = 20812,
+ SPELL_CLEAVE = 15284,
+
+ // Sothos
+ SPELL_SHIELD_CHARGE = 15749,
+ SPELL_SHIELD_SLAM = 15655,
+ SPELL_SHIELD_BLOCK = 12169,
+ SPELL_SHADOW_BOLT = 27646,
+ SPELL_FEAR = 27641,
+
+ // Both
+ SPELL_VENGEANCE = 27650
+};
+
+enum Phases
+{
+ PHASE_TALK = 0,
+ PHASE_FIGHT
+};
+
+enum Actions
+{
+ ACTION_PARTNER_DEAD = 0
+};
+
+const Position heirloomsPosition = { 3423.389893f, -3055.571045f, 136.49837f, 5.707379f };
+
+void HandleBothDead(Creature* creature, bool jarien, Unit* killer)
+{
+ // Spirit talk
+ if (jarien)
+ {
+ if (Creature* spirit = creature->FindNearestCreature(NPC_SPIRIT_OF_JARIEN, 100.f))
+ {
+ spirit->AttackStop();
+ spirit->SetReactState(REACT_PASSIVE);
+ spirit->AI()->Talk(SAY_SPIRIT_BOTH_DEAD);
+ }
+ }
+ else
+ {
+ if (Creature* spirit = creature->FindNearestCreature(NPC_SPIRIT_OF_SOTHOS, 100.f))
+ {
+ spirit->AttackStop();
+ spirit->SetReactState(REACT_PASSIVE);
+ spirit->AI()->Talk(SAY_SPIRIT_BOTH_DEAD);
+ }
+ }
+
+ // chest spawn
+ if (GameObject* chest = killer->SummonGameObject(GO_JARIEN_AND_SOTHOS_HEIRLOOMS, heirloomsPosition.GetPositionX(), heirloomsPosition.GetPositionY(), heirloomsPosition.GetPositionZ(), heirloomsPosition.GetOrientation(), 0.f, 0.f, 0.f, 0.f, 0))
+ {
+ chest->setActive(true);
+ chest->SetGoState(GO_STATE_READY);
+ chest->SetLootState(GO_READY);
+ chest->SetUInt32Value(GAMEOBJECT_FACTION, 35);
+ }
+}
+
+struct boss_jarien : public BossAI
+{
+ boss_jarien(Creature* creature) : BossAI(creature, DATA_JARIEN)
+ {
+ _scheduler.SetValidator([this]
+ {
+ return !me->HasUnitState(UNIT_STATE_CASTING);
+ });
+
+ _talked = false;
+ _phase = PHASE_TALK;
+ }
+
+ void Reset() override
+ {
+ _scheduler.CancelAll();
+ _phase = _talked ? PHASE_FIGHT : PHASE_TALK;
+ if (_sothosDied)
+ {
+ if (Creature* sothos = me->FindNearestCreature(NPC_SOTHOS, 200.f, false))
+ {
+ sothos->Respawn();
+ }
+ }
+ _sothosDied = false;
+ _Reset();
+ }
+
+ void IsSummonedBy(Unit* /*summoner*/) override
+ {
+ me->SetReactState(REACT_PASSIVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+ Talk(SAY_JARIEN_ON_SUMMON_0);
+
+ _scheduler.Schedule(6s, [this](TaskContext /*context*/)
+ {
+ Talk(SAY_JARIEN_ON_SUMMON_1);
+ })
+ .Schedule(12s, [this](TaskContext /*context*/)
+ {
+ Talk(SAY_JARIEN_ON_SUMMON_2);
+ _talked = true;
+ _phase = PHASE_FIGHT;
+ me->SetReactState(REACT_AGGRESSIVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+ });
+ }
+
+ void JustDied(Unit* killer) override
+ {
+ _JustDied();
+ if (Creature * sothos = me->FindNearestCreature(NPC_SOTHOS, 200.f))
+ {
+ sothos->AI()->DoAction(ACTION_PARTNER_DEAD);
+ }
+
+ me->SummonCreature(NPC_SPIRIT_OF_JARIEN, me->GetPosition());
+
+ if (_sothosDied)
+ {
+ HandleBothDead(me, true, killer);
+ }
+ }
+
+ void DoAction(int32 action) override
+ {
+ if (action == ACTION_PARTNER_DEAD)
+ {
+ me->SetFullHealth();
+ DoCastSelf(SPELL_VENGEANCE);
+ Talk(EMOTE_JARIEN_VENGEANCE);
+ _sothosDied = true;
+ }
+ }
+
+ void EnterCombat(Unit* /*who*/) override
+ {
+ _EnterCombat();
+ _scheduler.Schedule(5s, [this](TaskContext context)
+ {
+ DoCastVictim(SPELL_SHADOW_SHOCK);
+ context.Repeat(10s, 12s);
+ })
+ .Schedule(3s, [this](TaskContext context)
+ {
+ DoCastVictim(SPELL_CLEAVE);
+ context.Repeat(10s);
+ })
+ .Schedule(11s, [this](TaskContext context)
+ {
+ DoCastRandomTarget(SPELL_CRIPPLE);
+ context.Repeat(30s);
+ })
+ .Schedule(10s, [this](TaskContext context)
+ {
+ DoCastVictim(SPELL_MORTAL_STRIKE);
+ context.Repeat(15s);
+ });
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim() && _phase != PHASE_TALK)
+ {
+ return;
+ }
+
+ _scheduler.Update(diff, [this]
+ {
+ DoMeleeAttackIfReady();
+ });
+ }
+
+ protected:
+ TaskScheduler _scheduler;
+ uint8 _phase;
+ bool _talked;
+ bool _sothosDied;
+};
+
+struct boss_sothos : public BossAI
+{
+ boss_sothos(Creature* creature) : BossAI(creature, DATA_SOTHOS)
+ {
+ _scheduler.SetValidator([this]
+ {
+ return !me->HasUnitState(UNIT_STATE_CASTING);
+ });
+
+ _talked = false;
+ _phase = PHASE_TALK;
+ }
+
+ void Reset() override
+ {
+ _scheduler.CancelAll();
+ _phase = _talked ? PHASE_FIGHT : PHASE_TALK;
+ if (_jarienDied)
+ {
+ if (Creature* jarien = me->FindNearestCreature(NPC_JARIEN, 200.f, false))
+ {
+ jarien->Respawn();
+ }
+ }
+ _jarienDied = false;
+ _Reset();
+ }
+
+ void IsSummonedBy(Unit* /*summoner*/) override
+ {
+ me->SetReactState(REACT_PASSIVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
+ _scheduler.Schedule(12s, [this](TaskContext /*context*/)
+ {
+ _talked = true;
+ _phase = PHASE_FIGHT;
+ me->SetReactState(REACT_AGGRESSIVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+ })
+ .Schedule(3s, [this](TaskContext /*context*/)
+ {
+ Talk(SAY_SOTHOS_ON_SUMMON_0);
+ })
+ .Schedule(9s, [this](TaskContext /*context*/)
+ {
+ Talk(SAY_SOTHOS_ON_SUMMON_1);
+ });
+ }
+
+ void JustDied(Unit* killer) override
+ {
+ _JustDied();
+ if (Creature* jarien = me->FindNearestCreature(NPC_JARIEN, 200.f))
+ {
+ jarien->AI()->DoAction(ACTION_PARTNER_DEAD);
+ }
+
+ me->SummonCreature(NPC_SPIRIT_OF_SOTHOS, me->GetPosition());
+
+ if (_jarienDied)
+ {
+ HandleBothDead(me, false, killer);
+ }
+ }
+
+ void DoAction(int32 action) override
+ {
+ if (action == ACTION_PARTNER_DEAD)
+ {
+ me->SetFullHealth();
+ DoCastSelf(SPELL_VENGEANCE);
+ Talk(EMOTE_SOTHOS_VENGEANCE);
+ _jarienDied = true;
+ }
+ }
+
+ void EnterCombat(Unit* /*who*/) override
+ {
+ _EnterCombat();
+ _scheduler.Schedule(10s, [this](TaskContext context)
+ {
+ DoCastAOE(SPELL_FEAR);
+ context.Repeat(18s, 20s);
+ })
+ .Schedule(9s, [this](TaskContext context)
+ {
+ DoCastAOE(SPELL_SHADOW_BOLT);
+ context.Repeat(10s, 11s);
+ })
+ .Schedule(15s, [this](TaskContext context)
+ {
+ DoCastRandomTarget(SPELL_SHIELD_CHARGE);
+ context.Repeat();
+ })
+ .Schedule(3s, [this](TaskContext context)
+ {
+ DoCastSelf(SPELL_SHIELD_BLOCK);
+ context.Repeat(10s, 12s);
+ })
+ .Schedule(4s, [this](TaskContext context)
+ {
+ DoCastVictim(SPELL_SHIELD_SLAM);
+ context.Repeat(14s);
+ });
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim() && _phase != PHASE_TALK)
+ {
+ return;
+ }
+
+ _scheduler.Update(diff, [this]
+ {
+ DoMeleeAttackIfReady();
+ });
+ }
+
+ protected:
+ TaskScheduler _scheduler;
+ uint8 _phase;
+ bool _talked;
+ bool _jarienDied;
+};
+
+void AddSC_boss_jarien_and_sothos()
+{
+ RegisterStratholmeCreatureAI(boss_jarien);
+ RegisterStratholmeCreatureAI(boss_sothos);
+}
diff --git a/src/server/scripts/EasternKingdoms/Stratholme/stratholme.h b/src/server/scripts/EasternKingdoms/Stratholme/stratholme.h
index 1f94dc2ce..c51eb32ec 100644
--- a/src/server/scripts/EasternKingdoms/Stratholme/stratholme.h
+++ b/src/server/scripts/EasternKingdoms/Stratholme/stratholme.h
@@ -33,6 +33,8 @@ enum DataTypes
DATA_BARON_RUN_NONE = 0,
DATA_BARON_RUN_GATE = 1,
+ DATA_JARIEN = 2,
+ DATA_SOTHOS = 3
};
enum CreatureIds
@@ -47,6 +49,10 @@ enum CreatureIds
NPC_PLAGUED_RAT = 10441,
NPC_PLAGUED_INSECT = 10461,
NPC_PLAGUED_MAGGOT = 10536,
+ NPC_JARIEN = 16101,
+ NPC_SOTHOS = 16102,
+ NPC_SPIRIT_OF_JARIEN = 16103,
+ NPC_SPIRIT_OF_SOTHOS = 16104
};
enum GameobjectIds
@@ -64,6 +70,7 @@ enum GameobjectIds
GO_PORT_TRAP_GATE_2 = 175350, // Gate trap scarlet side
GO_PORT_TRAP_GATE_3 = 175355, // Gate trap undead side
GO_PORT_TRAP_GATE_4 = 175354,
+ GO_JARIEN_AND_SOTHOS_HEIRLOOMS = 181083
};
enum MiscIds
@@ -98,4 +105,6 @@ inline AI* GetStratholmeAI(T* obj)
return GetInstanceAI(obj, StratholmeScriptName);
}
+#define RegisterStratholmeCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetStratholmeAI)
+
#endif
diff --git a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp
index 40ab731a0..58cc5fa7f 100644
--- a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp
+++ b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp
@@ -100,6 +100,7 @@ void AddSC_boss_darkmaster_gandling();
void AddSC_instance_scholomance(); //Scholomance
void AddSC_instance_shadowfang_keep(); //Shadowfang keep
void AddSC_boss_baroness_anastari();
+void AddSC_boss_jarien_and_sothos();
void AddSC_instance_stratholme(); //Stratholme
void AddSC_instance_sunken_temple(); //Sunken Temple
void AddSC_instance_sunwell_plateau(); //Sunwell Plateau
@@ -242,6 +243,7 @@ void AddEasternKingdomsScripts()
AddSC_instance_scholomance(); //Scholomance
AddSC_instance_shadowfang_keep(); //Shadowfang keep
AddSC_boss_baroness_anastari();
+ AddSC_boss_jarien_and_sothos();
AddSC_instance_stratholme(); //Stratholme
AddSC_instance_sunken_temple(); //Sunken Temple
AddSC_instance_sunwell_plateau(); //Sunwell Plateau