[Quest] The Balance of Light and Shadow

things i dislike:

  • the Archers will eventually start shooting the player because of ThreatAssist. The aggro-dump on each wave-spawn is a huge band-aid-fix

  • there are some texts missing, that i could not find in videos

  • the regenerative Spell used by Eris Havenfire has some really weired targeting. It is commented out for now…

  • sometimes the skeleton-soldiers won’t attack (out of range issue?)

  • the Spawn, Stats and Spells for Eris could probably be done better by using sniffs … that i don’t have

i used videos as a reference

code:

[SPOILER]

[CODE]— a/src/server/scripts/EasternKingdoms/eastern_plaguelands.cpp

+++ b/src/server/scripts/EasternKingdoms/eastern_plaguelands.cpp

@@ -191,6 +191,382 @@ public:

};

+/*######

+## npc_eris_heavenfire

+######*/

+enum BalanceOfLightAndShadow

+{

  • SPELL_BLESSING_OF_NORDRASSIL = 23108,

  • SPELL_DEATH_DOOR = 23127,

  • NPC_INJURED_PEASANT = 14484,

  • // NPC_PLAGUED_PEASANT = 14485,

  • NPC_SCOURGE_ARCHER = 14489,

  • NPC_SCOURGE_FOOTSOLDIER = 14486,

  • QUEST_BALANCE_OF_LIGHT_AND_SHADOW = 7622

+};

+float bolas_coords[13][4] =

+{

  • // Peasant Spawn Rectangle UpperLeft

  • {3350.240234, -3049.189941, 164.775314, 2.03},

  • // Peasant Spawn Rectangle LowerRight

  • {3368.810059, -3053.790039, 166.264008, 0.0},

  • // Peasant Destination Area Center

  • {3330.746826, -2974.629150, 160.388611, 0.0},

  • // Footsoldiers

  • {3347.603271, -3045.536377, 164.029877, 1.814429},

  • {3363.609131, -3037.187256, 163.541885, 2.277649},

  • {3349.105469, -3056.500977, 168.079468, 1.857460},

  • // Archer

  • {3347.865234, -3070.707275, 177.881882, 1.645396},

  • {3357.144287, -3063.327637, 172.499222, 1.841747},

  • {3371.682373, -3067.965332, 175.233582, 2.144123},

  • {3379.904053, -3059.370117, 181.981873, 2.646778},

  • {3334.646973, -3053.084717, 174.101074, 0.400536},

  • {3368.005371, -3022.475830, 171.617966, 4.268625},

  • {3327.000244, -3021.307861, 170.578796, 5.584163}

+};

+#define SAY_SPAWN1 “The Scourge is uppon us! Run! Run for your lives!”

+#define SAY_SPAWN2 “Please help us! The Prince has gone mad!”

+#define SAY_SPAWN3 “Seek sanctuary in Hearthglen! It is our only hope!”

+// #define SAY_SPAWN4 “”

+// #define SAY_SPAWN5 “”

+#define SAY_SAVED1 “Thank you, kind stranger. May your heroism never be forgotten.”

+#define SAY_SAVED2 “The power of the light is truly great and merciful.”

+#define SAY_SAVED3 “Stranger, find the fallen Prince Menethil and end his reign of terror.”

+#define SAY_DEATH1 “Death take me! I cannot go on! I have nothing left…”

+#define SAY_DEATH2 “Should I live through this, I shall make it my live’s sole ambition to destroy Arthas…”

+#define SAY_DEATH3 “The pain is unbearable!”

+#define SAY_DEATH4 “I won’t make it… go… go on without me…”

+#define SAY_EMPOWER “Be healed!”

+#define SAY_COMPLETE “We are saved! The peasants have escaped the Scourge!”

+// #define SAY_FAIL “”

+class npc_fleeing_peasant : public CreatureScript

