TrinityCore
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 "BattlePetMgr.h"
20#include "ConditionMgr.h"
21#include "Creature.h"
22#include "Log.h"
23#include "NPCPackets.h"
24#include "Player.h"
25#include "SpellInfo.h"
26#include "SpellMgr.h"
27#include "WorldSession.h"
28
29namespace Trainer
30{
31 bool Spell::IsCastable() const
32 {
33 return sSpellMgr->AssertSpellInfo(SpellId, DIFFICULTY_NONE)->HasEffect(SPELL_EFFECT_LEARN_SPELL);
34 }
35
36 Trainer::Trainer(uint32 id, Type type, std::string_view greeting, std::vector<Spell> spells) : _id(id), _type(type), _spells(std::move(spells))
37 {
38 _greeting[DEFAULT_LOCALE] = greeting;
39 }
40
41 void Trainer::SendSpells(Creature const* npc, Player* player, LocaleConstant locale) const
42 {
43 float reputationDiscount = player->GetReputationPriceDiscount(npc);
44
46 trainerList.TrainerGUID = npc->GetGUID();
47 trainerList.TrainerType = AsUnderlyingType(_type);
48 trainerList.TrainerID = _id;
49 trainerList.Greeting = GetGreeting(locale);
50 trainerList.Spells.reserve(_spells.size());
51 for (Spell const& trainerSpell : _spells)
52 {
53 if (!player->IsSpellFitByClassAndRace(trainerSpell.SpellId))
54 continue;
55
56 if (!sConditionMgr->IsObjectMeetingTrainerSpellConditions(_id, trainerSpell.SpellId, player))
57 {
58 TC_LOG_DEBUG("condition", "SendSpells: conditions not met for trainer id {} spell {} player '{}' ({})", _id, trainerSpell.SpellId, player->GetName(), player->GetGUID().ToString());
59 continue;
60 }
61
62 trainerList.Spells.emplace_back();
63 WorldPackets::NPC::TrainerListSpell& trainerListSpell = trainerList.Spells.back();
64 trainerListSpell.SpellID = trainerSpell.SpellId;
65 trainerListSpell.MoneyCost = int32(trainerSpell.MoneyCost * reputationDiscount);
66 trainerListSpell.ReqSkillLine = trainerSpell.ReqSkillLine;
67 trainerListSpell.ReqSkillRank = trainerSpell.ReqSkillRank;
68 std::copy(trainerSpell.ReqAbility.begin(), trainerSpell.ReqAbility.end(), trainerListSpell.ReqAbility.begin());
69 trainerListSpell.Usable = AsUnderlyingType(GetSpellState(player, &trainerSpell));
70 trainerListSpell.ReqLevel = trainerSpell.ReqLevel;
71 }
72
73 player->SendDirectMessage(trainerList.Write());
74 }
75
76 void Trainer::TeachSpell(Creature const* npc, Player* player, uint32 spellId) const
77 {
78 Spell const* trainerSpell = GetSpell(spellId);
79 if (!trainerSpell || !CanTeachSpell(player, trainerSpell))
80 {
81 SendTeachFailure(npc, player, spellId, FailReason::Unavailable);
82 return;
83 }
84
85 bool sendSpellVisual = true;
87 if (speciesEntry)
88 {
89 if (player->GetSession()->GetBattlePetMgr()->HasMaxPetCount(speciesEntry, player->GetGUID()))
90 {
91 // Don't send any error to client (intended)
92 return;
93 }
94
95 sendSpellVisual = false;
96 }
97
98 float reputationDiscount = player->GetReputationPriceDiscount(npc);
99 int64 moneyCost = int64(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 if (sendSpellVisual)
109 {
110 npc->SendPlaySpellVisualKit(179, 0, 0); // 53 SpellCastDirected
111 player->SendPlaySpellVisualKit(362, 1, 0); // 113 EmoteSalute
112 }
113
114 // learn explicitly or cast explicitly
115 if (trainerSpell->IsCastable())
116 {
117 player->CastSpell(player, trainerSpell->SpellId, true);
118 }
119 else
120 {
121 bool dependent = false;
122
123 if (speciesEntry)
124 {
125 player->GetSession()->GetBattlePetMgr()->AddPet(speciesEntry->ID, BattlePets::BattlePetMgr::SelectPetDisplay(speciesEntry),
127 // If the spell summons a battle pet, we fake that it has been learned and the battle pet is added
128 // marking as dependent prevents saving the spell to database (intended)
129 dependent = true;
130 }
131
132 player->LearnSpell(trainerSpell->SpellId, dependent);
133 }
134 }
135
136 Spell const* Trainer::GetSpell(uint32 spellId) const
137 {
138 auto itr = std::find_if(_spells.begin(), _spells.end(), [spellId](Spell const& trainerSpell)
139 {
140 return trainerSpell.SpellId == spellId;
141 });
142
143 if (itr != _spells.end())
144 return &(*itr);
145
146 return nullptr;
147 }
148
149 bool Trainer::CanTeachSpell(Player const* player, Spell const* trainerSpell) const
150 {
151 SpellState state = GetSpellState(player, trainerSpell);
152 if (state != SpellState::Available)
153 return false;
154
155 SpellInfo const* trainerSpellInfo = sSpellMgr->AssertSpellInfo(trainerSpell->SpellId, DIFFICULTY_NONE);
156 if (trainerSpellInfo->IsPrimaryProfessionFirstRank() && !player->GetFreePrimaryProfessionPoints())
157 return false;
158
159 for (SpellEffectInfo const& effect : trainerSpellInfo->GetEffects())
160 {
161 if (!effect.IsEffect(SPELL_EFFECT_LEARN_SPELL))
162 continue;
163
164 SpellInfo const* learnedSpellInfo = sSpellMgr->GetSpellInfo(effect.TriggerSpell, DIFFICULTY_NONE);
165 if (learnedSpellInfo && learnedSpellInfo->IsPrimaryProfessionFirstRank() && !player->GetFreePrimaryProfessionPoints())
166 return false;
167 }
168
169 return true;
170 }
171
172 SpellState Trainer::GetSpellState(Player const* player, Spell const* trainerSpell) const
173 {
174 if (player->HasSpell(trainerSpell->SpellId))
175 return SpellState::Known;
176
177 // check race/class requirement
178 if (!player->IsSpellFitByClassAndRace(trainerSpell->SpellId))
180
181 // check skill requirement
182 if (trainerSpell->ReqSkillLine && player->GetBaseSkillValue(trainerSpell->ReqSkillLine) < trainerSpell->ReqSkillRank)
184
185 for (int32 reqAbility : trainerSpell->ReqAbility)
186 if (reqAbility && !player->HasSpell(reqAbility))
188
189 // check level requirement
190 if (player->GetLevel() < trainerSpell->ReqLevel)
192
193 // check ranks
194 bool hasLearnSpellEffect = false;
195 bool knowsAllLearnedSpells = true;
196 for (SpellEffectInfo const& spellEffectInfo : sSpellMgr->AssertSpellInfo(trainerSpell->SpellId, DIFFICULTY_NONE)->GetEffects())
197 {
198 if (!spellEffectInfo.IsEffect(SPELL_EFFECT_LEARN_SPELL))
199 continue;
200
201 hasLearnSpellEffect = true;
202 if (!player->HasSpell(spellEffectInfo.TriggerSpell))
203 knowsAllLearnedSpells = false;
204 }
205
206 if (hasLearnSpellEffect && knowsAllLearnedSpells)
207 return SpellState::Known;
208
210 }
211
212 void Trainer::SendTeachFailure(Creature const* npc, Player const* player, uint32 spellId, FailReason reason) const
213 {
215 trainerBuyFailed.TrainerGUID = npc->GetGUID();
216 trainerBuyFailed.SpellID = spellId;
217 trainerBuyFailed.TrainerFailedReason = AsUnderlyingType(reason);
218 player->SendDirectMessage(trainerBuyFailed.Write());
219 }
220
221 std::string const& Trainer::GetGreeting(LocaleConstant locale) const
222 {
223 if (_greeting[locale].empty())
224 return _greeting[DEFAULT_LOCALE];
225
226 return _greeting[locale];
227 }
228
229 void Trainer::AddGreetingLocale(LocaleConstant locale, std::string_view greeting)
230 {
231 _greeting[locale] = greeting;
232 }
233}
LocaleConstant
Definition: Common.h:48
#define DEFAULT_LOCALE
Definition: Common.h:66
#define sConditionMgr
Definition: ConditionMgr.h:365
@ DIFFICULTY_NONE
Definition: DBCEnums.h:874
int64_t int64
Definition: Define.h:137
int32_t int32
Definition: Define.h:138
uint32_t uint32
Definition: Define.h:142
#define TC_LOG_DEBUG(filterType__,...)
Definition: Log.h:156
@ SPELL_EFFECT_LEARN_SPELL
#define sSpellMgr
Definition: SpellMgr.h:849
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition: Util.h:491
static uint16 RollPetBreed(uint32 species)
static uint32 SelectPetDisplay(BattlePetSpeciesEntry const *speciesEntry)
static BattlePetSpeciesEntry const * GetBattlePetSpeciesBySpell(uint32 spellId)
void AddPet(uint32 species, uint32 display, uint16 breed, BattlePetBreedQuality quality, uint16 level=1)
bool HasMaxPetCount(BattlePetSpeciesEntry const *battlePetSpecies, ObjectGuid ownerGuid) const
static BattlePetBreedQuality GetDefaultPetQuality(uint32 species)
std::string ToString() const
Definition: ObjectGuid.cpp:554
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:159
void LearnSpell(uint32 spell_id, bool dependent, int32 fromSkill=0, bool suppressMessaging=false, Optional< int32 > traitDefinitionId={})
Definition: Player.cpp:3216
bool ModifyMoney(int64 amount, bool sendError=true)
Definition: Player.cpp:24098
void SendDirectMessage(WorldPacket const *data) const
Definition: Player.cpp:6324
bool IsSpellFitByClassAndRace(uint32 spell_id) const
Definition: Player.cpp:25155
float GetReputationPriceDiscount(Creature const *creature) const
Definition: Player.cpp:25133
WorldSession * GetSession() const
Definition: Player.h:2101
uint16 GetBaseSkillValue(uint32 skill) const
Definition: Player.cpp:6094
bool HasSpell(uint32 spell) const override
Definition: Player.cpp:3792
uint32 GetFreePrimaryProfessionPoints() const
Definition: Player.h:1897
bool HasEnoughMoney(uint64 amount) const
Definition: Player.h:1740
std::vector< SpellEffectInfo > const & GetEffects() const
Definition: SpellInfo.h:576
bool IsPrimaryProfessionFirstRank() const
Definition: SpellInfo.cpp:1516
void SendPlaySpellVisualKit(uint32 id, uint32 type, uint32 duration) const
Definition: Unit.cpp:11711
uint8 GetLevel() const
Definition: Unit.h:746
SpellCastResult CastSpell(CastSpellTargetArg const &targets, uint32 spellId, CastSpellExtraArgs const &args={ })
Definition: Object.cpp:2896
std::string const & GetName() const
Definition: Object.h:555
WorldPacket const * Write() override
Definition: NPCPackets.cpp:250
WorldPacket const * Write() override
Definition: NPCPackets.cpp:161
std::vector< TrainerListSpell > Spells
Definition: NPCPackets.h:208
BattlePets::BattlePetMgr * GetBattlePetMgr() const
SpellState
Definition: Trainer.h:41
FailReason
Definition: Trainer.h:48
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:31
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:193