Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
SkillDiscovery.cpp
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
6#include "DatabaseEnv.h"
7#include "Log.h"
8#include "Player.h"
9#include "SkillDiscovery.h"
10#include "SpellInfo.h"
11#include "SpellMgr.h"
12#include "Util.h"
13#include "World.h"
14#include <map>
15
17{
18 uint32 spellId; // discavered spell
19 uint32 reqSkillValue; // skill level limitation
20 float chance; // chance
21
24
25 SkillDiscoveryEntry(uint32 _spellId, uint32 req_skill_val, float _chance)
26 : spellId(_spellId), reqSkillValue(req_skill_val), chance(_chance) { }
27};
28
29typedef std::list<SkillDiscoveryEntry> SkillDiscoveryList;
31
33
35{
36 uint32 oldMSTime = getMSTime();
37
38 SkillDiscoveryStore.clear(); // need for reload
39
40 // 0 1 2 3
41 QueryResult result = WorldDatabase.Query("SELECT spellId, reqSpell, reqSkillValue, chance FROM skill_discovery_template");
42
43 if (!result)
44 {
45 SF_LOG_ERROR("server.loading", ">> Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty.");
46 return;
47 }
48
49 uint32 count = 0;
50
51 std::ostringstream ssNonDiscoverableEntries;
52 std::set<uint32> reportedReqSpells;
53
54 do
55 {
56 Field* fields = result->Fetch();
57
58 uint32 spellId = fields[0].GetUInt32();
59 int32 reqSkillOrSpell = fields[1].GetInt32();
60 uint32 reqSkillValue = fields[2].GetUInt16();
61 float chance = fields[3].GetFloat();
62
63 if (chance <= 0) // chance
64 {
65 ssNonDiscoverableEntries << "spellId = " << spellId << " reqSkillOrSpell = " << reqSkillOrSpell
66 << " reqSkillValue = " << reqSkillValue << " chance = " << chance << "(chance problem)\n";
67 continue;
68 }
69
70 if (reqSkillOrSpell > 0) // spell case
71 {
72 uint32 absReqSkillOrSpell = uint32(reqSkillOrSpell);
73 SpellInfo const* reqSpellInfo = sSpellMgr->GetSpellInfo(absReqSkillOrSpell);
74 if (!reqSpellInfo)
75 {
76 if (reportedReqSpells.find(absReqSkillOrSpell) == reportedReqSpells.end())
77 {
78 SF_LOG_ERROR("sql.sql", "Spell (ID: %u) have not existed spell (ID: %i) in `reqSpell` field in `skill_discovery_template` table", spellId, reqSkillOrSpell);
79 reportedReqSpells.insert(absReqSkillOrSpell);
80 }
81 continue;
82 }
83
84 // mechanic discovery
85 if (reqSpellInfo->Mechanic != MECHANIC_DISCOVERY &&
86 // explicit discovery ability
87 !reqSpellInfo->IsExplicitDiscovery())
88 {
89 if (reportedReqSpells.find(absReqSkillOrSpell) == reportedReqSpells.end())
90 {
91 SF_LOG_ERROR("sql.sql", "Spell (ID: %u) not have MECHANIC_DISCOVERY (28) value in Mechanic field in spell.dbc"
92 " and not 100%% chance random discovery ability but listed for spellId %u (and maybe more) in `skill_discovery_template` table",
93 absReqSkillOrSpell, spellId);
94 reportedReqSpells.insert(absReqSkillOrSpell);
95 }
96 continue;
97 }
98
99 SkillDiscoveryStore[reqSkillOrSpell].push_back(SkillDiscoveryEntry(spellId, reqSkillValue, chance));
100 }
101 else if (reqSkillOrSpell == 0) // skill case
102 {
103 SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
104
105 if (bounds.first == bounds.second)
106 {
107 SF_LOG_ERROR("sql.sql", "Spell (ID: %u) not listed in `SkillLineAbility.dbc` but listed with `reqSpell`=0 in `skill_discovery_template` table", spellId);
108 continue;
109 }
110
111 for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
112 SkillDiscoveryStore[-int32(_spell_idx->second->skillId)].push_back(SkillDiscoveryEntry(spellId, reqSkillValue, chance));
113 }
114 else
115 {
116 SF_LOG_ERROR("sql.sql", "Spell (ID: %u) have negative value in `reqSpell` field in `skill_discovery_template` table", spellId);
117 continue;
118 }
119
120 ++count;
121 } while (result->NextRow());
122
123 if (!ssNonDiscoverableEntries.str().empty())
124 SF_LOG_ERROR("sql.sql", "Some items can't be successfully discovered: have in chance field value < 0.000001 in `skill_discovery_template` DB table . List:\n%s", ssNonDiscoverableEntries.str().c_str());
125
126 // report about empty data for explicit discovery spells
127 for (uint32 spell_id = 1; spell_id < sSpellMgr->GetSpellInfoStoreSize(); ++spell_id)
128 {
129 SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spell_id);
130 if (!spellEntry)
131 continue;
132
133 // skip not explicit discovery spells
134 if (!spellEntry->IsExplicitDiscovery())
135 continue;
136
137 if (SkillDiscoveryStore.find(int32(spell_id)) == SkillDiscoveryStore.end())
138 SF_LOG_ERROR("sql.sql", "Spell (ID: %u) is 100%% chance random discovery ability but not have data in `skill_discovery_template` table", spell_id);
139 }
140
141 SF_LOG_INFO("server.loading", ">> Loaded %u skill discovery definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
142}
143
145{
146 // explicit discovery spell chances (always success if case exist)
147 // in this case we have both skill and spell
148 SkillDiscoveryMap::const_iterator tab = SkillDiscoveryStore.find(int32(spellId));
149 if (tab == SkillDiscoveryStore.end())
150 return 0;
151
152 SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
153 uint32 skillvalue = bounds.first != bounds.second ? player->GetSkillValue(bounds.first->second->skillId) : uint32(0);
154
155 float full_chance = 0;
156 for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
157 if (item_iter->reqSkillValue <= skillvalue)
158 if (!player->HasSpell(item_iter->spellId))
159 full_chance += item_iter->chance;
160
161 float rate = full_chance / 100.0f;
162 float roll = (float)rand_chance() * rate; // roll now in range 0..full_chance
163
164 for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
165 {
166 if (item_iter->reqSkillValue > skillvalue)
167 continue;
168
169 if (player->HasSpell(item_iter->spellId))
170 continue;
171
172 if (item_iter->chance > roll)
173 return item_iter->spellId;
174
175 roll -= item_iter->chance;
176 }
177
178 return 0;
179}
180
182{
183 SkillDiscoveryMap::const_iterator tab = SkillDiscoveryStore.find(int32(spellId));
184 if (tab == SkillDiscoveryStore.end())
185 return true;
186
187 for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
188 if (!player->HasSpell(item_iter->spellId))
189 return false;
190
191 return true;
192}
193
195{
196 uint32 skillvalue = skillId ? player->GetSkillValue(skillId) : uint32(0);
197
198 // check spell case
199 SkillDiscoveryMap::const_iterator tab = SkillDiscoveryStore.find(int32(spellId));
200
201 if (tab != SkillDiscoveryStore.end())
202 {
203 for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
204 {
205 if (roll_chance_f(item_iter->chance * sWorld->getRate(Rates::RATE_SKILL_DISCOVERY)) &&
206 item_iter->reqSkillValue <= skillvalue &&
207 !player->HasSpell(item_iter->spellId))
208 return item_iter->spellId;
209 }
210
211 return 0;
212 }
213
214 if (!skillId)
215 return 0;
216
217 // check skill line case
218 tab = SkillDiscoveryStore.find(-(int32)skillId);
219 if (tab != SkillDiscoveryStore.end())
220 {
221 for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
222 {
223 if (roll_chance_f(item_iter->chance * sWorld->getRate(Rates::RATE_SKILL_DISCOVERY)) &&
224 item_iter->reqSkillValue <= skillvalue &&
225 !player->HasSpell(item_iter->spellId))
226 return item_iter->spellId;
227 }
228
229 return 0;
230 }
231
232 return 0;
233}
std::int32_t int32
Definition Define.h:73
std::uint32_t uint32
Definition Define.h:77
#define SF_LOG_ERROR(filterType__,...)
Definition Log.h:143
#define SF_LOG_INFO(filterType__,...)
Definition Log.h:137
Skyfire::AutoPtr< ResultSet, Skyfire::Mutex > QueryResult
Definition QueryResult.h:48
@ MECHANIC_DISCOVERY
std::list< SkillDiscoveryEntry > SkillDiscoveryList
static SkillDiscoveryMap SkillDiscoveryStore
uint32 GetExplicitDiscoverySpell(uint32 spellId, Player *player)
UNORDERED_MAP< int32, SkillDiscoveryList > SkillDiscoveryMap
uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player *player)
bool HasDiscoveredAllSpells(uint32 spellId, Player *player)
void LoadSkillDiscoveryTable()
#define sSpellMgr
Definition SpellMgr.h:744
std::pair< SkillLineAbilityMap::const_iterator, SkillLineAbilityMap::const_iterator > SkillLineAbilityMapBounds
Definition SpellMgr.h:544
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:22
uint32 getMSTime()
Definition Timer.h:12
#define UNORDERED_MAP
double rand_chance(void)
Definition Util.cpp:55
bool roll_chance_f(float chance)
Definition Util.h:83
Definition Field.h:16
uint16 GetUInt16() const
Definition Field.h:69
float GetFloat() const
Definition Field.h:177
uint32 GetUInt32() const
Definition Field.h:105
int32 GetInt32() const
Definition Field.h:123
uint16 GetSkillValue(uint32 skill) const
Definition Player.cpp:6563
bool HasSpell(uint32 spell) const override
Definition Player.cpp:4674
bool IsExplicitDiscovery() const
uint32 Mechanic
Definition SpellInfo.h:163
WorldDatabaseWorkerPool WorldDatabase
Accessor to the world database.
Definition Main.cpp:40
#define sWorld
Definition World.h:910
@ RATE_SKILL_DISCOVERY
Definition World.h:386
SkillDiscoveryEntry(uint32 _spellId, uint32 req_skill_val, float _chance)
SkillDiscoveryEntry()
uint32 reqSkillValue
float chance
uint32 spellId