AzerothCore 3.3.5a
OpenSource WoW Emulator
Loading...
Searching...
No Matches
SkillDiscovery.cpp File Reference
#include "SkillDiscovery.h"
#include "DatabaseEnv.h"
#include "Log.h"
#include "Player.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
#include "Util.h"
#include "World.h"
#include <map>

Go to the source code of this file.

Classes

struct  SkillDiscoveryEntry
 

Typedefs

typedef std::list< SkillDiscoveryEntrySkillDiscoveryList
 
typedef std::unordered_map< int32, SkillDiscoveryListSkillDiscoveryMap
 

Functions

void LoadSkillDiscoveryTable ()
 
uint32 GetExplicitDiscoverySpell (uint32 spellId, Player *player)
 
bool HasDiscoveredAllSpells (uint32 spellId, Player *player)
 
uint32 GetSkillDiscoverySpell (uint32 skillId, uint32 spellId, Player *player)
 

Variables

static SkillDiscoveryMap SkillDiscoveryStore
 

Typedef Documentation

◆ SkillDiscoveryList

◆ SkillDiscoveryMap

typedef std::unordered_map<int32, SkillDiscoveryList> SkillDiscoveryMap

Function Documentation

◆ GetExplicitDiscoverySpell()

uint32 GetExplicitDiscoverySpell ( uint32  spellId,
Player player 
)
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 {
188 // Update skill, not Book of Glyph Mastery
189 if (spellId != 64323)
190 player->UpdateGatherSkill(SKILL_INSCRIPTION, player->GetPureSkillValue(SKILL_INSCRIPTION), item_iter->reqSkillValue);
191 return item_iter->spellId;
192 }
193
194 roll -= item_iter->chance;
195 }
196
197 return 0;
198}
std::int32_t int32
Definition: Define.h:104
std::uint32_t uint32
Definition: Define.h:108
double rand_chance()
Definition: Random.cpp:83
static SkillDiscoveryMap SkillDiscoveryStore
Definition: SkillDiscovery.cpp:44
#define sSpellMgr
Definition: SpellMgr.h:825
std::pair< SkillLineAbilityMap::const_iterator, SkillLineAbilityMap::const_iterator > SkillLineAbilityMapBounds
Definition: SpellMgr.h:584
@ SKILL_INSCRIPTION
Definition: SharedDefines.h:3002
uint16 GetSkillValue(uint32 skill) const
Definition: Player.cpp:5367
uint16 GetPureSkillValue(uint32 skill) const
Definition: Player.cpp:5432
bool HasSpell(uint32 spell) const override
Definition: Player.cpp:3831
bool UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLevel, uint32 Multiplicator=1)
Definition: PlayerUpdates.cpp:723

References Player::GetPureSkillValue(), Player::GetSkillValue(), Player::HasSpell(), rand_chance(), SKILL_INSCRIPTION, SkillDiscoveryStore, sSpellMgr, and Player::UpdateGatherSkill().

Referenced by spell_gen_profession_research::HandleScript(), and spell_item_book_of_glyph_mastery::HandleScript().

◆ GetSkillDiscoverySpell()

uint32 GetSkillDiscoverySpell ( uint32  skillId,
uint32  spellId,
Player player 
)
214{
215 uint32 skillvalue = skillId ? player->GetSkillValue(skillId) : uint32(0);
216
217 // check spell case
218 SkillDiscoveryMap::const_iterator tab = SkillDiscoveryStore.find(int32(spellId));
219
220 if (tab != SkillDiscoveryStore.end())
221 {
222 for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
223 {
224 if (roll_chance_f(item_iter->chance * sWorld->getRate(RATE_SKILL_DISCOVERY)) &&
225 item_iter->reqSkillValue <= skillvalue &&
226 !player->HasSpell(item_iter->spellId))
227 return item_iter->spellId;
228 }
229
230 return 0;
231 }
232
233 if (!skillId)
234 return 0;
235
236 // check skill line case
237 tab = SkillDiscoveryStore.find(-(int32)skillId);
238 if (tab != SkillDiscoveryStore.end())
239 {
240 for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
241 {
242 if (roll_chance_f(item_iter->chance * sWorld->getRate(RATE_SKILL_DISCOVERY)) &&
243 item_iter->reqSkillValue <= skillvalue &&
244 !player->HasSpell(item_iter->spellId))
245 return item_iter->spellId;
246 }
247
248 return 0;
249 }
250
251 return 0;
252}
bool roll_chance_f(float chance)
Definition: Random.h:53
@ RATE_SKILL_DISCOVERY
Definition: IWorld.h:435
#define sWorld
Definition: World.h:447

References Player::GetSkillValue(), Player::HasSpell(), RATE_SKILL_DISCOVERY, roll_chance_f(), SkillDiscoveryStore, and sWorld.

