diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 8c3fcafaf83a..e68965571144 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -14233,7 +14233,7 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool break; case GOSSIP_OPTION_TRAINER: { - Trainer::Trainer const* trainer = sObjectMgr->GetTrainer(creature->GetEntry()); + Trainer::Trainer const* trainer = sObjectMgr->GetTrainer(itr->second.ActionMenuID ? itr->second.ActionMenuID : creature->GetEntry()); if (!trainer || !trainer->IsTrainerValidForPlayer(this)) { TC_LOG_ERROR("sql.sql", "GOSSIP_OPTION_TRAINER:: Player %s %s requested wrong gossip menu: %u at Creature: %s (Entry: %u)", @@ -14421,7 +14421,7 @@ void Player::OnGossipSelect(WorldObject* source, uint32 gossipListId, uint32 men GetSession()->SendStablePet(guid); break; case GOSSIP_OPTION_TRAINER: - GetSession()->SendTrainerList(source->ToCreature()); + GetSession()->SendTrainerList(source->ToCreature(), menuItemData->GossipActionMenuId); break; case GOSSIP_OPTION_LEARNDUALSPEC: if (GetSpecsCount() == 1 && GetLevel() >= sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL)) diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index dbc84d072250..2bdd4d946cc3 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -97,13 +97,13 @@ void WorldSession::HandleTrainerListOpcode(WorldPackets::NPC::Hello& packet) SendTrainerList(npc); } -void WorldSession::SendTrainerList(Creature* npc) +void WorldSession::SendTrainerList(Creature* npc, uint32 trainerEntry) { // remove fake death if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - Trainer::Trainer const* trainer = sObjectMgr->GetTrainer(npc->GetEntry()); + Trainer::Trainer const* trainer = sObjectMgr->GetTrainer(trainerEntry ? trainerEntry : npc->GetEntry()); if (!trainer) { TC_LOG_DEBUG("network", "WorldSession: SendTrainerList - trainer spells not found for %s", npc->GetGUID().ToString().c_str()); @@ -116,6 +116,9 @@ void WorldSession::SendTrainerList(Creature* npc) return; } + SetCurrentTrainer(trainerEntry); + GetPlayer()->PlayerTalkClass->GetGossipMenu().SetSenderGUID(npc->GetGUID()); + trainer->SendSpells(npc, _player, GetSessionDbLocaleIndex()); } @@ -130,11 +133,14 @@ void WorldSession::HandleTrainerBuySpellOpcode(WorldPackets::NPC::TrainerBuySpel return; } + if (packet.TrainerGUID != GetPlayer()->PlayerTalkClass->GetGossipMenu().GetSenderGUID()) + return; // Cheating + // remove fake death if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - Trainer::Trainer const* trainer = sObjectMgr->GetTrainer(npc->GetEntry()); + Trainer::Trainer const* trainer = sObjectMgr->GetTrainer(GetCurrentTrainer() ? GetCurrentTrainer() : npc->GetEntry()); if (!trainer) return; diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 8c2f073fa674..986d7abdc86d 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -492,7 +492,7 @@ class TC_GAME_API WorldSession void SendNameQueryOpcode(ObjectGuid guid); - void SendTrainerList(Creature* npc); + void SendTrainerList(Creature* npc, uint32 trainer_entry = 0); void SendListInventory(ObjectGuid guid); void SendShowBank(ObjectGuid guid); bool CanOpenMailBox(ObjectGuid guid); @@ -568,6 +568,10 @@ class TC_GAME_API WorldSession bool CanSpeak() const; time_t m_muteTime; + // Multitrainer + uint32 GetCurrentTrainer() const { return m_current_trainer; } + void SetCurrentTrainer(uint32 entry) { m_current_trainer = entry; } + // Locales LocaleConstant GetSessionDbcLocale() const { return m_sessionDbcLocale; } LocaleConstant GetSessionDbLocaleIndex() const { return m_sessionDbLocaleIndex; } @@ -1192,6 +1196,8 @@ class TC_GAME_API WorldSession std::string _accountName; uint8 m_expansion; + uint32 m_current_trainer; + // Warden std::unique_ptr _warden; // Remains NULL if Warden system is not enabled by config diff --git a/src/server/scripts/Custom/multitrainer/MultitrainerExample.cpp b/src/server/scripts/Custom/multitrainer/MultitrainerExample.cpp new file mode 100644 index 000000000000..c20ec57e8650 --- /dev/null +++ b/src/server/scripts/Custom/multitrainer/MultitrainerExample.cpp @@ -0,0 +1,42 @@ +#include "ScriptMgr.h" +#include "Creature.h" +#include "Player.h" +#include "ScriptedGossip.h" +#include "ScriptedCreature.h" +#include "WorldSession.h" + +class MultivendorExample : public CreatureScript +{ +public: + MultivendorExample() : CreatureScript("MultivendorExample") {} + + struct MultivendorAI : public ScriptedAI + { + MultivendorAI(Creature* creature) : ScriptedAI(creature) {} + + bool OnGossipHello(Player* player) override + { + AddGossipItemFor(player, GOSSIP_ICON_CHAT, "TrainerTest 33684", GOSSIP_SENDER_MAIN, 33684); + AddGossipItemFor(player, GOSSIP_ICON_CHAT, "TrainerTest 2704", GOSSIP_SENDER_MAIN, 2704); + SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, me); + return true; + } + + bool OnGossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override + { + uint32 action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); + player->GetSession()->SendTrainerList(me, action); + return true; + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new MultivendorAI(creature); + } +}; + +void AddSC_MultivendorExample() +{ + new MultivendorExample(); +} diff --git a/src/server/scripts/Custom/multitrainer/MultitrainerExample.sql b/src/server/scripts/Custom/multitrainer/MultitrainerExample.sql new file mode 100644 index 000000000000..8b94ebc32f0b --- /dev/null +++ b/src/server/scripts/Custom/multitrainer/MultitrainerExample.sql @@ -0,0 +1,9 @@ +SET @ENTRY = (SELECT max(entry)+1 FROM creature_template); +SET @MENU_ID = (SELECT max(menuid)+1 FROM gossip_menu_option); + +INSERT INTO `creature_template` (`entry`, `modelid1`, `name`, `subname`, `IconName`, `gossip_menu_id`, `minlevel`, `maxlevel`, `exp`, `faction`, `npcflag`, `speed_walk`, `speed_run`, `scale`, `rank`, `dmgschool`, `baseattacktime`, `rangeattacktime`, `unit_class`, `unit_flags`, `unit_flags2`, `dynamicflags`, `family`, `type`, `type_flags`, `lootid`, `pickpocketloot`, `skinloot`, `PetSpellDataId`, `VehicleId`, `mingold`, `maxgold`, `AIName`, `MovementType`, `HoverHeight`, `RacialLeader`, `movementId`, `RegenHealth`, `mechanic_immune_mask`, `flags_extra`, `ScriptName`) VALUES +(@ENTRY, 5080, "Rakka", "MultiTrainer", NULL, @MENU_ID, 10, 10, 0, 35, 17, 1, 1.14286, 1, 0, 0, 1500, 0, 1, 512, 2048, 8, 0, 7, 138412032, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 0, 0, 1, 0, 2, ''); + +INSERT INTO `gossip_menu_option` (`menuid`, `optionid`, `optionicon`, `optiontext`, `optiontype`, `optionnpcflag`, `actionmenuid`, `actionpoiid`, `boxcoded`, `boxmoney`, `boxtext`) VALUES +(@MENU_ID, 0, 3, 'TrainerTest 33684', 5, 16, 33684, 0, 0, 0, ''), +(@MENU_ID, 1, 3, 'TrainerTest 2704', 5, 16, 2704, 0, 0, 0, ''); diff --git a/src/server/scripts/Custom/multitrainer/README.md b/src/server/scripts/Custom/multitrainer/README.md new file mode 100644 index 000000000000..8d171d74a661 --- /dev/null +++ b/src/server/scripts/Custom/multitrainer/README.md @@ -0,0 +1,36 @@ +# Multitrainer [![Build Status](https://travis-ci.org/Rochet2/TrinityCore.svg?branch=multitrainer_3.3.5)](https://travis-ci.org/Rochet2/TrinityCore) + +#### About +This patch was coded originally by [CyberMist2](https://github.com/CyberMist2). +Multitrainer allows you to show multiple different trainer windows on an NPC. + +Source: http://rochet2.github.io/Multitrainer.html + +#### Installation + +Available as: +- Direct merge: https://github.com/Rochet2/TrinityCore/tree/multitrainer_3.3.5 +- Diff: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:3.3.5...multitrainer_3.3.5.diff +- Diff in github view: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:3.3.5...multitrainer_3.3.5 + +Using direct merge: +- open git bash to source location +- do `git remote add rochet2 https://github.com/Rochet2/TrinityCore.git` +- do `git pull rochet2 multitrainer_3.3.5` +- use cmake and compile + +Using diff: +- DO NOT COPY THE DIFF DIRECTLY! It causes apply to fail. +- download the diff by __right clicking__ the link and select __Save link as__ +- place the downloaded `multitrainer_3.3.5.diff` to the source root folder +- open git bash to source location +- do `git apply multitrainer_3.3.5.diff` +- use cmake and compile + +#### Usage +The NPC is required to have `npcflag` set to `17`. The `trainer_type` can be `0`. +In C++ you can pass the trainer entry to `SendTrainerList` function. +In SQL in `world` database you can set the trainer entry to the `gossip_menu_option` table `ActionMenuID` column for an option that has `OptionType` set to `5`. + +#### Bugs and Contact +Report issues and similar to https://rochet2.github.io/