TrinityCore
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Trainer.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "Trainer.h"
19#include "Creature.h"
20#include "NPCPackets.h"
21#include "Player.h"
22#include "SpellInfo.h"
23#include "SpellMgr.h"
24
25namespace Trainer
26{
27 bool Spell::IsCastable() const
28 {
29 return sSpellMgr->AssertSpellInfo(SpellId)->HasEffect(SPELL_EFFECT_LEARN_SPELL);
30 }
31
32 Trainer::Trainer(uint32 trainerId, Type type, uint32 requirement, std::string greeting, std::vector<Spell> spells) : _trainerId(trainerId), _type(type), _requirement(requirement), _spells(std::move(spells))
33 {
34 _greeting[DEFAULT_LOCALE] = std::move(greeting);
35 }
36
37 void Trainer::SendSpells(Creature const* npc, Player const* player, LocaleConstant locale) const
38 {
39 float reputationDiscount = player->GetReputationPriceDiscount(npc);
40
42 trainerList.TrainerGUID = npc->GetGUID();
43 trainerList.TrainerType = AsUnderlyingType(_type);
44 trainerList.Greeting = GetGreeting(locale);
45 trainerList.Spells.reserve(_spells.size());
46 for (Spell const& trainerSpell : _spells)
47 {
48 if (!player->IsSpellFitByClassAndRace(trainerSpell.SpellId))
49 continue;
50
51 SpellInfo const* trainerSpellInfo = sSpellMgr->AssertSpellInfo(trainerSpell.SpellId);
52
53 bool primaryProfessionFirstRank = false;
54 for (SpellEffectInfo const& spellEffectInfo : trainerSpellInfo->GetEffects())
55 {
56 if (!spellEffectInfo.IsEffect(SPELL_EFFECT_LEARN_SPELL))
57 continue;
58
59 SpellInfo const* learnedSpellInfo = sSpellMgr->GetSpellInfo(spellEffectInfo.TriggerSpell);
60 if (learnedSpellInfo && learnedSpellInfo->IsPrimaryProfessionFirstRank())
61 primaryProfessionFirstRank = true;
62 }
63
64 trainerList.Spells.emplace_back();
65 WorldPackets::NPC::TrainerListSpell& trainerListSpell = trainerList.Spells.back();
66 trainerListSpell.SpellID = trainerSpell.SpellId;
67 trainerListSpell.Usable = AsUnderlyingType(GetSpellState(player, &trainerSpell));
68 trainerListSpell.MoneyCost = int32(trainerSpell.MoneyCost * reputationDiscount);
69 trainerListSpell.PointCost[0] = 0; // spells don't cost talent points
70 trainerListSpell.PointCost[1] = (primaryProfessionFirstRank ? 1 : 0);
71 trainerListSpell.ReqLevel = trainerSpell.ReqLevel;
72 trainerListSpell.ReqSkillLine = trainerSpell.ReqSkillLine;
73 trainerListSpell.ReqSkillRank = trainerSpell.ReqSkillRank;
74 std::copy(trainerSpell.ReqAbility.begin(), trainerSpell.ReqAbility.end(), trainerListSpell.ReqAbility.begin());
75 }
76
77 player->SendDirectMessage(trainerList.Write());
78 }
79
80 void Trainer::TeachSpell(Creature const* npc, Player* player, uint32 spellId) const
81 {
82 if (!IsTrainerValidForPlayer(player))
83 return;
84
85 Spell const* trainerSpell = GetSpell(spellId);
86 if (!trainerSpell)
87 {
88 SendTeachFailure(npc, player, spellId, FailReason::Unavailable);
89 return;
90 }
91
92 if (!CanTeachSpell(player, trainerSpell))
93 {
94 SendTeachFailure(npc, player, spellId, FailReason::NotEnoughSkill);
95 return;
96 }
97
98 float reputationDiscount = player->GetReputationPriceDiscount(npc);
99 int32 moneyCost = int32(trainerSpell->MoneyCost * reputationDiscount);
100 if (!player->HasEnoughMoney(moneyCost))
101 {
102 SendTeachFailure(npc, player, spellId, FailReason::NotEnoughMoney);
103 return;
104 }
105
106 player->ModifyMoney(-moneyCost);
107
108 npc->SendPlaySpellVisualKit(179, 0); // 53 SpellCastDirected
109 player->SendPlaySpellVisualKit(362, 1); // 113 EmoteSalute
110
111 // learn explicitly or cast explicitly
112 if (trainerSpell->IsCastable())
113 player->CastSpell(player, trainerSpell->SpellId, true);
114 else
115 player->LearnSpell(trainerSpell->SpellId, false);
116
117 SendTeachSucceeded(npc, player, spellId);
118 }
119
120 Spell const* Trainer::GetSpell(uint32 spellId) const
121 {
122 auto itr = std::find_if(_spells.begin(), _spells.end(), [spellId](Spell const& trainerSpell)
123 {
124 return trainerSpell.SpellId == spellId;
125 });
126
127 if (itr != _spells.end())
128 return &(*itr);
129
130 return nullptr;
131 }
132
133 bool Trainer::CanTeachSpell(Player const* player, Spell const* trainerSpell) const
134 {
135 SpellState state = GetSpellState(player, trainerSpell);
136 if (state != SpellState::Available)
137 return false;
138
139 SpellInfo const* trainerSpellInfo = sSpellMgr->AssertSpellInfo(trainerSpell->SpellId);
140
141 for (SpellEffectInfo const& spellEffectInfo : trainerSpellInfo->GetEffects())
142 {
143 if (!spellEffectInfo.IsEffect(SPELL_EFFECT_LEARN_SPELL))
144 continue;
145
146 SpellInfo const* learnedSpellInfo = sSpellMgr->GetSpellInfo(spellEffectInfo.TriggerSpell);
147 if (learnedSpellInfo && learnedSpellInfo->IsPrimaryProfessionFirstRank() && !player->GetFreePrimaryProfessionPoints())
148 return false;
149 }
150
151 return true;
152 }
153
154 SpellState Trainer::GetSpellState(Player const* player, Spell const* trainerSpell) const
155 {
156 if (player->HasSpell(trainerSpell->SpellId))
157 return SpellState::Known;
158
159 // check race/class requirement
160 if (!player->IsSpellFitByClassAndRace(trainerSpell->SpellId))
162
163 // check skill requirement
164 if (trainerSpell->ReqSkillLine && player->GetBaseSkillValue(trainerSpell->ReqSkillLine) < trainerSpell->ReqSkillRank)
166
167 for (int32 reqAbility : trainerSpell->ReqAbility)
168 if (reqAbility && !player->HasSpell(reqAbility))
170
171 // check level requirement
172 if (player->GetLevel() < trainerSpell->ReqLevel)
174
175 // check ranks
176 bool hasLearnSpellEffect = false;
177 bool knowsAllLearnedSpells = true;
178 for (SpellEffectInfo const& spellEffectInfo : sSpellMgr->AssertSpellInfo(trainerSpell->SpellId)->GetEffects())
179 {
180 if (!spellEffectInfo.IsEffect(SPELL_EFFECT_LEARN_SPELL))
181 continue;
182
183 hasLearnSpellEffect = true;
184 if (!player->HasSpell(spellEffectInfo.TriggerSpell))
185 knowsAllLearnedSpells = false;
186
187 if (uint32 previousRankSpellId = sSpellMgr->GetPrevSpellInChain(spellEffectInfo.TriggerSpell))
188 if (!player->HasSpell(previousRankSpellId))
190 }
191
192 if (!hasLearnSpellEffect)
193 {
194 if (uint32 previousRankSpellId = sSpellMgr->GetPrevSpellInChain(trainerSpell->SpellId))
195 if (!player->HasSpell(previousRankSpellId))
197 }
198 else if (knowsAllLearnedSpells)
199 return SpellState::Known;
200
201 // check additional spell requirement
202 for (auto const& requirePair : sSpellMgr->GetSpellsRequiredForSpellBounds(trainerSpell->SpellId))
203 if (!player->HasSpell(requirePair.second))
205
207 }
208
209 bool Trainer::IsTrainerValidForPlayer(Player const* player) const
210 {
211 if (!GetTrainerRequirement())
212 return true;
213
214 switch (GetTrainerType())
215 {
216 case Type::Class:
217 case Type::Pet:
218 // check class for class trainers
219 return player->GetClass() == GetTrainerRequirement();
220 case Type::Mount:
221 // check race for mount trainers
222 return player->GetRace() == GetTrainerRequirement();
223 case Type::Tradeskill:
224 // check spell for profession trainers
225 return player->HasSpell(GetTrainerRequirement());
226 default:
227 break;
228 }
229
230 return true;
231 }
232
233 void Trainer::SendTeachFailure(Creature const* npc, Player const* player, uint32 spellId, FailReason reason) const
234 {
236 trainerBuyFailed.TrainerGUID = npc->GetGUID();
237 trainerBuyFailed.SpellID = spellId;
238 trainerBuyFailed.TrainerFailedReason = AsUnderlyingType(reason);
239 player->SendDirectMessage(trainerBuyFailed.Write());
240 }
241
242 void Trainer::SendTeachSucceeded(Creature const* npc, Player const* player, uint32 spellId) const
243 {
244 WorldPackets::NPC::TrainerBuySucceeded trainerBuySucceeded;
245 trainerBuySucceeded.TrainerGUID = npc->GetGUID();
246 trainerBuySucceeded.SpellID = spellId;
247 player->SendDirectMessage(trainerBuySucceeded.Write());
248 }
249
250 std::string const& Trainer::GetGreeting(LocaleConstant locale) const
251 {
252 if (_greeting[locale].empty())
253 return _greeting[DEFAULT_LOCALE];
254
255 return _greeting[locale];
256 }
257
258 void Trainer::AddGreetingLocale(LocaleConstant locale, std::string greeting)
259 {
260 _greeting[locale] = std::move(greeting);
261 }
262}
LocaleConstant
Definition: Common.h:48
#define DEFAULT_LOCALE
Definition: Common.h:62
int32_t int32
Definition: Define.h:129
uint32_t uint32
Definition: Define.h:133
@ SPELL_EFFECT_LEARN_SPELL
#define sSpellMgr
Definition: SpellMgr.h:738
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition: Util.h:554
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:77
Definition: Player.h:915
void LearnSpell(uint32 spell_id, bool dependent, uint32 fromSkill=0)
Definition: Player.cpp:3421
void SendDirectMessage(WorldPacket const *data) const
Definition: Player.cpp:6242
bool ModifyMoney(int32 amount, bool sendError=true)
Definition: Player.cpp:22493
bool IsSpellFitByClassAndRace(uint32 spell_id) const
Definition: Player.cpp:23344
float GetReputationPriceDiscount(Creature const *creature) const
Definition: Player.cpp:23322
bool HasEnoughMoney(uint32 amount) const
Definition: Player.h:1403
uint16 GetBaseSkillValue(uint32 skill) const
Definition: Player.cpp:6014
bool HasSpell(uint32 spell) const override
Definition: Player.cpp:3921
uint32 GetFreePrimaryProfessionPoints() const
Definition: Player.h:1523
bool IsPrimaryProfessionFirstRank() const
Definition: SpellInfo.cpp:1013
std::array< SpellEffectInfo, MAX_SPELL_EFFECTS > const & GetEffects() const
Definition: SpellInfo.h:486
uint8 GetClass() const
Definition: Unit.h:893
void SendPlaySpellVisualKit(uint32 id, uint32 type) const
Definition: Unit.cpp:12052
uint8 GetLevel() const
Definition: Unit.h:887
uint8 GetRace() const
Definition: Unit.h:890
SpellCastResult CastSpell(CastSpellTargetArg const &targets, uint32 spellId, CastSpellExtraArgs const &args={ })
Definition: Object.cpp:2831
WorldPacket const * Write() override
Definition: NPCPackets.cpp:65
WorldPacket const * Write() override
Definition: NPCPackets.cpp:74
WorldPacket const * Write() override
Definition: NPCPackets.cpp:25
std::vector< TrainerListSpell > Spells
Definition: NPCPackets.h:67
SpellState
Definition: Trainer.h:40
FailReason
Definition: Trainer.h:47
STL namespace.
uint32 ReqSkillLine
Definition: Trainer.h:57
std::array< uint32, 3 > ReqAbility
Definition: Trainer.h:59
uint8 ReqLevel
Definition: Trainer.h:60
bool IsCastable() const
Definition: Trainer.cpp:27
uint32 ReqSkillRank
Definition: Trainer.h:58
uint32 SpellId
Definition: Trainer.h:55
uint32 MoneyCost
Definition: Trainer.h:56
std::array< int32, 3 > ReqAbility
Definition: NPCPackets.h:55
std::array< int32, 2 > PointCost
Definition: NPCPackets.h:51