Referenced by Player::UpdateCraftSkill().

◆ HasDiscoveredAllSpells()

bool HasDiscoveredAllSpells ( uint32  spellId,
Player player 
)
201{
202 SkillDiscoveryMap::const_iterator tab = SkillDiscoveryStore.find(int32(spellId));
203 if (tab == SkillDiscoveryStore.end())
204 return true;
205
206 for (SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
207 if (!player->HasSpell(item_iter->spellId))
208 return false;
209
210 return true;
211}

References Player::HasSpell(), and SkillDiscoveryStore.

Referenced by spell_gen_profession_research::CheckRequirement(), and spell_item_book_of_glyph_mastery::CheckRequirement().

◆ LoadSkillDiscoveryTable()

void LoadSkillDiscoveryTable ( )
47{
48 uint32 oldMSTime = getMSTime();
49
50 SkillDiscoveryStore.clear(); // need for reload
51
52 // 0 1 2 3
53 QueryResult result = WorldDatabase.Query("SELECT spellId, reqSpell, reqSkillValue, chance FROM skill_discovery_template");
54
55 if (!result)
56 {
57 LOG_WARN("server.loading", ">> Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty.");
58 LOG_INFO("server.loading", " ");
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].Get<uint32>();
72 int32 reqSkillOrSpell = fields[1].Get<int32>();
73 uint32 reqSkillValue = fields[2].Get<uint16>();
74 float chance = fields[3].Get<float>();
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 LOG_ERROR("sql.sql", "Spell (ID: {}) have not existed spell (ID: {}) in `reqSpell` field in `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 LOG_ERROR("sql.sql", "Spell (ID: {}) not have MECHANIC_DISCOVERY (28) value in Mechanic field in spell.dbc"
105 " and not 100% chance random discovery ability but listed for spellId {} (and maybe more) in `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 LOG_ERROR("sql.sql", "Spell (ID: {}) not listed in `SkillLineAbility.dbc` but listed with `reqSpell`=0 in `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 LOG_ERROR("sql.sql", "Spell (ID: {}) have negative value in `reqSpell` field in `skill_discovery_template` table", spellId);
130 continue;
131 }
132
133 ++count;
134 } while (result->NextRow());
135
136 if (!ssNonDiscoverableEntries.str().empty())
137 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{}", ssNonDiscoverableEntries.str());
138
139 // report about empty data for explicit discovery spells
140 for (uint32 spell_id = 1; spell_id < sSpellMgr->GetSpellInfoStoreSize(); ++spell_id)
141 {
142 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id);
143 if (!spellInfo)
144 continue;
145
146 // skip not explicit discovery spells
147 if (!spellInfo->IsExplicitDiscovery())
148 continue;
149
150 if (SkillDiscoveryStore.find(int32(spell_id)) == SkillDiscoveryStore.end())
151 LOG_ERROR("sql.sql", "Spell (ID: {}) is 100% chance random discovery ability but not have data in `skill_discovery_template` table", spell_id);
152 }
153
154 LOG_INFO("server.loading", ">> Loaded {} skill discovery definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
155 LOG_INFO("server.loading", " ");
156}
std::uint16_t uint16
Definition: Define.h:109
#define LOG_INFO(filterType__,...)
Definition: Log.h:165
#define LOG_ERROR(filterType__,...)
Definition: Log.h:157
#define LOG_WARN(filterType__,...)
Definition: Log.h:161
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition: Timer.h:131
uint32 getMSTime()
Definition: Timer.h:103
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
Definition: DatabaseEnv.cpp:20
std::shared_ptr< ResultSet > QueryResult
Definition: DatabaseEnvFwd.h:28
@ MECHANIC_DISCOVERY
Definition: SharedDefines.h:1353
Class used to access individual fields of database query result.
Definition: Field.h:99
std::enable_if_t< std::is_arithmetic_v< T >, T > Get() const
Definition: Field.h:113
Definition: SkillDiscovery.cpp:29
Definition: SpellInfo.h:314
bool IsExplicitDiscovery() const
Definition: SpellInfo.cpp:904
uint32 Mechanic
Definition: SpellInfo.h:321

References Field::Get(), getMSTime(), GetMSTimeDiffToNow(), SpellInfo::IsExplicitDiscovery(), LOG_ERROR, LOG_INFO, LOG_WARN, SpellInfo::Mechanic, MECHANIC_DISCOVERY, SkillDiscoveryStore, sSpellMgr, and WorldDatabase.

Referenced by reload_commandscript::HandleReloadSkillDiscoveryTemplateCommand(), and World::SetInitialWorldSettings().

Variable Documentation

◆ SkillDiscoveryStore