diff options
author | Shocker <shocker@freakz.ro> | 2012-01-05 19:14:23 -0800 |
---|---|---|
committer | Shocker <shocker@freakz.ro> | 2012-01-05 19:14:23 -0800 |
commit | e0bafc73f8ef581e7d7a6969d00828ac44f7ac3e (patch) | |
tree | e1c71b1b61a6026eedeafd4dc6f6a649770ec339 /src | |
parent | 0412c800838f2d3fdad087211ba8b8b25d8b5d78 (diff) | |
parent | 2d8a6fdf154ff0971bbf5e6cb201ec3c1cd56aac (diff) |
Merge pull request #4649 from Souler/fix_simon_game
Scripts/Quest: An Apexis Relic and The Relic's Emanation and Guardian of the Monument
Diffstat (limited to 'src')
-rwxr-xr-x | src/server/game/Spells/SpellMgr.cpp | 11 | ||||
-rw-r--r-- | src/server/scripts/Outland/blades_edge_mountains.cpp | 561 |
2 files changed, 570 insertions, 2 deletions
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index b7c31ee29c6..e57cbf4a390 100755 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -2960,8 +2960,10 @@ void SpellMgr::LoadDbcDataCorrections() switch (spellInfo->Id) { - case 42835: // Spout - spellInfo->Effect[0] = 0; // remove damage effect, only anim is needed + case 40244: case 40245: // Simon Game Visual + case 40246: case 40247: // Simon Game Visual + case 42835: // Spout, remove damage effect, only anim is needed + spellInfo->Effect[0] = 0; break; case 30657: // Quake spellInfo->EffectTriggerSpell[0] = 30571; @@ -3495,6 +3497,11 @@ void SpellMgr::LoadDbcDataCorrections() case 72405: // Broken Frostmourne spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_200_YARDS; // 200yd break; + case 40055: // Introspection + case 40165: // Introspection + case 40166: // Introspection + case 40167: // Introspection + spellInfo->Attributes |= SPELL_ATTR0_NEGATIVE_1; default: break; } 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(); } |