+{

+public:

  • npc_fleeing_peasant() : CreatureScript(“npc_fleeing_peasant”) { }

  • struct npc_fleeing_peasantAI : public ScriptedAI

  • {

  • public:

  •    npc_fleeing_peasantAI(Creature *c) : ScriptedAI(c) {}
    
  •    uint16 sayTimer;
    
  •    uint8  sayStep;
    
  •    void Reset()
    
  •    {
    
  •        sayTimer = 0;
    
  •        me->SetUnitMovementFlags(MOVEMENTFLAG_WALKING);
    
  •        me->SetSpeed(MOVE_WALK, 0.45f);
    
  •        me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
    
  •        me->SetReactState(REACT_PASSIVE);
    
  •        me->GetMotionMaster()->MovePoint(0, bolas_coords[2][0] + irand(-3, 3),  bolas_coords[2][1] + irand(-3, 3), bolas_coords[2][2]);
    
  •    }
    
  •    void StartOutro()
    
  •    {
    
  •        sayStep = 1;
    
  •    }
    
  •    void UpdateAI(const uint32 diff)
    
  •    {
    
  •        if (!sayStep)
    
  •            return;
    
  •        if (sayTimer > diff)
    
  •        {
    
  •            sayTimer -= diff;
    
  •            return;
    
  •        }
    
  •        else
    
  •            sayTimer = 3000;
    
  •        switch (sayStep)
    
  •        {
    
  •            case 1: me->MonsterSay(SAY_SAVED1, LANG_UNIVERSAL, 0); sayStep++; break;
    
  •            case 2: me->MonsterSay(SAY_SAVED2, LANG_UNIVERSAL, 0); sayStep++; break;
    
  •            case 3: me->MonsterSay(SAY_SAVED3, LANG_UNIVERSAL, 0); sayStep++; break;
    
  •            default:                                                          break;
    
  •        }
    
  •    }
    
  •    void DamageTaken(Unit* /*pKiller*/, uint32 &damage)
    
  •    {
    
  •        if (damage < me->GetHealth())
    
  •            return;
    
  •        if (Creature* e = me->FindNearestCreature(14494, 200.0f))
    
  •            e->AI()->DoAction(1);
    
  •        switch (urand(0, 6))
    
  •        {
    
  •            case 0: me->MonsterSay(SAY_DEATH1, LANG_UNIVERSAL, 0); break;
    
  •            case 1: me->MonsterSay(SAY_DEATH2, LANG_UNIVERSAL, 0); break;
    
  •            case 2: me->MonsterSay(SAY_DEATH3, LANG_UNIVERSAL, 0); break;
    
  •            case 3: me->MonsterSay(SAY_DEATH4, LANG_UNIVERSAL, 0); break;
    
  •            default:                                               break;
    
  •        }
    
  •    }
    
  • };

  • CreatureAI* GetAI(Creature* pCreature) const

  • {

  •    return new npc_fleeing_peasantAI(pCreature);
    
  • }

+};

+class npc_eris_havenfire : public CreatureScript

