[8483] Implement glyph 43361.
[getmangos.git] / src / game / SkillDiscovery.cpp
blob91c915ff650e69010a8a88c853b55ecbed59e646
1 /*
2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "Database/DatabaseEnv.h"
20 #include "Log.h"
21 #include "ProgressBar.h"
22 #include "Policies/SingletonImp.h"
23 #include "World.h"
24 #include "Util.h"
25 #include "SkillDiscovery.h"
26 #include "SpellMgr.h"
27 #include "Player.h"
28 #include <map>
30 struct SkillDiscoveryEntry
32 uint32 spellId; // discavered spell
33 uint32 reqSkillValue; // skill level limitation
34 float chance; // chance
36 SkillDiscoveryEntry()
37 : spellId(0), reqSkillValue(0), chance(0) {}
39 SkillDiscoveryEntry(uint32 _spellId, uint32 req_skill_val, float _chance)
40 : spellId(_spellId), reqSkillValue(req_skill_val), chance(_chance) {}
43 typedef std::list<SkillDiscoveryEntry> SkillDiscoveryList;
44 typedef UNORDERED_MAP<int32, SkillDiscoveryList> SkillDiscoveryMap;
46 static SkillDiscoveryMap SkillDiscoveryStore;
48 void LoadSkillDiscoveryTable()
51 SkillDiscoveryStore.clear(); // need for reload
53 uint32 count = 0;
55 // 0 1 2 3
56 QueryResult *result = WorldDatabase.Query("SELECT spellId, reqSpell, reqSkillValue, chance FROM skill_discovery_template");
58 if (!result)
60 sLog.outString();
61 sLog.outString( ">> Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty." );
62 return;
65 barGoLink bar(result->GetRowCount());
67 std::ostringstream ssNonDiscoverableEntries;
68 std::set<uint32> reportedReqSpells;
72 Field *fields = result->Fetch();
73 bar.step();
75 uint32 spellId = fields[0].GetUInt32();
76 int32 reqSkillOrSpell = fields[1].GetInt32();
77 uint32 reqSkillValue = fields[2].GetInt32();
78 float chance = fields[3].GetFloat();
80 if (chance <= 0) // chance
82 ssNonDiscoverableEntries << "spellId = " << spellId << " reqSkillOrSpell = " << reqSkillOrSpell
83 << " reqSkillValue = " << reqSkillValue << " chance = " << chance << "(chance problem)\n";
84 continue;
87 if (reqSkillOrSpell > 0) // spell case
89 SpellEntry const* reqSpellEntry = sSpellStore.LookupEntry(reqSkillOrSpell);
90 if (!reqSpellEntry)
92 if(reportedReqSpells.count(reqSkillOrSpell)==0)
94 sLog.outErrorDb("Spell (ID: %u) have not existed spell (ID: %i) in `reqSpell` field in `skill_discovery_template` table",spellId,reqSkillOrSpell);
95 reportedReqSpells.insert(reqSkillOrSpell);
97 continue;
100 // mechanic discovery
101 if (reqSpellEntry->Mechanic != MECHANIC_DISCOVERY &&
102 // explicit discovery ability
103 !IsExplicitDiscoverySpell(reqSpellEntry))
105 if (reportedReqSpells.count(reqSkillOrSpell)==0)
107 sLog.outErrorDb("Spell (ID: %u) not have MECHANIC_DISCOVERY (28) value in Mechanic field in spell.dbc"
108 " and not 100%% chance random discovery ability but listed for spellId %u (and maybe more) in `skill_discovery_template` table",
109 reqSkillOrSpell,spellId);
110 reportedReqSpells.insert(reqSkillOrSpell);
112 continue;
115 SkillDiscoveryStore[reqSkillOrSpell].push_back( SkillDiscoveryEntry(spellId, reqSkillValue, chance) );
117 else if (reqSkillOrSpell == 0) // skill case
119 SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellId);
121 if (bounds.first==bounds.second)
123 sLog.outErrorDb("Spell (ID: %u) not listed in `SkillLineAbility.dbc` but listed with `reqSpell`=0 in `skill_discovery_template` table",spellId);
124 continue;
127 for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
128 SkillDiscoveryStore[-int32(_spell_idx->second->skillId)].push_back( SkillDiscoveryEntry(spellId, reqSkillValue, chance) );
130 else
132 sLog.outErrorDb("Spell (ID: %u) have negative value in `reqSpell` field in `skill_discovery_template` table",spellId);
133 continue;
136 ++count;
137 } while (result->NextRow());
139 delete result;
141 sLog.outString();
142 sLog.outString( ">> Loaded %u skill discovery definitions", count );
143 if (!ssNonDiscoverableEntries.str().empty())
144 sLog.outErrorDb("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());
146 // report about empty data for explicit discovery spells
147 for(uint32 spell_id = 1; spell_id < sSpellStore.GetNumRows(); ++spell_id)
149 SpellEntry const* spellEntry = sSpellStore.LookupEntry(spell_id);
150 if (!spellEntry)
151 continue;
153 // skip not explicit discovery spells
154 if (!IsExplicitDiscoverySpell(spellEntry))
155 continue;
157 if (SkillDiscoveryStore.find(spell_id)==SkillDiscoveryStore.end())
158 sLog.outErrorDb("Spell (ID: %u) is 100%% chance random discovery ability but not have data in `skill_discovery_template` table",spell_id);
162 uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player)
164 // explicit discovery spell chances (always success if case exist)
165 // in this case we have both skill and spell
166 SkillDiscoveryMap::const_iterator tab = SkillDiscoveryStore.find(spellId);
167 if (tab == SkillDiscoveryStore.end())
168 return 0;
170 SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellId);
171 uint32 skillvalue = bounds.first != bounds.second ? player->GetSkillValue(bounds.first->second->skillId) : 0;
173 float full_chance = 0;
174 for(SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
175 if (item_iter->reqSkillValue <= skillvalue)
176 if (!player->HasSpell(item_iter->spellId))
177 full_chance += item_iter->chance;
179 float rate = full_chance / 100.0f;
180 float roll = rand_chance() * rate; // roll now in range 0..full_chance
182 for(SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
184 if (item_iter->reqSkillValue > skillvalue)
185 continue;
187 if (player->HasSpell(item_iter->spellId))
188 continue;
190 if (item_iter->chance > roll)
191 return item_iter->spellId;
193 roll -= item_iter->chance;
196 return 0;
199 uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player)
201 uint32 skillvalue = skillId ? player->GetSkillValue(skillId) : 0;
203 // check spell case
204 SkillDiscoveryMap::const_iterator tab = SkillDiscoveryStore.find(spellId);
206 if (tab != SkillDiscoveryStore.end())
208 for(SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
210 if (roll_chance_f(item_iter->chance * sWorld.getRate(RATE_SKILL_DISCOVERY)) &&
211 item_iter->reqSkillValue <= skillvalue &&
212 !player->HasSpell(item_iter->spellId))
213 return item_iter->spellId;
216 return 0;
219 if (!skillId)
220 return 0;
222 // check skill line case
223 tab = SkillDiscoveryStore.find(-(int32)skillId);
224 if (tab != SkillDiscoveryStore.end())
226 for(SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
228 if (roll_chance_f(item_iter->chance * sWorld.getRate(RATE_SKILL_DISCOVERY)) &&
229 item_iter->reqSkillValue <= skillvalue &&
230 !player->HasSpell(item_iter->spellId))
231 return item_iter->spellId;
234 return 0;
237 return 0;