diff options
Diffstat (limited to 'src/server/scripts')
| -rw-r--r-- | src/server/scripts/Outland/blades_edge_mountains.cpp | 561 | 
1 files changed, 561 insertions, 0 deletions
diff --git a/src/server/scripts/Outland/blades_edge_mountains.cpp b/src/server/scripts/Outland/blades_edge_mountains.cpp index d78d4afbfb8..97ce9f45430 100644 --- a/src/server/scripts/Outland/blades_edge_mountains.cpp +++ b/src/server/scripts/Outland/blades_edge_mountains.cpp @@ -562,6 +562,564 @@ class go_thunderspike : public GameObjectScript          }  }; +enum SimonGame +{ +    NPC_SIMON_BUNNY                 = 22923, +    NPC_APEXIS_GUARDIAN             = 22275, + +    GO_APEXIS_RELIC                 = 185890, +    GO_APEXIS_MONUMENT              = 185944, +    GO_AURA_BLUE                    = 185872, +    GO_AURA_GREEN                   = 185873, +    GO_AURA_RED                     = 185874, +    GO_AURA_YELLOW                  = 185875, + +    GO_BLUE_CLUSTER_DISPLAY         = 7369, +    GO_GREEN_CLUSTER_DISPLAY        = 7371, +    GO_RED_CLUSTER_DISPLAY          = 7373, +    GO_YELLOW_CLUSTER_DISPLAY       = 7375, +    GO_BLUE_CLUSTER_DISPLAY_LARGE   = 7364, +    GO_GREEN_CLUSTER_DISPLAY_LARGE  = 7365, +    GO_RED_CLUSTER_DISPLAY_LARGE    = 7366, +    GO_YELLOW_CLUSTER_DISPLAY_LARGE = 7367, + +    SPELL_PRE_GAME_BLUE             = 40176, +    SPELL_PRE_GAME_GREEN            = 40177, +    SPELL_PRE_GAME_RED              = 40178, +    SPELL_PRE_GAME_YELLOW           = 40179, +    SPELL_VISUAL_BLUE               = 40244, +    SPELL_VISUAL_GREEN              = 40245, +    SPELL_VISUAL_RED                = 40246, +    SPELL_VISUAL_YELLOW             = 40247, + +    SOUND_BLUE                      = 11588, +    SOUND_GREEN                     = 11589, +    SOUND_RED                       = 11590, +    SOUND_YELLOW                    = 11591, +    SOUND_DISABLE_NODE              = 11758, + +    SPELL_AUDIBLE_GAME_TICK         = 40391, +    SPELL_VISUAL_START_PLAYER_LEVEL = 40436, +    SPELL_VISUAL_START_AI_LEVEL     = 40387, + +    SPELL_BAD_PRESS_TRIGGER         = 41241, +    SPELL_BAD_PRESS_DAMAGE          = 40065, +    SPELL_REWARD_BUFF_1             = 40310, +    SPELL_REWARD_BUFF_2             = 40311, +    SPELL_REWARD_BUFF_3             = 40312, +}; + +enum SimonEvents +{ +    EVENT_SIMON_SETUP_PRE_GAME         = 1, +    EVENT_SIMON_PLAY_SEQUENCE          = 2, +    EVENT_SIMON_RESET_CLUSTERS         = 3, +    EVENT_SIMON_PERIODIC_PLAYER_CHECK  = 4, +    EVENT_SIMON_TOO_LONG_TIME          = 5, +    EVENT_SIMON_GAME_TICK              = 6, +    EVENT_SIMON_ROUND_FINISHED         = 7, + +    ACTION_SIMON_CORRECT_FULL_SEQUENCE = 8, +    ACTION_SIMON_WRONG_SEQUENCE        = 9, +    ACTION_SIMON_ROUND_FINISHED        = 10, +}; + +enum SimonColors +{ +    SIMON_BLUE          = 0, +    SIMON_RED           = 1, +    SIMON_GREEN         = 2, +    SIMON_YELLOW        = 3, +    SIMON_MAX_COLORS    = 4, +}; + +class npc_simon_bunny : public CreatureScript +{ +    public: +        npc_simon_bunny() : CreatureScript("npc_simon_bunny") { } + +        struct npc_simon_bunnyAI : public ScriptedAI +        { +            npc_simon_bunnyAI(Creature* creature) : ScriptedAI(creature) { } + +            bool large; +            bool listening; +            uint8 gameLevel; +            uint8 fails; +            uint8 gameTicks; +            uint64 playerGUID; +            uint32 clusterIds[SIMON_MAX_COLORS]; +            float zCoordCorrection; +            float searchDistance; +            EventMap _events; +            std::list<uint8> colorSequence, playableSequence, playerSequence; + +            void UpdateAI(const uint32 diff) +            { +                _events.Update(diff); + +                switch(_events.ExecuteEvent()) +                { +                    case EVENT_SIMON_PERIODIC_PLAYER_CHECK: +                        if (!CheckPlayer()) +                            ResetNode(); +                        else +                            _events.ScheduleEvent(EVENT_SIMON_PERIODIC_PLAYER_CHECK, 2000); +                        break; +                    case EVENT_SIMON_SETUP_PRE_GAME: +                        SetUpPreGame(); +                        _events.CancelEvent(EVENT_SIMON_GAME_TICK); +                        _events.ScheduleEvent(EVENT_SIMON_PLAY_SEQUENCE, 1000); +                        break; +                    case EVENT_SIMON_PLAY_SEQUENCE: +                        if (!playableSequence.empty()) +                        { +                            PlayNextColor(); +                            _events.ScheduleEvent(EVENT_SIMON_PLAY_SEQUENCE, 1500); +                        } +                        else +                        { +                            listening = true; +                            DoCast(SPELL_VISUAL_START_PLAYER_LEVEL); +                            playerSequence.clear(); +                            PrepareClusters(); +                            gameTicks = 0; +                            _events.ScheduleEvent(EVENT_SIMON_GAME_TICK, 3000); +                        } +                        break; +                    case EVENT_SIMON_GAME_TICK: +                        DoCast(SPELL_AUDIBLE_GAME_TICK); + +                        if (gameTicks > gameLevel) +                            _events.ScheduleEvent(EVENT_SIMON_TOO_LONG_TIME, 500); +                        else +                            _events.ScheduleEvent(EVENT_SIMON_GAME_TICK, 3000); +                        gameTicks++; +                        break; +                    case EVENT_SIMON_RESET_CLUSTERS: +                        PrepareClusters(true); +                        break; +                    case EVENT_SIMON_TOO_LONG_TIME: +                        DoAction(ACTION_SIMON_WRONG_SEQUENCE); +                        break; +                    case EVENT_SIMON_ROUND_FINISHED: +                        DoAction(ACTION_SIMON_ROUND_FINISHED); +                        break; +                } +            } + +            void DoAction(const int32 action) +            { +                switch (action) +                { +                    case ACTION_SIMON_ROUND_FINISHED: +                        listening = false; +                        DoCast(SPELL_VISUAL_START_AI_LEVEL); +                        GiveRewardForLevel(gameLevel); +                        _events.CancelEventGroup(0); +                        if (gameLevel == 10) +                            ResetNode(); +                        else +                            _events.ScheduleEvent(EVENT_SIMON_SETUP_PRE_GAME, 1000); +                        break; +                    case ACTION_SIMON_CORRECT_FULL_SEQUENCE: +                        gameLevel++; +                        DoAction(ACTION_SIMON_ROUND_FINISHED); +                        break; +                    case ACTION_SIMON_WRONG_SEQUENCE: +                        GivePunishment(); +                        DoAction(ACTION_SIMON_ROUND_FINISHED); +                        break; +                } +            } + +            // Called by color clusters script (go_simon_cluster) and used for knowing the button pressed by player +            void SetData(uint32 type, uint32 /*data*/) +            { +                if (!listening) +                    return; + +                uint8 pressedColor; + +                if (type == clusterIds[SIMON_RED]) +                    pressedColor = SIMON_RED; +                else if (type == clusterIds[SIMON_BLUE]) +                    pressedColor = SIMON_BLUE; +                else if (type == clusterIds[SIMON_GREEN]) +                    pressedColor = SIMON_GREEN; +                else if (type == clusterIds[SIMON_YELLOW]) +                    pressedColor = SIMON_YELLOW; + +                PlayColor(pressedColor); +                playerSequence.push_back(pressedColor); +                _events.ScheduleEvent(EVENT_SIMON_RESET_CLUSTERS, 500); +                CheckPlayerSequence(); +            } + +            // Used for getting involved player guid. Parameter id is used for defining if is a large(Monument) or small(Relic) node +            void SetGUID(uint64 guid, int32 id) +            { +                me->SetFlying(true); + +                large = (bool)id; +                playerGUID = guid; +                StartGame(); +            } + +            /* +            Resets all variables and also find the ids of the four closests color clusters, since every simon +            node have diferent ids for clusters this is absolutely NECESSARY. +            */ +            void StartGame() +            { +                listening = false; +                gameLevel = 0; +                fails = 0; +                gameTicks = 0; +                zCoordCorrection = large ? 8.0f : 2.75f; +                searchDistance = large ? 13.0f : 5.0f; +                colorSequence.clear(); +                playableSequence.clear(); +                playerSequence.clear(); +                me->SetFloatValue(OBJECT_FIELD_SCALE_X, large ? 2 : 1); + +                std::list<WorldObject*> ClusterList; +                Trinity::AllWorldObjectsInRange objects(me, searchDistance); +                Trinity::WorldObjectListSearcher<Trinity::AllWorldObjectsInRange> searcher(me, ClusterList, objects); +                me->VisitNearbyObject(searchDistance, searcher); + +                for (std::list<WorldObject*>::const_iterator i = ClusterList.begin(); i != ClusterList.end(); ++i) +                { +                    if (GameObject* go = (*i)->ToGameObject()) +                    { +                        // We are checking for displayid because all simon nodes have 4 clusters with different entries +                        if (large) +                        { +                            switch (go->GetGOInfo()->displayId) +                            { +                                case GO_BLUE_CLUSTER_DISPLAY_LARGE: clusterIds[SIMON_BLUE] = go->GetEntry(); break; +                                case GO_RED_CLUSTER_DISPLAY_LARGE: clusterIds[SIMON_RED] = go->GetEntry(); break; +                                case GO_GREEN_CLUSTER_DISPLAY_LARGE: clusterIds[SIMON_GREEN] = go->GetEntry(); break; +                                case GO_YELLOW_CLUSTER_DISPLAY_LARGE: clusterIds[SIMON_YELLOW] = go->GetEntry(); break; +                            } +                        } +                        else +                        { +                            switch (go->GetGOInfo()->displayId) +                            { +                                case GO_BLUE_CLUSTER_DISPLAY: clusterIds[SIMON_BLUE] = go->GetEntry(); break; +                                case GO_RED_CLUSTER_DISPLAY: clusterIds[SIMON_RED] = go->GetEntry(); break; +                                case GO_GREEN_CLUSTER_DISPLAY: clusterIds[SIMON_GREEN] = go->GetEntry(); break; +                                case GO_YELLOW_CLUSTER_DISPLAY: clusterIds[SIMON_YELLOW] = go->GetEntry(); break; +                            } +                        } +                    } +                } + +                _events.Reset(); +                _events.ScheduleEvent(EVENT_SIMON_ROUND_FINISHED, 1000); +                _events.ScheduleEvent(EVENT_SIMON_PERIODIC_PLAYER_CHECK, 2000); + +                if (GameObject* relic = me->FindNearestGameObject(large ? GO_APEXIS_MONUMENT : GO_APEXIS_RELIC, searchDistance)) +                    relic->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); +            } + +            // Called when despawning the bunny. Sets all the node GOs to their default states. +            void ResetNode() +            { +                DoPlaySoundToSet(me, SOUND_DISABLE_NODE); + +                for (uint32 clusterId = SIMON_BLUE; clusterId < SIMON_MAX_COLORS; clusterId++) +                    if (GameObject* cluster = me->FindNearestGameObject(clusterIds[clusterId], searchDistance)) +                        cluster->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + +                for (uint32 auraId = GO_AURA_BLUE; auraId <= GO_AURA_YELLOW; auraId++) +                    if (GameObject* auraGo = me->FindNearestGameObject(auraId, searchDistance)) +                        auraGo->RemoveFromWorld(); + +                if (GameObject* relic = me->FindNearestGameObject(large ? GO_APEXIS_MONUMENT : GO_APEXIS_RELIC, searchDistance)) +                    relic->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + +                me->ForcedDespawn(1000); +            } + +            /* +            Called on every button click of player. Adds the clicked color to the player created sequence and +            checks if it corresponds to the AI created sequence. If so, incremente gameLevel and start a new +            round, if not, give punishment and restart current level. +            */ +            void CheckPlayerSequence() +            { +                bool correct = true; +                if (playerSequence.size() <= colorSequence.size()) +                    for (std::list<uint8>::const_iterator i = playerSequence.begin(), j = colorSequence.begin(); i != playerSequence.end(); ++i, ++j) +                        if ((*i) != (*j)) +                            correct = false; + +                if (correct && (playerSequence.size() == colorSequence.size())) +                    DoAction(ACTION_SIMON_CORRECT_FULL_SEQUENCE); +                else if (!correct) +                    DoAction(ACTION_SIMON_WRONG_SEQUENCE); +            } + +            /* +            Generates a random sequence of colors depending on the gameLevel. We also copy this sequence to +            the playableSequence wich will be used when playing the sequence to the player. +            */ +            void GenerateColorSequence() +            { +                colorSequence.clear(); +                for (uint8 i = 0; i <= gameLevel; i++) +                    colorSequence.push_back(RAND(SIMON_BLUE, SIMON_RED, SIMON_GREEN, SIMON_YELLOW)); + +                for (std::list<uint8>::const_iterator i = colorSequence.begin(); i != colorSequence.end(); ++i) +                    playableSequence.push_back(*i); +            } + + +            // Remove any existant glowing auras over clusters and set clusters ready for interating with them. +            void PrepareClusters(bool clustersOnly = false) +            { +                for (uint32 clusterId = SIMON_BLUE; clusterId < SIMON_MAX_COLORS; clusterId++) +                    if (GameObject* cluster = me->FindNearestGameObject(clusterIds[clusterId], searchDistance)) +                        cluster->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + +                if (clustersOnly) +                    return; + +                for (uint32 auraId = GO_AURA_BLUE; auraId <= GO_AURA_YELLOW; auraId++) +                    if (GameObject* auraGo = me->FindNearestGameObject(auraId, searchDistance)) +                        auraGo->RemoveFromWorld(); +            } + +            /* +            Called when AI is playing the sequence for player. We cast the visual spell and then remove the +            casted color from the casting sequence. +            */ +            void PlayNextColor() +            { +                PlayColor(*playableSequence.begin()); +                playableSequence.erase(playableSequence.begin()); +            } + +            // Casts a spell and plays a sound depending on parameter color. +            void PlayColor(uint8 color) +            { +                switch (color) +                { +                    case SIMON_BLUE: +                        DoCast(SPELL_VISUAL_BLUE); +                        DoPlaySoundToSet(me, SOUND_BLUE); +                        break; +                    case SIMON_GREEN: +                        DoCast(SPELL_VISUAL_GREEN); +                        DoPlaySoundToSet(me, SOUND_GREEN); +                        break; +                    case SIMON_RED: +                        DoCast(SPELL_VISUAL_RED); +                        DoPlaySoundToSet(me, SOUND_RED); +                        break; +                    case SIMON_YELLOW: +                        DoCast(SPELL_VISUAL_YELLOW); +                        DoPlaySoundToSet(me, SOUND_YELLOW); +                        break; +                } +            } + +            /* +            Creates the transparent glowing auras on every cluster of this node. +            After calling this function bunny is teleported to the center of the node. +            */ +            void SetUpPreGame() +            { +                for (uint32 clusterId = SIMON_BLUE; clusterId < SIMON_MAX_COLORS; clusterId++) +                { +                    if (GameObject* cluster = me->FindNearestGameObject(clusterIds[clusterId], 2.0f*searchDistance)) +                    { +                        cluster->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + +                        // break since we don't need glowing auras for large clusters +                        if (large) +                            break; + +                        float x, y, z, o; +                        cluster->GetPosition(x, y, z, o); +                        me->NearTeleportTo(x, y, z, o); + +                        uint32 preGameSpellId; +                        if (cluster->GetEntry() == clusterIds[SIMON_RED]) +                            preGameSpellId = SPELL_PRE_GAME_RED; +                        else if (cluster->GetEntry() == clusterIds[SIMON_BLUE]) +                            preGameSpellId = SPELL_PRE_GAME_BLUE; +                        else if (cluster->GetEntry() == clusterIds[SIMON_GREEN]) +                            preGameSpellId = SPELL_PRE_GAME_GREEN; +                        else if (cluster->GetEntry() == clusterIds[SIMON_YELLOW]) +                            preGameSpellId = SPELL_PRE_GAME_YELLOW; +                        else break; + +                        me->CastSpell(cluster, preGameSpellId, true); +                    } +                } + +                if (GameObject* relic = me->FindNearestGameObject(large ? GO_APEXIS_MONUMENT : GO_APEXIS_RELIC, searchDistance)) +                { +                    float x, y, z, o; +                    relic->GetPosition(x, y, z, o); +                    me->NearTeleportTo(x, y, z + zCoordCorrection, o); +                } + +                GenerateColorSequence(); +            } + +            // Handles the spell rewards. The spells also have the QuestCompleteEffect, so quests credits are working. +            void GiveRewardForLevel(uint8 level) +            { +                uint32 rewSpell; +                switch (level) +                { +                    case 6:  +                        if (large) +                            GivePunishment(); +                        else +                            rewSpell = SPELL_REWARD_BUFF_1; +                        break; +                    case 8: +                        rewSpell = SPELL_REWARD_BUFF_2; +                        break; +                    case 10: +                        rewSpell = SPELL_REWARD_BUFF_3; +                        break; +                    default: +                        rewSpell = 0; +                } + +                if (rewSpell) +                    if (Player* player = me->GetPlayer(*me, playerGUID)) +                        DoCast(player, rewSpell, true); +            } + +            /* +            Depending on the number of failed pushes for player the damage of the spell scales, so we first +            cast the spell on the target that hits for 50 and shows the visual and then forces the player +            to cast the damaging spell on it self with the modified basepoints. +            4 fails = death. +            On large nodes punishment and reward are the same, summoning the Apexis Guardian. +            */ +            void GivePunishment() +            { +                if (large) +                { +                    if (Player* player = me->GetPlayer(*me, playerGUID)) +                        if (Creature* guardian = me->SummonCreature(NPC_APEXIS_GUARDIAN, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() - zCoordCorrection, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 20000)) +                            guardian->AI()->AttackStart(player); + +                    ResetNode(); +                } +                else +                { +                    fails++; + +                    if (Player* player = me->GetPlayer(*me, playerGUID)) +                        DoCast(player, SPELL_BAD_PRESS_TRIGGER, true); + +                    if (fails >= 4) +                        ResetNode(); +                } +            } + +            void SpellHitTarget(Unit* target, const SpellInfo* spell) +            { +                // Cast SPELL_BAD_PRESS_DAMAGE with scaled basepoints when the visual hits the target. +                // Need Fix: When SPELL_BAD_PRESS_TRIGGER hits target it triggers spell SPELL_BAD_PRESS_DAMAGE by itself +                // so player gets damage equal to calculated damage  dbc basepoints for SPELL_BAD_PRESS_DAMAGE (~50) +                if (spell->Id == SPELL_BAD_PRESS_TRIGGER) +                { +                    int32 bp = (int32)((float)(fails)*0.33f*target->GetMaxHealth()); +                    target->CastCustomSpell(target, SPELL_BAD_PRESS_DAMAGE, &bp, NULL, NULL, true); +                } +            } + +            // Checks if player has already die or has get too far from the current node +            bool CheckPlayer() +            { +                if (Player* player = me->GetPlayer(*me, playerGUID)) +                { +                    if (player->isDead()) +                        return false; +                    if (player->GetDistance2d(me) >= 2.0f*searchDistance) +                    { +                        GivePunishment(); +                        return false; +                    } +                } +                else +                    return false; + +                return true; +            } +        }; + +        CreatureAI* GetAI(Creature* creature) const +        { +            return new npc_simon_bunnyAI(creature); +        } +}; + +class go_simon_cluster : public GameObjectScript +{ +    public: +        go_simon_cluster() : GameObjectScript("go_simon_cluster") { } + +        bool OnGossipHello(Player* player, GameObject* go) +        { +            if (Creature* bunny = go->FindNearestCreature(NPC_SIMON_BUNNY, 12.0f, true)) +                bunny->AI()->SetData(go->GetEntry(), 0); + +            player->CastSpell(player, go->GetGOInfo()->goober.spellId, true); +            go->AddUse(); +            return true; +        } +}; + +enum ApexisRelic +{ +    QUEST_CRYSTALS            = 11025, +    GOSSIP_TEXT_ID            = 10948, + +    ITEM_APEXIS_SHARD         = 32569, +    SPELL_TAKE_REAGENTS_SOLO  = 41145, +    SPELL_TAKE_REAGENTS_GROUP = 41146, +}; + +class go_apexis_relic : public GameObjectScript +{ +    public: +        go_apexis_relic() : GameObjectScript("go_apexis_relic") { } + +        bool OnGossipHello(Player* player, GameObject* go) +        { +            player->PrepareGossipMenu(go, go->GetGOInfo()->questgiver.gossipID); +            player->SendPreparedGossip(go); +            return true; +        } + +        bool OnGossipSelect(Player* player, GameObject* go, uint32 /*sender*/, uint32 /*action*/) +        { +            player->CLOSE_GOSSIP_MENU(); + +            bool large = (go->GetEntry() == GO_APEXIS_MONUMENT); +            if (player->HasItemCount(ITEM_APEXIS_SHARD, large ? 35 : 1)) +            { +                player->CastSpell(player, large ? SPELL_TAKE_REAGENTS_GROUP : SPELL_TAKE_REAGENTS_SOLO, false); + +                if (Creature* bunny = player->SummonCreature(NPC_SIMON_BUNNY, go->GetPositionX(), go->GetPositionY(), go->GetPositionZ())) +                    bunny->AI()->SetGUID(player->GetGUID(), large); +            } + +            return true; +        } +}; +  void AddSC_blades_edge_mountains()  {      new mobs_bladespire_ogre(); @@ -573,4 +1131,7 @@ void AddSC_blades_edge_mountains()      new npc_bloodmaul_brutebane();      new npc_ogre_brute();      new go_thunderspike(); +    new npc_simon_bunny(); +    new go_simon_cluster(); +    new go_apexis_relic();  }  | 
