diff --git a/sql/updates/world/2016_01_09_toytrain.sql b/sql/updates/world/2016_01_09_toytrain.sql new file mode 100644 index 0000000000000..fc0b8a0cda078 --- /dev/null +++ b/sql/updates/world/2016_01_09_toytrain.sql @@ -0,0 +1,6 @@ +-- +UPDATE `gameobject_template` SET `ScriptName`="go_toy_train_set" WHERE `entry`=193963; +UPDATE `creature_template` SET `ScriptName`="npc_train_wrecker" WHERE `entry`=33404; +DELETE FROM `spell_script_names` WHERE `spell_id`=61551; +INSERT INTO `spell_script_names` (`spell_id`,`ScriptName`) VALUES +(61551,"spell_item_toy_train_set_pulse"); diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 5f1de6732948e..c73350872cc6e 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -91,6 +91,9 @@ DBCStorage sDurabilityCostsStore(DurabilityCostsfmt); DBCStorage sEmotesStore(EmotesEntryfmt); DBCStorage sEmotesTextStore(EmotesTextEntryfmt); +typedef std::tuple EmotesTextSoundKey; +static std::map sEmotesTextSoundMap; +DBCStorage sEmotesTextSoundStore(EmotesTextSoundEntryfmt); typedef std::map FactionTeamMap; static FactionTeamMap sFactionTeamMap; @@ -338,6 +341,10 @@ void LoadDBCStores(const std::string& dataPath) LoadDBC(availableDbcLocales, bad_dbc_files, sDurabilityQualityStore, dbcPath, "DurabilityQuality.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sEmotesStore, dbcPath, "Emotes.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sEmotesTextStore, dbcPath, "EmotesText.dbc"); + LoadDBC(availableDbcLocales, bad_dbc_files, sEmotesTextSoundStore, dbcPath, "EmotesTextSound.dbc"); + for (uint32 i = 0; i < sEmotesTextSoundStore.GetNumRows(); ++i) + if (EmotesTextSoundEntry const* entry = sEmotesTextSoundStore.LookupEntry(i)) + sEmotesTextSoundMap[EmotesTextSoundKey(entry->EmotesTextId, entry->RaceId, entry->SexId)] = entry; LoadDBC(availableDbcLocales, bad_dbc_files, sFactionStore, dbcPath, "Faction.dbc"); for (uint32 i=0; i sAchievementStore; extern DBCStorage sAchievementCriteriaStore; extern DBCStorage sAreaStore;// recommend access using functions @@ -113,6 +115,7 @@ extern DBCStorage sDurabilityCostsStore; extern DBCStorage sDurabilityQualityStore; extern DBCStorage sEmotesStore; extern DBCStorage sEmotesTextStore; +extern DBCStorage sEmotesTextSoundStore; extern DBCStorage sFactionStore; extern DBCStorage sFactionTemplateStore; extern DBCStorage sGameObjectDisplayInfoStore; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index d1794a0ea908e..2f4820d0353ba 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -926,6 +926,15 @@ struct EmotesTextEntry uint32 textid; }; +struct EmotesTextSoundEntry +{ + uint32 Id; // 0 + uint32 EmotesTextId; // 1 + uint32 RaceId; // 2 + uint32 SexId; // 3, 0 male / 1 female + uint32 SoundId; // 4 +}; + struct FactionEntry { uint32 ID; // 0 m_ID diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index aade6d91d614a..22a01dae9f08b 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -52,6 +52,7 @@ char const DurabilityCostsfmt[] = "niiiiiiiiiiiiiiiiiiiiiiiiiiiii"; char const DurabilityQualityfmt[] = "nf"; char const EmotesEntryfmt[] = "nxxiiix"; char const EmotesTextEntryfmt[] = "nxixxxxxxxxxxxxxxxx"; +char const EmotesTextSoundEntryfmt[] = "niiii"; char const FactionEntryfmt[] = "niiiiiiiiiiiiiiiiiiffixssssssssssssssssxxxxxxxxxxxxxxxxxx"; char const FactionTemplateEntryfmt[] = "niiiiiiiiiiiii"; char const GameObjectDisplayInfofmt[] = "nsxxxxxxxxxxffffffx"; diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index 9475da91a77d7..0abff255e4b14 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -29,6 +29,7 @@ #include "SpellAuraEffects.h" #include "SkillDiscovery.h" #include "Battleground.h" +#include "DBCStores.h" // Generic script for handling item dummy effects which trigger another spell. class spell_item_trigger_spell : public SpellScriptLoader @@ -2630,6 +2631,43 @@ class spell_item_greatmothers_soulcatcher : public SpellScriptLoader } }; +class spell_item_toy_train_set_pulse : public SpellScriptLoader +{ +public: + spell_item_toy_train_set_pulse() : SpellScriptLoader("spell_item_toy_train_set_pulse") { } + + class spell_item_toy_train_set_pulse_SpellScript : public SpellScript + { + PrepareSpellScript(spell_item_toy_train_set_pulse_SpellScript); + + void HandleDummy(SpellEffIndex /*index*/) + { + if (Player* target = GetHitUnit()->ToPlayer()) + { + target->HandleEmoteCommand(EMOTE_ONESHOT_TRAIN); + if (EmotesTextSoundEntry const* soundEntry = FindTextSoundEmoteFor(TEXT_EMOTE_TRAIN, target->getRace(), target->getGender())) + target->PlayDistanceSound(soundEntry->SoundId); + } + } + + void HandleTargets(std::list& targetList) + { + targetList.remove_if([](WorldObject const* obj) { return obj->GetTypeId() != TYPEID_PLAYER; }); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_toy_train_set_pulse_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_item_toy_train_set_pulse_SpellScript::HandleTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ALLY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_item_toy_train_set_pulse_SpellScript(); + } +}; + void AddSC_item_spell_scripts() { // 23074 Arcanite Dragonling @@ -2698,4 +2736,5 @@ void AddSC_item_spell_scripts() new spell_item_chicken_cover(); new spell_item_muisek_vessel(); new spell_item_greatmothers_soulcatcher(); + new spell_item_toy_train_set_pulse(); } diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp index ef4a2b0e32f76..b90839f50c555 100644 --- a/src/server/scripts/World/go_scripts.cpp +++ b/src/server/scripts/World/go_scripts.cpp @@ -41,6 +41,7 @@ go_tadpole_cage go_amberpine_outhouse go_hive_pod go_veil_skith_cage +go_toy_train_set EndContentData */ #include "ScriptMgr.h" @@ -1196,6 +1197,48 @@ class go_midsummer_bonfire : public GameObjectScript } }; + +enum ToyTrainSpells +{ + SPELL_TOY_TRAIN_PULSE = 61551, +}; + +class go_toy_train_set : public GameObjectScript +{ + public: + go_toy_train_set() : GameObjectScript("go_toy_train_set") { } + + struct go_toy_train_setAI : public GameObjectAI + { + go_toy_train_setAI(GameObject* go) : GameObjectAI(go), _pulseTimer(3 * IN_MILLISECONDS) { } + + void UpdateAI(uint32 diff) override + { + if (diff < _pulseTimer) + _pulseTimer -= diff; + else + { + go->CastSpell(nullptr, SPELL_TOY_TRAIN_PULSE, true); + _pulseTimer = 6 * IN_MILLISECONDS; + } + } + + // triggered on wrecker'd + void DoAction(int32 /*action*/) override + { + go->Delete(); + } + + private: + uint32 _pulseTimer; + }; + + GameObjectAI* GetAI(GameObject* go) const override + { + return new go_toy_train_setAI(go); + } +}; + void AddSC_go_scripts() { new go_cat_figurine(); @@ -1231,4 +1274,5 @@ void AddSC_go_scripts() new go_veil_skith_cage(); new go_frostblade_shrine(); new go_midsummer_bonfire(); + new go_toy_train_set(); } diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index 16b95e555bbf5..0dd461d87f396 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -38,6 +38,7 @@ npc_snake_trap_serpents 80% AI for snakes that summoned by Snake Trap npc_shadowfiend 100% restore 5% of owner's mana when shadowfiend die from damage npc_locksmith 75% list of keys needs to be confirmed npc_firework 100% NPC's summoned by rockets and rocket clusters, for making them cast visual +npc_train_wrecker 100% Wind-Up Train Wrecker that kills train set EndContentData */ #include "ScriptMgr.h" @@ -2387,6 +2388,129 @@ class npc_stable_master : public CreatureScript } }; +enum TrainWrecker +{ + GO_TOY_TRAIN = 193963, + SPELL_TOY_TRAIN_PULSE = 61551, + SPELL_WRECK_TRAIN = 62943, + ACTION_WRECKED = 1, + EVENT_DO_JUMP = 1, + EVENT_DO_FACING = 2, + EVENT_DO_WRECK = 3, + EVENT_DO_DANCE = 4, + MOVEID_CHASE = 1, + MOVEID_JUMP = 2 +}; +class npc_train_wrecker : public CreatureScript +{ + public: + npc_train_wrecker() : CreatureScript("npc_train_wrecker") { } + + struct npc_train_wreckerAI : public NullCreatureAI + { + npc_train_wreckerAI(Creature* creature) : NullCreatureAI(creature), _isSearching(true), _nextAction(0), _timer(1 * IN_MILLISECONDS) { } + + GameObject* VerifyTarget() const + { + if (GameObject* target = ObjectAccessor::GetGameObject(*me, _target)) + return target; + me->HandleEmoteCommand(EMOTE_ONESHOT_RUDE); + me->DespawnOrUnsummon(3 * IN_MILLISECONDS); + return nullptr; + } + + void UpdateAI(uint32 diff) override + { + if (_isSearching) + { + if (diff < _timer) + _timer -= diff; + else + { + if (GameObject* target = me->FindNearestGameObject(GO_TOY_TRAIN, 15.0f)) + { + _isSearching = false; + _target = target->GetGUID(); + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(MOVEID_CHASE, target->GetNearPosition(3.0f, target->GetAngle(me))); + } + else + _timer = 3 * IN_MILLISECONDS; + } + } + else + { + switch (_nextAction) + { + case EVENT_DO_JUMP: + if (GameObject* target = VerifyTarget()) + me->GetMotionMaster()->MoveJump(*target, 5.0, 10.0, MOVEID_JUMP); + _nextAction = 0; + break; + case EVENT_DO_FACING: + if (GameObject* target = VerifyTarget()) + { + me->SetFacingTo(target->GetOrientation()); + me->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK1H); + _timer = 1.5 * IN_MILLISECONDS; + _nextAction = EVENT_DO_WRECK; + } + else + _nextAction = 0; + break; + case EVENT_DO_WRECK: + if (diff < _timer) + { + _timer -= diff; + break; + } + if (GameObject* target = VerifyTarget()) + { + me->CastSpell(target, SPELL_WRECK_TRAIN, false); + target->AI()->DoAction(ACTION_WRECKED); + _timer = 2 * IN_MILLISECONDS; + _nextAction = EVENT_DO_DANCE; + } + else + _nextAction = 0; + break; + case EVENT_DO_DANCE: + if (diff < _timer) + { + _timer -= diff; + break; + } + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_DANCE); + me->DespawnOrUnsummon(5 * IN_MILLISECONDS); + _nextAction = 0; + break; + default: + break; + } + } + } + + void MovementInform(uint32 /*type*/, uint32 id) + { + if (id == MOVEID_CHASE) + _nextAction = EVENT_DO_JUMP; + else if (id == MOVEID_JUMP) + _nextAction = EVENT_DO_FACING; + } + + private: + bool _isSearching; + uint8 _nextAction; + uint32 _timer; + ObjectGuid _target; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_train_wreckerAI(creature); + } +}; + void AddSC_npcs_special() { new npc_air_force_bots(); @@ -2410,4 +2534,5 @@ void AddSC_npcs_special() new npc_spring_rabbit(); new npc_imp_in_a_ball(); new npc_stable_master(); + new npc_train_wrecker(); }