+{

+public:

  • npc_eris_havenfire() : CreatureScript(“npc_eris_havenfire”) { }

  • struct npc_eris_havenfireAI : public ScriptedAI

  • {

  • public:

  •    npc_eris_havenfireAI(Creature *c) : ScriptedAI(c) {}
    
  •    bool    eventActive;
    
  •    uint32  waveTimer;
    
  •    uint8   waveCount;
    
  •    uint8   saveCount;
    
  •    uint8   killCount;
    
  •    uint32  soldierTimer[3];
    
  •    Player* pPriest;
    
  •    void Reset()
    
  •    {
    
  •        pPriest = NULL;
    
  •        eventActive = false;
    
  •        soldierTimer = {0, 0 ,0};
    
  •        waveTimer = 0;
    
  •        waveCount = 0;
    
  •        saveCount = 0;
    
  •        killCount = 0;
    
  •    }
    
  •    void DoAction(const int32 action)
    
  •    {
    
  •        if (!eventActive || !pPriest || !pPriest->IsInWorld())
    
  •        {
    
  •            pPriest = NULL;
    
  •            eventActive = false;
    
  •            return;
    
  •        }
    
  •        switch (action)
    
  •        {
    
  •            case 0: saveCount++; break;
    
  •            case 1: killCount++; break;
    
  •            default:             break;
    
  •        }
    
  •        if (killCount >= 15)
    
  •        {
    
  •            // FailQuest
    
  •            //me->MonsterYell(SAY_FAIL, LANG_UNIVERSAL, 0);
    
  •            pPriest->FailQuest(QUEST_BALANCE_OF_LIGHT_AND_SHADOW);
    
  •            eventActive = false;
    
  •            pPriest = NULL;
    
  •        }
    
  •        else if (saveCount >= 50)
    
  •        {
    
  •            // award Quest
    
  •            me->MonsterYell(SAY_COMPLETE, LANG_UNIVERSAL, 0);
    
  •            pPriest->CompleteQuest(QUEST_BALANCE_OF_LIGHT_AND_SHADOW);
    
  •            eventActive = false;
    
  •            pPriest = NULL;
    
  •        }
    
  •    }
    
  •    void StartEvent(Player* pPlayer)
    
  •    {
    
  •        if (eventActive)
    
  •            return;
    
  •        // init vars
    
  •        eventActive = true;
    
  •        soldierTimer = {72000, 72000, 72000};
    
  •        waveTimer = 10000;
    
  •        waveCount = 0;
    
  •        saveCount = 0;
    
  •        killCount = 0;
    
  •        pPriest = pPlayer;
    
  •        // spawn archer
    
  •        for (uint8 i = 6; i < 13; i++)
    
  •        {
    
  •            Position pos;
    
  •            pos.m_positionX = bolas_coords[i][0];
    
  •            pos.m_positionY = bolas_coords[i][1];
    
  •            pos.m_positionZ = bolas_coords[i][2];
    
  •            pos.m_orientation = bolas_coords[i][3];
    
  •            if (Creature* p = me->SummonCreature(NPC_SCOURGE_ARCHER, pos, TEMPSUMMON_TIMED_DESPAWN, 5*MINUTE*IN_MILLISECONDS))
    
  •            {
    
  •                p->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
    
  •                p->SetReactState(REACT_AGGRESSIVE);
    
  •            }
    
  •        }
    
  •    }
    
  •    void MoveInLineOfSight(Unit* who)
    
  •    {
    
  •        /// out of range or already registered
    
  •        if (!me->IsWithinDist(who, 10.0f) || !who->ToCreature() || who->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
    
  •            return;
    
  •        // irrelevant creature
    
  •        Creature* p = who->ToCreature();
    
  •        if (p->GetEntry() != NPC_INJURED_PEASANT)
    
  •            return;
    
  •        p->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
    
  •        p->ForcedDespawn(10000);
    
  •        // priest lost or event not active
    
  •        if (!eventActive || !pPriest || !pPriest->IsInWorld())
    
  •        {
    
  •            pPriest = NULL;
    
  •            eventActive = false;
    
  •            return;
    
  •        }
    
  •        // register survivor
    
  •        DoAction(0);
    
  •        if (saveCount == 50)
    
  •            CAST_AI(npc_fleeing_peasant::npc_fleeing_peasantAI, p->AI())->StartOutro();
    
  •    }
    
  •    void UpdateAI(const uint32 diff)
    
  •    {
    
  •        if (!eventActive || !pPriest || !pPriest->IsInWorld())
    
  •        {
    
  •            pPriest = NULL;
    
  •            eventActive = false;
    
  •            return;
    
  •        }
    

+/* strange targeting

  •        if (!pPriest->HasAuraEffect(SPELL_BLESSING_OF_NORDRASSIL, EFFECT_0) && (100.0f * pPriest->GetPower(POWER_MANA) / pPriest->GetMaxPower(POWER_MANA)) < 35.0f)
    
  •        {
    
  •            me->CastSpell(pPriest, SPELL_BLESSING_OF_NORDRASSIL, true);
    
  •            me->MonsterYell(SAY_EMPOWER, LANG_UNIVERSAL, 0);
    
  •        }
    

+*/

  •        if (soldierTimer[0] <= diff)
    
  •        {
    
  •            Position pos;
    
  •            pos.m_positionX = bolas_coords[3][0];
    
  •            pos.m_positionY = bolas_coords[3][1];
    
  •            pos.m_positionZ = bolas_coords[3][2];
    
  •            pos.m_orientation = bolas_coords[3][3];
    
  •            for (uint8 i = 0; (waveCount - 1) >= i; i++)
    
  •                if (Creature* s = me->SummonCreature(NPC_SCOURGE_FOOTSOLDIER, pos, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000))
    
  •                    s->getThreatManager().addThreat(pPriest, 1.0f);
    
  •            soldierTimer[0] = 20000 + urand(0, 20000);
    
  •        }
    
  •        else
    
  •            soldierTimer[0] -= diff;
    
  •        if (soldierTimer[1] <= diff)
    
  •        {
    
  •            Position pos;
    
  •            pos.m_positionX = bolas_coords[4][0];
    
  •            pos.m_positionY = bolas_coords[4][1];
    
  •            pos.m_positionZ = bolas_coords[4][2];
    
  •            pos.m_orientation = bolas_coords[4][3];
    
  •            for (uint8 i = 0; (waveCount - 1) >= i; i++)
    
  •                if (Creature* s = me->SummonCreature(NPC_SCOURGE_FOOTSOLDIER, pos, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000))
    
  •                    s->getThreatManager().addThreat(pPriest, 1.0f);
    
  •            soldierTimer[1] = 20000 + urand(0, 20000);
    
  •        }
    
  •        else
    
  •            soldierTimer[1] -= diff;
    
  •        if (soldierTimer[2] <= diff)
    
  •        {
    
  •            Position pos;
    
  •            pos.m_positionX = bolas_coords[5][0];
    
  •            pos.m_positionY = bolas_coords[5][1];
    
  •            pos.m_positionZ = bolas_coords[5][2];
    
  •            pos.m_orientation = bolas_coords[5][3];
    
  •            for (uint8 i = 0; (waveCount - 1) >= i; i++)
    
  •                if (Creature* s = me->SummonCreature(NPC_SCOURGE_FOOTSOLDIER, pos, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000))
    
  •                    s->getThreatManager().addThreat(pPriest, 1.0f);
    
  •            soldierTimer[2] = 20000 + urand(0, 20000);
    
  •        }
    
  •        else
    
  •            soldierTimer[2] -= diff;
    
  •        if (waveTimer <= diff)
    
  •        {
    
  •            // 6*12 => 72 Peasants sent, 14 may die => 58 are underway
    
  •            if (waveCount >= 6)
    
  •                return;
    
  •            pPriest->getHostileRefManager().deleteReferences();
    
  •            for (uint8 i = 0; i <= 12; i++)
    
  •            {
    
  •                Position pos;
    
  •                pos.m_positionX = bolas_coords[0][0] + (bolas_coords[1][0] - bolas_coords[0][0]) * urand(0, 100) / 100.0f;
    
  •                pos.m_positionY = bolas_coords[0][1] + (bolas_coords[1][1] - bolas_coords[0][1]) * urand(0, 100) / 100.0f;
    
  •                pos.m_positionZ = bolas_coords[0][2];
    
  •                pos.m_orientation = bolas_coords[0][3];
    
  •                if (Creature* p = me->SummonCreature(NPC_INJURED_PEASANT, pos, TEMPSUMMON_MANUAL_DESPAWN, 0))
    
  •                {
    
  •                    p->setFaction(pPriest->getFaction());
    
  •                    if (i == 0)
    
  •                    {
    
  •                        switch (waveCount)
    
  •                        {
    
  •                            case 0: p->MonsterYell(SAY_SPAWN1, LANG_UNIVERSAL, 0); break;
    
  •                            case 1: p->MonsterYell(SAY_SPAWN2, LANG_UNIVERSAL, 0); break;
    
  •                            //case 2:                                                break;
    
  •                            case 3: p->MonsterYell(SAY_SPAWN3, LANG_UNIVERSAL, 0); break;
    
  •                            //case 4:                                                break;
    
  •                            //case 5:                                                break;
    
  •                            default:                                               break;
    
  •                        }
    
  •                    }
    
  •                    if (!urand(0, 3))
    
  •                        p->CastSpell(p, SPELL_DEATH_DOOR, true);
    
  •                }
    
  •            }
    
  •            waveCount++;
    
  •            waveTimer = 60000;
    
  •        }
    
  •        else
    
  •            waveTimer -= diff;
    
  •    }
    
  • };

  • bool OnQuestAccept(Player* pPlayer, Creature* pCreature, Quest const *quest)

  • {

  •    if (quest->GetQuestId() == QUEST_BALANCE_OF_LIGHT_AND_SHADOW)
    
  •        CAST_AI(npc_eris_havenfire::npc_eris_havenfireAI, pCreature->AI())->StartEvent(pPlayer);
    
  • }

  • CreatureAI* GetAI(Creature* pCreature) const

  • {

  •    return new npc_eris_havenfireAI(pCreature);
    
  • }

+};

void AddSC_eastern_plaguelands()

{

@@ -198,4 +574,6 @@ void AddSC_eastern_plaguelands()

 new npc_augustus_the_touched();

 new npc_darrowshire_spirit();

 new npc_tirion_fordring();
  • new npc_eris_havenfire();

  • new npc_fleeing_peasant();

}[/CODE][/SPOILER]

sql:


SET @ArcherEntry  := 14489;

SET @ErisEntry    := 14494;

SET @PeasantEntry := 14484;


-- spawn Eris

DELETE FROM creature WHERE id = @ErisEntry;

INSERT INTO creature (id, map, position_x, position_y, position_z, orientation) VALUES (@ErisEntry, 0, 3325.712646, -2995.190918, 164.270721, 0.121896);


-- stealth Eris

SET @eris := (SELECT guid FROM creature WHERE id = @ErisEntry);

DELETE FROM creature_addon WHERE guid = @eris;

INSERT INTO creature_addon (guid, auras) VALUES (@eris, '32648 0');


-- update involved creatures

UPDATE creature_template SET ScriptName = 'npc_fleeing_peasant', RegenHealth = 0 WHERE entry = @PeasantEntry;

UPDATE creature_template SET ScriptName = 'npc_eris_havenfire', AIName = '' WHERE entry = @ErisEntry;

UPDATE creature_template SET AIName = 'ArcherAI', equipment_id = 14489 WHERE entry = @ArcherEntry;


-- hand out weapons

DELETE FROM creature_equip_template WHERE entry = @ArcherEntry;

INSERT INTO creature_equip_template VALUES (@ArcherEntry, 0, 0, 11304);

Very nice work, you have a clean script without hacks, /emoticons/default_wink.png

You should correct this so people can compile:


        void Reset()

        {

            pPriest = NULL;

            eventActive = false;

This -->    soldierTimer = {0, 0 ,0};

            waveTimer = 0;

            waveCount = 0;

            saveCount = 0;

            killCount = 0;

        }

       void StartEvent(Player* pPlayer)

        {

            if (eventActive)

                return;


            // init vars

            eventActive = true;

This -->    soldierTimer = {72000, 72000, 72000};

            waveTimer = 10000;

            waveCount = 0;

            saveCount = 0;

            killCount = 0;

            pPriest = pPlayer;


            // spawn archer

            for (uint8 i = 6; i < 13; i++)

            {

                Position pos;

                pos.m_positionX = bolas_coords[i][0];

                pos.m_positionY = bolas_coords[i][1];

                pos.m_positionZ = bolas_coords[i][2];

                pos.m_orientation = bolas_coords[i][3];

                if (Creature* p = me->SummonCreature(NPC_SCOURGE_ARCHER, pos, TEMPSUMMON_TIMED_DESPAWN, 5*MINUTE*IN_MILLISECONDS))

                {

                    p->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);

                    p->SetReactState(REACT_AGGRESSIVE);

                }

            }

        }

Regards and Thanks for sharing.

Script may have some potential, but it would need a hugh cleanup for standards and some hacks, you can start by reading http://www.trinitycore.org/f/index.php?/topic/6-trinitycore-developing-standards/, i could help out if you’d like.