[7770] Prevent access to possible deleted aura's spell proto in Aura::HandleModStealth.
[AHbot.git] / src / game / SkillDiscovery.cpp
blob32b75eab72f0f1d07e0ebd028de2cbdee8d74876
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 <map>
29 struct SkillDiscoveryEntry
31 uint32 spellId; // discavered spell
32 uint32 reqSkillValue; // skill level limitation
33 float chance; // chance
35 SkillDiscoveryEntry()
36 : spellId(0), reqSkillValue(0), chance(0) {}
38 SkillDiscoveryEntry(uint16 _spellId, uint32 req_skill_val, float _chance)
39 : spellId(_spellId), reqSkillValue(req_skill_val), chance(_chance) {}
42 typedef std::list<SkillDiscoveryEntry> SkillDiscoveryList;
43 typedef UNORDERED_MAP<int32, SkillDiscoveryList> SkillDiscoveryMap;
45 static SkillDiscoveryMap SkillDiscoveryStore;
47 void LoadSkillDiscoveryTable()
50 SkillDiscoveryStore.clear(); // need for reload
52 uint32 count = 0;
54 // 0 1 2 3
55 QueryResult *result = WorldDatabase.Query("SELECT spellId, reqSpell, reqSkillValue, chance FROM skill_discovery_template");
57 if (result)
59 barGoLink bar(result->GetRowCount());
61 std::ostringstream ssNonDiscoverableEntries;
65 Field *fields = result->Fetch();
66 bar.step();
68 uint32 spellId = fields[0].GetUInt32();
69 int32 reqSkillOrSpell = fields[1].GetInt32();
70 uint32 reqSkillValue = fields[2].GetInt32();
71 float chance = fields[3].GetFloat();
73 if( chance <= 0 ) // chance
75 ssNonDiscoverableEntries << "spellId = " << spellId << " reqSkillOrSpell = " << reqSkillOrSpell
76 << " reqSkillValue = " << reqSkillValue << " chance = " << chance << "(chance problem)\n";
77 continue;
80 if(reqSkillOrSpell > 0) // spell case
82 SpellEntry const* spellEntry = sSpellStore.LookupEntry(reqSkillOrSpell);
83 if( !spellEntry )
85 sLog.outErrorDb("Spell (ID: %u) have not existed spell (ID: %i) in `reqSpell` field in `skill_discovery_template` table",spellId,reqSkillOrSpell);
86 continue;
89 // mechanic discovery
90 if (spellEntry->Mechanic != MECHANIC_DISCOVERY &&
91 // explicit discovery ability
92 !IsExplicitDiscoverySpell(spellEntry))
94 sLog.outErrorDb("Spell (ID: %u) not have have MECHANIC_DISCOVERY (28) value in Mechanic field in spell.dbc and not 100%% chance random discovery ability but listed in `skill_discovery_template` table",spellId);
95 continue;
98 SkillDiscoveryStore[reqSkillOrSpell].push_back( SkillDiscoveryEntry(spellId, reqSkillValue, chance) );
100 else if( reqSkillOrSpell == 0 ) // skill case
102 SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellId);
103 SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellId);
105 if(lower==upper)
107 sLog.outErrorDb("Spell (ID: %u) not listed in `SkillLineAbility.dbc` but listed with `reqSpell`=0 in `skill_discovery_template` table",spellId);
108 continue;
111 for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
113 SkillDiscoveryStore[-int32(_spell_idx->second->skillId)].push_back( SkillDiscoveryEntry(spellId, reqSkillValue, chance) );
116 else
118 sLog.outErrorDb("Spell (ID: %u) have negative value in `reqSpell` field in `skill_discovery_template` table",spellId);
119 continue;
122 ++count;
123 } while (result->NextRow());
125 delete result;
127 sLog.outString();
128 sLog.outString( ">> Loaded %u skill discovery definitions", count );
129 if(!ssNonDiscoverableEntries.str().empty())
130 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());
132 else
134 sLog.outString();
135 sLog.outString( ">> Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty." );
139 uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player)
141 // explicit discovery spell chances (always success if case exist)
142 // in this case we have both skill and spell
143 SkillDiscoveryMap::const_iterator tab = SkillDiscoveryStore.find(spellId);
144 if(tab == SkillDiscoveryStore.end())
145 return 0;
147 SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellId);
148 SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellId);
149 uint32 skillvalue = lower != upper ? player->GetSkillValue(lower->second->skillId) : 0;
151 float full_chance = 0;
152 for(SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
153 if(item_iter->reqSkillValue <= skillvalue)
154 if(!player->HasSpell(item_iter->spellId))
155 full_chance += item_iter->chance;
157 float rate = full_chance / 100.0f;
158 float roll = rand_chance() * rate; // roll now in range 0..full_chance
160 for(SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
162 if(item_iter->reqSkillValue > skillvalue)
163 continue;
165 if(player->HasSpell(item_iter->spellId))
166 continue;
168 if(item_iter->chance > roll)
169 return item_iter->spellId;
171 roll -= item_iter->chance;
174 return 0;
177 uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player)
179 uint32 skillvalue = skillId ? player->GetSkillValue(skillId) : 0;
181 // check spell case
182 SkillDiscoveryMap::const_iterator tab = SkillDiscoveryStore.find(spellId);
184 if(tab != SkillDiscoveryStore.end())
186 for(SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
188 if( roll_chance_f(item_iter->chance * sWorld.getRate(RATE_SKILL_DISCOVERY))
189 && item_iter->reqSkillValue <= skillvalue
190 && !player->HasSpell(item_iter->spellId) )
191 return item_iter->spellId;
194 return 0;
197 if(!skillId)
198 return 0;
200 // check skill line case
201 tab = SkillDiscoveryStore.find(-(int32)skillId);
202 if(tab != SkillDiscoveryStore.end())
204 for(SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
206 if( roll_chance_f(item_iter->chance * sWorld.getRate(RATE_SKILL_DISCOVERY))
207 && item_iter->reqSkillValue <= skillvalue
208 && !player->HasSpell(item_iter->spellId) )
209 return item_iter->spellId;
212 return 0;
215 return 0;