TrinityCore
Loading...
Searching...
No Matches
SkillDiscovery.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 "SkillDiscovery.h"
19#include "DatabaseEnv.h"
20#include "Log.h"
21#include "Player.h"
22#include "Random.h"
23#include "SpellMgr.h"
24#include "SpellInfo.h"
25#include "Util.h"
26#include "World.h"
27#include <map>
28
30{
31 uint32 spellId; // discavered spell
32 uint32 reqSkillValue; // skill level limitation
33 float chance; // chance
34
37
38 SkillDiscoveryEntry(uint32 _spellId, uint32 req_skill_val, float _chance)
39 : spellId(_spellId), reqSkillValue(req_skill_val), chance(_chance) { }
40};
41
42typedef std::list<SkillDiscoveryEntry> SkillDiscoveryList;
43typedef std::unordered_map<int32, SkillDiscoveryList> SkillDiscoveryMap;
44
46
48{
49 uint32 oldMSTime = getMSTime();
50
51 SkillDiscoveryStore.clear(); // need for reload
52
53 // 0 1 2 3
54 QueryResult result = WorldDatabase.Query("SELECT spellId, reqSpell, reqSkillValue, chance FROM skill_discovery_template");
55
56 if (!result)
57 {
58 TC_LOG_INFO("server.loading", ">> Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty.");
59 return;
60 }
61
62 uint32 count = 0;
63
64 std::ostringstream ssNonDiscoverableEntries;
65 std::set<uint32> reportedReqSpells;
66
67 do
68 {
69 Field* fields = result->Fetch();
70
71 uint32 spellId = fields[0].GetUInt32();
72 int32 reqSkillOrSpell = fields[1].GetInt32();
73 uint32 reqSkillValue = fields[2].GetUInt16();
74 float chance = fields[3].GetFloat();
75
76 if (chance <= 0) // chance
77 {
78 ssNonDiscoverableEntries << "spellId = " << spellId << " reqSkillOrSpell = " << reqSkillOrSpell
79 << " reqSkillValue = " << reqSkillValue << " chance = " << chance << "(chance problem)\n";
80 continue;
81 }
82
83 if (reqSkillOrSpell > 0) // spell case
84 {
85 uint32 absReqSkillOrSpell = uint32(reqSkillOrSpell);
86 SpellInfo const* reqSpellInfo = sSpellMgr->GetSpellInfo(absReqSkillOrSpell);
87 if (!reqSpellInfo)
88 {
89 if (reportedReqSpells.find(absReqSkillOrSpell) == reportedReqSpells.end())
90 {
91 TC_LOG_ERROR("sql.sql", "Spell (ID: {}) has a non-existing spell (ID: {}) in `reqSpell` field in the `skill_discovery_template` table.", spellId, reqSkillOrSpell);
92 reportedReqSpells.insert(absReqSkillOrSpell);
93 }
94 continue;
95 }
96
97 // mechanic discovery
98 if (reqSpellInfo->Mechanic != MECHANIC_DISCOVERY &&
99 // explicit discovery ability
100 !reqSpellInfo->IsExplicitDiscovery())
101 {
102 if (reportedReqSpells.find(absReqSkillOrSpell) == reportedReqSpells.end())
103 {
104 TC_LOG_ERROR("sql.sql", "Spell (ID: {}) does not have any MECHANIC_DISCOVERY (28) value in the Mechanic field in spell.dbc"
105 " nor 100% chance random discovery ability, but is listed for spellId {} (and maybe more) in the `skill_discovery_template` table.",
106 absReqSkillOrSpell, spellId);
107 reportedReqSpells.insert(absReqSkillOrSpell);
108 }
109 continue;
110 }
111
112 SkillDiscoveryStore[reqSkillOrSpell].push_back(SkillDiscoveryEntry(spellId, reqSkillValue, chance));
113 }
114 else if (reqSkillOrSpell == 0) // skill case
115 {
116 SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
117
118 if (bounds.first == bounds.second)
119 {
120 TC_LOG_ERROR("sql.sql", "Spell (ID: {}) is not listed in `SkillLineAbility.dbc`, but listed with `reqSpell`= 0 in the `skill_discovery_template` table.", spellId);
121 continue;
122 }
123
124 for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
125 SkillDiscoveryStore[-int32(_spell_idx->second->SkillLine)].push_back(SkillDiscoveryEntry(spellId, reqSkillValue, chance));
126 }
127 else
128 {
129 TC_LOG_ERROR("sql.sql", "Spell (ID: {}) has a negative value in `reqSpell` field in the `skill_discovery_template` table.", spellId);
130 continue;
131 }
132
133 ++count;
134 }
135 while (result->NextRow());
136
137 if (!ssNonDiscoverableEntries.str().empty())
138 TC_LOG_ERROR("sql.sql", "Some items can't be successfully discovered, their chance field value is < 0.000001 in the `skill_discovery_template` DB table. List:\n{}", ssNonDiscoverableEntries.str());
139
140 // report about empty data for explicit discovery spells
141 for (uint32 spell_id = 1; spell_id < sSpellMgr->GetSpellInfoStoreSize(); ++spell_id)
142 {
143 SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spell_id);
144 if (!spellEntry)
145 continue;
146
147 // skip not explicit discovery spells
148 if (!spellEntry->IsExplicitDiscovery())
149 continue;
150
151 if (SkillDiscoveryStore.find(int32(spell_id)) == SkillDiscoveryStore.end())
152 TC_LOG_ERROR("sql.sql", "Spell (ID: {}) has got 100% chance random discovery ability, but does not have data in the `skill_discovery_template` table.", spell_id);
153 }
154
155 TC_LOG_INFO("server.loading", ">> Loaded {} skill discovery definitions in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
156}
157
159{
160 // explicit discovery spell chances (always success if case exist)
161 // in this case we have both skill and spell
162 SkillDiscoveryMap::const_iterator tab = SkillDiscoveryStore.find(int32(spellId));
163 if (tab == SkillDiscoveryStore.end())
164 return 0;
165
166 SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
167 uint32 skillvalue = bounds.first != bounds.second ? player->GetSkillValue(bounds.first->second->SkillLine) : uint32(0);
168
169 float full_chance = 0;
170 for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
171 if (item_iter->reqSkillValue <= skillvalue)
172 if (!player->HasSpell(item_iter->spellId))
173 full_chance += item_iter->chance;
174
175 float rate = full_chance / 100.0f;
176 float roll = (float)rand_chance() * rate; // roll now in range 0..full_chance
177
178 for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
179 {
180 if (item_iter->reqSkillValue > skillvalue)
181 continue;
182
183 if (player->HasSpell(item_iter->spellId))
184 continue;
185
186 if (item_iter->chance > roll)
187 return item_iter->spellId;
188
189 roll -= item_iter->chance;
190 }
191
192 return 0;
193}
194
196{
197 SkillDiscoveryMap::const_iterator tab = SkillDiscoveryStore.find(int32(spellId));
198 if (tab == SkillDiscoveryStore.end())
199 return true;
200
201 for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
202 if (!player->HasSpell(item_iter->spellId))
203 return false;
204
205 return true;
206}
207
208bool HasDiscoveredAnySpell(uint32 spellId, Player* player)
209{
210 SkillDiscoveryMap::const_iterator tab = SkillDiscoveryStore.find(int32(spellId));
211 if (tab == SkillDiscoveryStore.end())
212 return false;
213
214 for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
215 if (player->HasSpell(item_iter->spellId))
216 return true;
217
218 return false;
219}
220
222{
223 uint32 skillvalue = skillId ? player->GetSkillValue(skillId) : uint32(0);
224
225 // check spell case
226 SkillDiscoveryMap::const_iterator tab = SkillDiscoveryStore.find(int32(spellId));
227
228 if (tab != SkillDiscoveryStore.end())
229 {
230 for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
231 {
232 if (roll_chance_f(item_iter->chance * sWorld->getRate(RATE_SKILL_DISCOVERY)) &&
233 item_iter->reqSkillValue <= skillvalue &&
234 !player->HasSpell(item_iter->spellId))
235 return item_iter->spellId;
236 }
237
238 return 0;
239 }
240
241 if (!skillId)
242 return 0;
243
244 // check skill line case
245 tab = SkillDiscoveryStore.find(-(int32)skillId);
246 if (tab != SkillDiscoveryStore.end())
247 {
248 for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
249 {
250 if (roll_chance_f(item_iter->chance * sWorld->getRate(RATE_SKILL_DISCOVERY)) &&
251 item_iter->reqSkillValue <= skillvalue &&
252 !player->HasSpell(item_iter->spellId))
253 return item_iter->spellId;
254 }
255
256 return 0;
257 }
258
259 return 0;
260}
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
int32_t int32
Definition Define.h:129
uint32_t uint32
Definition Define.h:133
#define TC_LOG_ERROR(filterType__,...)
Definition Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition Log.h:159
double rand_chance()
Definition Random.cpp:81
bool roll_chance_f(float chance)
Definition Random.h:53
@ MECHANIC_DISCOVERY
std::list< SkillDiscoveryEntry > SkillDiscoveryList
static SkillDiscoveryMap SkillDiscoveryStore
uint32 GetExplicitDiscoverySpell(uint32 spellId, Player *player)
uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player *player)
std::unordered_map< int32, SkillDiscoveryList > SkillDiscoveryMap
bool HasDiscoveredAllSpells(uint32 spellId, Player *player)
bool HasDiscoveredAnySpell(uint32 spellId, Player *player)
void LoadSkillDiscoveryTable()
#define sSpellMgr
Definition SpellMgr.h:738
std::pair< SkillLineAbilityMap::const_iterator, SkillLineAbilityMap::const_iterator > SkillLineAbilityMapBounds
Definition SpellMgr.h:537
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:57
uint32 getMSTime()
Definition Timer.h:33
Class used to access individual fields of database query result.
Definition Field.h:92
uint16 GetUInt16() const
Definition Field.cpp:45
float GetFloat() const
Definition Field.cpp:93
uint32 GetUInt32() const
Definition Field.cpp:61
int32 GetInt32() const
Definition Field.cpp:69
uint16 GetSkillValue(uint32 skill) const
Definition Player.cpp:5892
bool HasSpell(uint32 spell) const override
Definition Player.cpp:3853
bool IsExplicitDiscovery() const
uint32 Mechanic
Definition SpellInfo.h:292
#define sWorld
Definition World.h:900
@ RATE_SKILL_DISCOVERY
Definition World.h:418
SkillDiscoveryEntry(uint32 _spellId, uint32 req_skill_val, float _chance)