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"
21 #include "ProgressBar.h"
22 #include "Policies/SingletonImp.h"
25 #include "SkillDiscovery.h"
30 struct SkillDiscoveryEntry
32 uint32 spellId
; // discavered spell
33 uint32 reqSkillValue
; // skill level limitation
34 float chance
; // chance
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
56 QueryResult
*result
= WorldDatabase
.Query("SELECT spellId, reqSpell, reqSkillValue, chance FROM skill_discovery_template");
61 sLog
.outString( ">> Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty." );
65 barGoLink
bar(result
->GetRowCount());
67 std::ostringstream ssNonDiscoverableEntries
;
68 std::set
<uint32
> reportedReqSpells
;
72 Field
*fields
= result
->Fetch();
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";
87 if (reqSkillOrSpell
> 0) // spell case
89 SpellEntry
const* reqSpellEntry
= sSpellStore
.LookupEntry(reqSkillOrSpell
);
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
);
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
);
115 SkillDiscoveryStore
[reqSkillOrSpell
].push_back( SkillDiscoveryEntry(spellId
, reqSkillValue
, chance
) );
117 else if (reqSkillOrSpell
== 0) // skill case
119 SkillLineAbilityMapBounds bounds
= sSpellMgr
.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
);
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
) );
132 sLog
.outErrorDb("Spell (ID: %u) have negative value in `reqSpell` field in `skill_discovery_template` table",spellId
);
137 } while (result
->NextRow());
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
);
153 // skip not explicit discovery spells
154 if (!IsExplicitDiscoverySpell(spellEntry
))
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())
170 SkillLineAbilityMapBounds bounds
= sSpellMgr
.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
)
187 if (player
->HasSpell(item_iter
->spellId
))
190 if (item_iter
->chance
> roll
)
191 return item_iter
->spellId
;
193 roll
-= item_iter
->chance
;
199 uint32
GetSkillDiscoverySpell(uint32 skillId
, uint32 spellId
, Player
* player
)
201 uint32 skillvalue
= skillId
? player
->GetSkillValue(skillId
) : 0;
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
;
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
;