Traits.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  1. /*
  2. EQ2Emulator: Everquest II Server Emulator
  3. Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
  4. This file is part of EQ2Emulator.
  5. EQ2Emulator is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. EQ2Emulator 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.
  13. You should have received a copy of the GNU General Public License
  14. along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include "Traits.h"
  17. #include "../../common/ConfigReader.h"
  18. #include "../../common/Log.h"
  19. #include "../Spells.h"
  20. #include "../WorldDatabase.h"
  21. #include "../client.h"
  22. #include "../Rules/Rules.h"
  23. #include <boost/assign.hpp>
  24. #include <map>
  25. extern ConfigReader configReader;
  26. extern MasterSpellList master_spell_list;
  27. extern WorldDatabase database;
  28. extern RuleManager rule_manager;
  29. using namespace boost::assign;
  30. MasterTraitList::MasterTraitList(){
  31. MMasterTraitList.SetName("MasterTraitList::TraitList");
  32. }
  33. MasterTraitList::~MasterTraitList(){
  34. DestroyTraits();
  35. }
  36. void MasterTraitList::AddTrait(TraitData* data){
  37. MMasterTraitList.writelock(__FUNCTION__, __LINE__);
  38. TraitList.push_back(data);
  39. MMasterTraitList.releasewritelock(__FUNCTION__, __LINE__);
  40. }
  41. int MasterTraitList::Size(){
  42. return TraitList.size();
  43. }
  44. bool MasterTraitList::GenerateTraitLists(Client* client, map <int8, map <int8, vector<TraitData*> > >* sortedTraitList, map <int8, vector<TraitData*> >* classTraining,
  45. map <int8, vector<TraitData*> >* raceTraits, map <int8, vector<TraitData*> >* innateRaceTraits, map <int8, vector<TraitData*> >* focusEffects, int16 max_level, int8 trait_group)
  46. {
  47. if (!client) {
  48. LogWrite(SPELL__ERROR, 0, "Traits", "GetTraitListPacket called with an invalid client");
  49. return false;
  50. }
  51. // Sort the Data
  52. if (Size() == 0 || !sortedTraitList || !classTraining || !raceTraits || !innateRaceTraits || !focusEffects)
  53. return false;
  54. map <int8, map <int8, vector<TraitData*> > >::iterator itr;
  55. map <int8, vector<TraitData*> >::iterator itr2;
  56. vector<TraitData*>::iterator itr3;
  57. MMasterTraitList.readlock(__FUNCTION__, __LINE__);
  58. for (int i=0; i < Size(); i++) {
  59. if(max_level > 0 && TraitList[i]->level > max_level) {
  60. continue; // skip per max level requirement
  61. }
  62. if(trait_group != 255 && trait_group != TraitList[i]->group) {
  63. continue;
  64. }
  65. // Sort Character Traits
  66. if (TraitList[i]->classReq == 255 && TraitList[i]->raceReq == 255 && !TraitList[i]->isFocusEffect && TraitList[i]->isTrait) {
  67. itr = sortedTraitList->lower_bound(TraitList[i]->group);
  68. if (itr != sortedTraitList->end() && !(sortedTraitList->key_comp()(TraitList[i]->group, itr->first))) {
  69. itr2 = (itr->second).lower_bound(TraitList[i]->level);
  70. if (itr2 != (itr->second).end() && !((itr->second).key_comp()(TraitList[i]->level, itr2->first))) {
  71. ((itr->second)[itr2->first]).push_back(TraitList[i]);
  72. LogWrite(SPELL__INFO, 0, "Traits", "Added Trait: %u Tier %i", TraitList[i]->spellID, TraitList[i]->tier);
  73. }
  74. else {
  75. vector<TraitData*> tempVec;
  76. tempVec.push_back(TraitList[i]);
  77. (itr->second).insert(make_pair(TraitList[i]->level, tempVec));
  78. LogWrite(SPELL__INFO, 0, "Traits", "Added Trait: %u Tier %i", TraitList[i]->spellID, TraitList[i]->tier);
  79. }
  80. }
  81. else {
  82. map <int8, vector<TraitData*> > tempMap;
  83. vector <TraitData*> tempVec;
  84. tempVec.push_back(TraitList[i]);
  85. tempMap.insert(make_pair(TraitList[i]->level, tempVec));
  86. sortedTraitList->insert(make_pair(TraitList[i]->group, tempMap));
  87. LogWrite(SPELL__INFO, 0, "Traits", "Added Trait: %u Tier %i", TraitList[i]->spellID, TraitList[i]->tier);
  88. }
  89. }
  90. // Sort Class Training
  91. if (TraitList[i]->classReq == client->GetPlayer()->GetAdventureClass() && TraitList[i]->isTraining) {
  92. itr2 = classTraining->lower_bound(TraitList[i]->level);
  93. if (itr2 != classTraining->end() && !(classTraining->key_comp()(TraitList[i]->level, itr2->first))) {
  94. (itr2->second).push_back(TraitList[i]);
  95. }
  96. else {
  97. vector<TraitData*> tempVec;
  98. tempVec.push_back(TraitList[i]);
  99. classTraining->insert(make_pair(TraitList[i]->level, tempVec));
  100. }
  101. }
  102. // Sort Racial Abilities
  103. if (TraitList[i]->raceReq == client->GetPlayer()->GetRace() && !TraitList[i]->isInate && !TraitList[i]->isTraining) {
  104. itr2 = raceTraits->lower_bound(TraitList[i]->group);
  105. if (itr2 != raceTraits->end() && !(raceTraits->key_comp()(TraitList[i]->group, itr2->first))) {
  106. (itr2->second).push_back(TraitList[i]);
  107. }
  108. else {
  109. vector<TraitData*> tempVec;
  110. tempVec.push_back(TraitList[i]);
  111. raceTraits->insert(make_pair(TraitList[i]->group, tempVec));
  112. }
  113. }
  114. // Sort Innate Racial Abilities
  115. if (TraitList[i]->raceReq == client->GetPlayer()->GetRace() && TraitList[i]->isInate) {
  116. itr2 = innateRaceTraits->lower_bound(TraitList[i]->group);
  117. if (itr2 != innateRaceTraits->end() && !(innateRaceTraits->key_comp()(TraitList[i]->group, itr2->first))) {
  118. (itr2->second).push_back(TraitList[i]);
  119. }
  120. else {
  121. vector<TraitData*> tempVec;
  122. tempVec.push_back(TraitList[i]);
  123. innateRaceTraits->insert(make_pair(TraitList[i]->group, tempVec));
  124. }
  125. }
  126. // Sort Focus Effects
  127. if ((TraitList[i]->classReq == client->GetPlayer()->GetAdventureClass() || TraitList[i]->classReq == 255) && TraitList[i]->isFocusEffect) {
  128. int8 j = 0;
  129. itr2 = focusEffects->lower_bound(TraitList[i]->group);
  130. if (itr2 != focusEffects->end() && !(focusEffects->key_comp()(TraitList[i]->group, itr2->first))) {
  131. (itr2->second).push_back(TraitList[i]);
  132. //LogWrite(SPELL__INFO, 0, "Traits", "Added Focus Effect: %u Tier: %i", TraitList[i]->spellID, TraitList[i]->tier);
  133. j++;
  134. }
  135. else {
  136. vector<TraitData*> tempVec;
  137. tempVec.push_back(TraitList[i]);
  138. focusEffects->insert(make_pair(TraitList[i]->group, tempVec));
  139. //LogWrite(SPELL__INFO, 0, "Traits", "Added Focus Effect: %u Tier %i", TraitList[i]->spellID, TraitList[i]->tier);
  140. }
  141. }
  142. }
  143. MMasterTraitList.releasereadlock(__FUNCTION__, __LINE__);
  144. return true;
  145. }
  146. bool MasterTraitList::IdentifyNextTrait(Client* client, map <int8, vector<TraitData*> >* traitList, vector<TraitData*>* collectTraits, vector<TraitData*>* tieredTraits, std::map<int32, int8>* previousMatchedSpells, bool omitFoundMatches) {
  147. bool found_spell_match = false;
  148. bool match = false;
  149. bool tiered_selection = rule_manager.GetGlobalRule(R_Player, TraitTieringSelection)->GetBool();
  150. int8 group_to_apply = 255;
  151. int8 count = 0;
  152. map <int8, vector<TraitData*> >::iterator itr2;
  153. vector<TraitData*>::iterator itr3;
  154. for (itr2 = traitList->begin(); itr2 != traitList->end(); itr2++) {
  155. //LogWrite(SPELL__INFO, 0, "AA", "Character Traits Size...%i ", traits_size);
  156. for (itr3 = itr2->second.begin(); itr3 != itr2->second.end(); itr3++) {
  157. if(tiered_selection) {
  158. if(found_spell_match && (*itr3)->group == group_to_apply) {
  159. continue; // skip!
  160. }
  161. else if((*itr3)->group != group_to_apply) {
  162. if(group_to_apply != 255 && !found_spell_match) {
  163. // found match
  164. LogWrite(SPELL__INFO, 0, "Traits", "Found match to group id %u", group_to_apply);
  165. match = true;
  166. break;
  167. }
  168. else {
  169. LogWrite(SPELL__INFO, 0, "Traits", "Try match to group... spell id %u, group id %u", (*itr3)->spellID, (*itr3)->group);
  170. found_spell_match = false;
  171. group_to_apply = (*itr3)->group;
  172. count = 0;
  173. if(!omitFoundMatches)
  174. tieredTraits->clear();
  175. }
  176. }
  177. }
  178. count++;
  179. std::map<int32,int8>::iterator spell_itr = previousMatchedSpells->find((*itr3)->spellID);
  180. if(spell_itr != previousMatchedSpells->end() && (*itr3)->group > spell_itr->second) {
  181. continue;
  182. }
  183. if(!IsPlayerAllowedTrait(client, (*itr3))) {
  184. LogWrite(SPELL__INFO, 0, "Traits", "We are not allowed any more spells from this type/group... spell id %u, group id %u", (*itr3)->spellID, (*itr3)->group);
  185. found_spell_match = true;
  186. }
  187. else if (client->GetPlayer()->HasSpell((*itr3)->spellID, (*itr3)->tier)) {
  188. LogWrite(SPELL__INFO, 0, "Traits", "Found existing spell match to group... spell id %u, group id %u", (*itr3)->spellID, (*itr3)->group);
  189. if(!omitFoundMatches)
  190. found_spell_match = true;
  191. previousMatchedSpells->insert(std::make_pair((*itr3)->spellID,(*itr3)->group));
  192. }
  193. else {
  194. tieredTraits->push_back((*itr3));
  195. collectTraits->push_back((*itr3));
  196. }
  197. }
  198. if(match)
  199. break;
  200. }
  201. if(!match && group_to_apply != 255 && !found_spell_match) {
  202. // found match
  203. match = true;
  204. }
  205. else if(!tiered_selection && collectTraits->size() > 0) {
  206. match = true;
  207. }
  208. return match;
  209. }
  210. bool MasterTraitList::ChooseNextTrait(Client* client) {
  211. map <int8, map <int8, vector<TraitData*> > >* SortedTraitList = new map <int8, map <int8, vector<TraitData*> > >;
  212. map <int8, map <int8, vector<TraitData*> > >::iterator itr;
  213. bool tiered_selection = rule_manager.GetGlobalRule(R_Player, TraitTieringSelection)->GetBool();
  214. vector<TraitData*>::iterator itr3;
  215. map <int8, vector<TraitData*> >* ClassTraining = new map <int8, vector<TraitData*> >;
  216. map <int8, vector<TraitData*> >* RaceTraits = new map <int8, vector<TraitData*> >;
  217. map <int8, vector<TraitData*> >* InnateRaceTraits = new map <int8, vector<TraitData*> >;
  218. map <int8, vector<TraitData*> >* FocusEffects = new map <int8, vector<TraitData*> >;
  219. vector<TraitData*>* collectTraits = new vector<TraitData*>;
  220. vector<TraitData*>* tieredTraits = new vector<TraitData*>;
  221. std::map<int32, int8>* previousMatchedSpells = new std::map<int32, int8>;
  222. bool match = false;
  223. if(GenerateTraitLists(client, SortedTraitList, ClassTraining, RaceTraits, InnateRaceTraits, FocusEffects, client->GetPlayer()->GetLevel())) {
  224. vector<TraitData*>* endTraits;
  225. if(!match || !tiered_selection) {
  226. match = IdentifyNextTrait(client, ClassTraining, collectTraits, tieredTraits, previousMatchedSpells);
  227. }
  228. if(!match || !tiered_selection) {
  229. match = IdentifyNextTrait(client, RaceTraits, collectTraits, tieredTraits, previousMatchedSpells, true);
  230. bool overrideMatch = IdentifyNextTrait(client, InnateRaceTraits, collectTraits, tieredTraits, previousMatchedSpells, true);
  231. if(!match && overrideMatch)
  232. match = true;
  233. }
  234. if(!match || !tiered_selection) {
  235. match = IdentifyNextTrait(client, FocusEffects, collectTraits, tieredTraits, previousMatchedSpells);
  236. }
  237. if(!tiered_selection && collectTraits->size() > 0) {
  238. endTraits = collectTraits;
  239. }
  240. else if (match) {
  241. endTraits = tieredTraits;
  242. }
  243. if(match) {
  244. PacketStruct* packet = configReader.getStruct("WS_QuestRewardPackMsg", client->GetVersion());
  245. // 0=enemy mastery, 1=specialized training,2=character trait, 3=racial tradition
  246. int8 packet_type = 0;
  247. int8 item_count = 0;
  248. packet->setSubstructArrayLengthByName("reward_data", "num_select_rewards", endTraits->size());
  249. for (itr3 = endTraits->begin(); itr3 != endTraits->end(); itr3++) {
  250. if((*itr3)->item_id) {
  251. //LogWrite(SPELL__INFO, 0, "Traits", "Item %u to be sent", (*itr3)->item_id);
  252. Item* item = master_item_list.GetItem((*itr3)->item_id);
  253. if(item) {
  254. //LogWrite(SPELL__INFO, 0, "Traits", "Item found %s to be sent", item->name.c_str());
  255. packet->setArrayDataByName("select_reward_id", (*itr3)->item_id, item_count);
  256. packet->setItemArrayDataByName("select_item", item, client->GetPlayer(), item_count, 0, -1);
  257. item_count++;
  258. }
  259. }
  260. // Sort Character Traits
  261. if ((*itr3)->classReq == 255 && (*itr3)->raceReq == 255 && (*itr3)->isTrait) {
  262. packet_type = 2;
  263. }
  264. // Sort Class Training
  265. else if ((*itr3)->classReq == client->GetPlayer()->GetAdventureClass() && (*itr3)->isTraining) {
  266. packet_type = 1;
  267. }
  268. // Sort Racial Abilities
  269. else if ((*itr3)->raceReq == client->GetPlayer()->GetRace() && !(*itr3)->isInate && !(*itr3)->isTraining) {
  270. packet_type = 3;
  271. }
  272. // Sort Innate Racial Abilities
  273. else if ((*itr3)->raceReq == client->GetPlayer()->GetRace() && (*itr3)->isInate) {
  274. packet_type = 3;
  275. }
  276. //LogWrite(SPELL__INFO, 0, "Traits", "Sending trait %u", (*itr3)->spellID);
  277. }
  278. packet->setSubstructDataByName("reward_data", "unknown1", packet_type);
  279. client->QueuePacket(packet->serialize());
  280. safe_delete(packet);
  281. }
  282. }
  283. safe_delete(SortedTraitList);
  284. safe_delete(ClassTraining);
  285. safe_delete(RaceTraits);
  286. safe_delete(InnateRaceTraits);
  287. safe_delete(FocusEffects);
  288. safe_delete(collectTraits);
  289. safe_delete(tieredTraits);
  290. safe_delete(previousMatchedSpells);
  291. return match;
  292. }
  293. int16 MasterTraitList::GetSpellCount(Client* client, map <int8, vector<TraitData*> >* traits, bool onlyCharTraits) {
  294. if(!traits)
  295. return 0;
  296. int16 count = 0;
  297. map <int8, vector<TraitData*> >::iterator itr2;
  298. vector<TraitData*>::iterator itr3;
  299. for (itr2 = traits->begin(); itr2 != traits->end(); itr2++) {
  300. for (itr3 = itr2->second.begin(); itr3 != itr2->second.end(); itr3++) {
  301. if (client->GetPlayer()->HasSpell((*itr3)->spellID, (*itr3)->tier)) {
  302. if(!onlyCharTraits || (onlyCharTraits && (*itr3)->classReq == 255 && (*itr3)->raceReq == 255 && (*itr3)->isTrait)) {
  303. count++;
  304. }
  305. }
  306. }
  307. }
  308. return count;
  309. }
  310. vector<int16> PersonalTraitLevelLimits = boost::assign::list_of(0)(8)(14)(22)(28)(36)(42)(46)(48);
  311. vector<int16> TrainingTraitLevelLimits = boost::assign::list_of(0)(10)(20)(30)(40)(50);
  312. vector<int16> RacialTraitLevelLimits = boost::assign::list_of(0)(18)(26)(34)(44);
  313. vector<int16> CharacterTraitLevelLimits = boost::assign::list_of(0)(12)(16)(24)(32)(38);
  314. /***
  315. Every other level, beginning at Level 8,
  316. you gain an additional advantage — a
  317. Personal Trait, an Enemy Tactic, a Racial
  318. Tradition or a Training ability. Each time
  319. you reach an even-numbered level, you
  320. can select another advantage from the
  321. appropriate list. You don’t have to select
  322. in order — you may take any of the avail-
  323. able choices.
  324. Level Advantage
  325. 8 Personal Trait (1st)
  326. 10 Training (1st)
  327. 12 Enemy Tactic (1st)
  328. 14 Personal Trait (2nd)
  329. 16 Enemy Tactic (2nd)
  330. 18 Racial Tradition (1st)
  331. 20 Training (2nd)
  332. 22 Personal Trait (3rd)
  333. 24 Enemy Tactic (3rd)
  334. 26 Racial Tradition (2nd)
  335. 28 Personal Trait (4th)
  336. 30 Training (3rd)
  337. 32 Enemy Tactic (4th)
  338. 34 Racial Tradition (3rd)
  339. 36 Personal Trait (5th)
  340. 38 Enemy Tactic (5th)
  341. 40 Training (4th)
  342. 42 Personal Trait (6th)
  343. 44 Racial Tradition (4th)
  344. 46 Personal Trait (7th)
  345. 48 Personal Trait (8th)
  346. 50 Training (5th)
  347. ***/
  348. bool MasterTraitList::IsPlayerAllowedTrait(Client* client, TraitData* trait) {
  349. std::unique_lock(client->GetPlayer()->trait_mutex);
  350. map <int8, map <int8, vector<TraitData*> > >* SortedTraitList = client->GetPlayer()->SortedTraitList;
  351. map <int8, map <int8, vector<TraitData*> > >::iterator itr;
  352. map <int8, vector<TraitData*> >::iterator itr2;
  353. vector<TraitData*>::iterator itr3;
  354. bool use_classic_table = rule_manager.GetGlobalRule(R_Player, ClassicTraitLevelTable)->GetBool();
  355. map <int8, vector<TraitData*> >* ClassTraining = client->GetPlayer()->ClassTraining;
  356. map <int8, vector<TraitData*> >* RaceTraits = client->GetPlayer()->RaceTraits;
  357. map <int8, vector<TraitData*> >* InnateRaceTraits = client->GetPlayer()->InnateRaceTraits;
  358. map <int8, vector<TraitData*> >* FocusEffects = client->GetPlayer()->FocusEffects;
  359. if(client->GetPlayer()->need_trait_update) {
  360. SortedTraitList->clear();
  361. ClassTraining->clear();
  362. RaceTraits->clear();
  363. InnateRaceTraits->clear();
  364. FocusEffects->clear();
  365. }
  366. bool allowed_trait = false;
  367. if(!client->GetPlayer()->need_trait_update || GenerateTraitLists(client, SortedTraitList, ClassTraining, RaceTraits, InnateRaceTraits, FocusEffects, 0)) {
  368. client->GetPlayer()->need_trait_update = false;
  369. if(trait->isFocusEffect) {
  370. int32 trait_level = rule_manager.GetGlobalRule(R_Player, TraitFocusSelectLevel)->GetInt32();
  371. int16 num_available_selections = 0;
  372. if(trait_level > 0) {
  373. num_available_selections = client->GetPlayer()->GetLevel() / trait_level;
  374. }
  375. int16 total_used = GetSpellCount(client, FocusEffects);
  376. int16 classic_avail = 0;
  377. if(use_classic_table && PersonalTraitLevelLimits.size() > total_used+1) {
  378. int16 classic_level_req = PersonalTraitLevelLimits.at(total_used+1);
  379. if(client->GetPlayer()->GetLevel() >= classic_level_req)
  380. classic_avail = num_available_selections = total_used+1;
  381. else
  382. num_available_selections = 0;
  383. }
  384. else if(use_classic_table)
  385. num_available_selections = 0;
  386. LogWrite(SPELL__INFO, 9, "Traits", "%s FocusEffects used %u, available %u, classic available %u", client->GetPlayer()->GetName(), total_used, num_available_selections, classic_avail);
  387. if(total_used < num_available_selections) {
  388. allowed_trait = true;
  389. }
  390. }
  391. else if(trait->isTraining) {
  392. int32 trait_level = rule_manager.GetGlobalRule(R_Player, TraitTrainingSelectLevel)->GetInt32();
  393. int16 num_available_selections = 0;
  394. if(trait_level > 0) {
  395. num_available_selections = client->GetPlayer()->GetLevel() / trait_level;
  396. }
  397. int16 total_used = GetSpellCount(client, ClassTraining);
  398. int16 classic_avail = 0;
  399. if(use_classic_table && TrainingTraitLevelLimits.size() > total_used+1) {
  400. int16 classic_level_req = TrainingTraitLevelLimits.at(total_used+1);
  401. if(client->GetPlayer()->GetLevel() >= classic_level_req)
  402. classic_avail = num_available_selections = total_used+1;
  403. else
  404. num_available_selections = 0;
  405. }
  406. else if(use_classic_table)
  407. num_available_selections = 0;
  408. LogWrite(SPELL__INFO, 9, "Traits", "%s ClassTraining used %u, available %u, classic available %u", client->GetPlayer()->GetName(), total_used, num_available_selections, classic_avail);
  409. if(total_used < num_available_selections) {
  410. allowed_trait = true;
  411. }
  412. }
  413. else {
  414. if(trait->raceReq == client->GetPlayer()->GetRace()) {
  415. int32 trait_level = rule_manager.GetGlobalRule(R_Player, TraitRaceSelectLevel)->GetInt32();
  416. int16 num_available_selections = 0;
  417. if(trait_level > 0) {
  418. num_available_selections = client->GetPlayer()->GetLevel() / trait_level;
  419. }
  420. int16 total_used = GetSpellCount(client, RaceTraits);
  421. int16 total_used2 = GetSpellCount(client, InnateRaceTraits);
  422. int16 classic_avail = 0;
  423. if(use_classic_table && RacialTraitLevelLimits.size() > total_used+total_used2+1) {
  424. int16 classic_level_req = RacialTraitLevelLimits.at(total_used+total_used2+1);
  425. if(client->GetPlayer()->GetLevel() >= classic_level_req)
  426. classic_avail = num_available_selections = total_used+total_used2+1;
  427. else
  428. num_available_selections = 0;
  429. }
  430. else if(use_classic_table)
  431. num_available_selections = 0;
  432. LogWrite(SPELL__INFO, 9, "Traits", "%s RaceTraits used %u, available %u, classic available %u", client->GetPlayer()->GetName(), total_used+total_used2, num_available_selections, classic_avail);
  433. if(total_used+total_used2 < num_available_selections) {
  434. allowed_trait = true;
  435. }
  436. }
  437. else { // character trait?
  438. int16 num_available_selections = 0;
  439. int32 trait_level = rule_manager.GetGlobalRule(R_Player, TraitCharacterSelectLevel)->GetInt32();
  440. if(trait_level > 0) {
  441. num_available_selections = client->GetPlayer()->GetLevel() / trait_level;
  442. }
  443. int16 total_used = 0;
  444. for (itr = SortedTraitList->begin(); itr != SortedTraitList->end(); itr++) {
  445. total_used += GetSpellCount(client, &itr->second, true);
  446. }
  447. int16 classic_avail = 0;
  448. if(use_classic_table && CharacterTraitLevelLimits.size() > total_used+1) {
  449. int16 classic_level_req = CharacterTraitLevelLimits.at(total_used+1);
  450. if(client->GetPlayer()->GetLevel() >= classic_level_req)
  451. classic_avail = num_available_selections = total_used+1;
  452. else
  453. num_available_selections = 0;
  454. }
  455. else if(use_classic_table)
  456. num_available_selections = 0;
  457. LogWrite(SPELL__INFO, 9, "Traits", "%s CharacterTraits used %u, available %u, classic available %u", client->GetPlayer()->GetName(), total_used, num_available_selections, classic_avail);
  458. if(total_used < num_available_selections) {
  459. allowed_trait = true;
  460. }
  461. }
  462. }
  463. }
  464. return allowed_trait;
  465. }
  466. EQ2Packet* MasterTraitList::GetTraitListPacket (Client* client)
  467. {
  468. std::unique_lock(client->GetPlayer()->trait_mutex);
  469. if (!client) {
  470. LogWrite(SPELL__ERROR, 0, "Traits", "GetTraitListPacket called with an invalid client");
  471. return 0;
  472. }
  473. // Sort the Data
  474. if (Size() == 0)
  475. return NULL;
  476. map <int8, map <int8, vector<TraitData*> > >* SortedTraitList = client->GetPlayer()->SortedTraitList;
  477. map <int8, map <int8, vector<TraitData*> > >::iterator itr;
  478. map <int8, vector<TraitData*> >::iterator itr2;
  479. vector<TraitData*>::iterator itr3;
  480. map <int8, vector<TraitData*> >* ClassTraining = client->GetPlayer()->ClassTraining;
  481. map <int8, vector<TraitData*> >* RaceTraits = client->GetPlayer()->RaceTraits;
  482. map <int8, vector<TraitData*> >* InnateRaceTraits = client->GetPlayer()->InnateRaceTraits;
  483. map <int8, vector<TraitData*> >* FocusEffects = client->GetPlayer()->FocusEffects;
  484. if(client->GetPlayer()->need_trait_update) {
  485. SortedTraitList->clear();
  486. ClassTraining->clear();
  487. RaceTraits->clear();
  488. InnateRaceTraits->clear();
  489. FocusEffects->clear();
  490. }
  491. if(client->GetPlayer()->need_trait_update && !GenerateTraitLists(client, SortedTraitList, ClassTraining, RaceTraits, InnateRaceTraits, FocusEffects)) {
  492. return NULL;
  493. }
  494. client->GetPlayer()->need_trait_update = false;
  495. int16 version = 1;
  496. int8 count = 0;
  497. int8 index = 0;
  498. int8 num_traits = 0;
  499. int8 traits_size = 0;
  500. int8 num_focuseffects = 0;
  501. char sTrait [20];
  502. char temp [20];
  503. version = client->GetVersion();
  504. // Jabantiz: Get the value for num_traits in the struct (num_traits refers to the number of rows not the total number of traits)
  505. for (itr = SortedTraitList->begin(); itr != SortedTraitList->end(); itr++) {
  506. num_traits += (itr->second).size();
  507. }
  508. PacketStruct* packet = configReader.getStruct("WS_TraitsList", version);
  509. if (packet == NULL) {
  510. return NULL;
  511. }
  512. packet->setArrayLengthByName("num_traits", num_traits);
  513. for (itr = SortedTraitList->begin(); itr != SortedTraitList->end(); itr++) {
  514. for (itr2 = itr->second.begin(); itr2 != itr->second.end(); itr2++, index++) {
  515. traits_size += (itr2->second).size();
  516. count = 0;
  517. Spell* tmp_spell = 0;
  518. packet->setArrayDataByName("trait_level", (*itr2).first, index);
  519. packet->setArrayDataByName("trait_line", 255, index);
  520. //LogWrite(SPELL__INFO, 0, "AA", "Character Traits Size...%i ", traits_size);
  521. for (itr3 = itr2->second.begin(); itr3 != itr2->second.end(); itr3++, count++) {
  522. // Jabantiz: cant have more then 5 traits per line
  523. tmp_spell = master_spell_list.GetSpell((*itr3)->spellID, (*itr3)->tier);
  524. if(!tmp_spell) {
  525. LogWrite(SPELL__ERROR, 0, "Traits", "Could not find SpellID %u and Tier %i in Master Spell List (line: %i)", (*itr3)->spellID, (*itr3)->tier, __LINE__);
  526. continue;
  527. }
  528. if (count > 4)
  529. break;
  530. strcpy(sTrait, "trait");
  531. itoa(count, temp, 10);
  532. strcat(sTrait, temp);
  533. strcpy(temp, sTrait);
  534. strcat(sTrait, "_icon");
  535. if (tmp_spell)
  536. packet->setArrayDataByName(sTrait, tmp_spell->GetSpellIcon(), index);
  537. else
  538. LogWrite(SPELL__ERROR, 0, "Traits", "Could not find SpellID %u and Tier %i in Master Spell List (line: %i)", (*itr3)->spellID, (*itr3)->tier, __LINE__);
  539. strcpy(sTrait, temp);
  540. strcat(sTrait, "_icon2");
  541. packet->setArrayDataByName(sTrait, 65535, index);
  542. strcpy(sTrait, temp);
  543. strcat(sTrait, "_id");
  544. packet->setArrayDataByName(sTrait, (*itr3)->spellID, index);
  545. strcpy(sTrait, temp);
  546. strcat(sTrait, "_name");
  547. if (tmp_spell)
  548. packet->setArrayDataByName(sTrait, tmp_spell->GetName(), index);
  549. else
  550. LogWrite(SPELL__ERROR, 0, "Traits", "Could not find SpellID %u and Tier %i in Master Spell List (line: %i)", (*itr3)->spellID, (*itr3)->tier, __LINE__);
  551. strcpy(sTrait, temp);
  552. strcat(sTrait, "_unknown2");
  553. packet->setArrayDataByName(sTrait, 1, index);
  554. strcpy(sTrait, temp);
  555. strcat(sTrait, "_unknown");
  556. packet->setArrayDataByName(sTrait, 1, index);
  557. if (client->GetPlayer()->HasSpell((*itr3)->spellID, (*itr3)->tier))
  558. packet->setArrayDataByName("trait_line", count, index);
  559. }
  560. // Jabantiz: If less then 5 fill the rest of the line with FF FF FF FF FF FF FF FF FF FF FF FF
  561. while (count < 5) {
  562. strcpy(sTrait, "trait");
  563. itoa(count, temp, 10);
  564. strcat(sTrait, temp);
  565. strcpy(temp, sTrait);
  566. strcat(sTrait, "_icon");
  567. packet->setArrayDataByName(sTrait, 65535, index); // FF FF
  568. strcpy(sTrait, temp);
  569. strcat(sTrait, "_icon2");
  570. packet->setArrayDataByName(sTrait, 65535, index); // FF FF
  571. strcpy(sTrait, temp);
  572. strcat(sTrait, "_id");
  573. packet->setArrayDataByName(sTrait, 0xFFFFFFFF, index);
  574. strcpy(sTrait, temp);
  575. strcat(sTrait, "_unknown");
  576. packet->setArrayDataByName(sTrait, 0xFFFFFFFF, index);
  577. strcpy(sTrait, temp);
  578. strcat(sTrait, "_name");
  579. packet->setArrayDataByName(sTrait, "", index);
  580. count++;
  581. }
  582. }
  583. }
  584. // Class Training portion of the packet
  585. packet->setArrayLengthByName("num_trainings", ClassTraining->size());
  586. index = 0;
  587. for (itr2 = ClassTraining->begin(); itr2 != ClassTraining->end(); itr2++, index++) {
  588. count = 0;
  589. Spell* tmp_spell = 0;
  590. packet->setArrayDataByName("training_level", itr2->first, index);
  591. packet->setArrayDataByName("training_line", 255, index);
  592. for (itr3 = itr2->second.begin(); itr3 != itr2->second.end(); itr3++, count++) {
  593. // Jabantiz: cant have more then 5 traits per line
  594. tmp_spell = master_spell_list.GetSpell((*itr3)->spellID, (*itr3)->tier);
  595. if(!tmp_spell) {
  596. LogWrite(SPELL__ERROR, 0, "Traits", "Could not find SpellID %u and Tier %i in Master Spell List (line: %i)", (*itr3)->spellID, (*itr3)->tier, __LINE__);
  597. continue;
  598. }
  599. if (count > 4)
  600. break;
  601. if (client->GetPlayer()->HasSpell((*itr3)->spellID, (*itr3)->tier)) {
  602. packet->setArrayDataByName("training_line", count, index);
  603. }
  604. strcpy(sTrait, "training");
  605. itoa(count, temp, 10);
  606. strcat(sTrait, temp);
  607. strcpy(temp, sTrait);
  608. strcat(sTrait, "_icon");
  609. if (tmp_spell)
  610. packet->setArrayDataByName(sTrait, tmp_spell->GetSpellIcon(), index);
  611. else
  612. LogWrite(SPELL__ERROR, 0, "Training", "Could not find SpellID %u and Tier %i in Master Spell List (line: %i)", (*itr3)->spellID, (*itr3)->tier, __LINE__);
  613. strcpy(sTrait, temp);
  614. strcat(sTrait, "_icon2");
  615. if (tmp_spell)
  616. packet->setArrayDataByName(sTrait, tmp_spell->GetSpellIconBackdrop(), index);
  617. else
  618. LogWrite(SPELL__ERROR, 0, "Training", "Could not find SpellID %u and Tier %i in Master Spell List (line: %i)", (*itr3)->spellID, (*itr3)->tier, __LINE__);
  619. strcpy(sTrait, temp);
  620. strcat(sTrait, "_id");
  621. packet->setArrayDataByName(sTrait, (*itr3)->spellID, index);
  622. strcpy(sTrait, temp);
  623. strcat(sTrait, "_unknown");
  624. packet->setArrayDataByName(sTrait,0xFFFFFFFF , index);
  625. strcpy(sTrait, temp);
  626. strcat(sTrait, "_unknown2");
  627. packet->setArrayDataByName(sTrait, 1, index);
  628. strcpy(sTrait, temp);
  629. strcat(sTrait, "_name");
  630. if (tmp_spell)
  631. packet->setArrayDataByName(sTrait, tmp_spell->GetName(), index);
  632. else
  633. LogWrite(SPELL__ERROR, 0, "Training", "Could not find SpellID %u and Tier %i in Master Spell List (line: %i)", (*itr3)->spellID, (*itr3)->tier, __LINE__);
  634. if (client->GetPlayer()->HasSpell((*itr3)->spellID, (*itr3)->tier))
  635. packet->setArrayDataByName("training_line", count, index);
  636. }
  637. // Jabantiz: If less then 5 fill the rest of the line with FF FF FF FF FF FF FF FF FF FF FF FF
  638. while (count < 5) {
  639. strcpy(sTrait, "training");
  640. itoa(count, temp, 10);
  641. strcat(sTrait, temp);
  642. strcpy(temp, sTrait);
  643. strcat(sTrait, "_icon");
  644. packet->setArrayDataByName(sTrait, 65535, index); // FF FF
  645. strcpy(sTrait, temp);
  646. strcat(sTrait, "_icon2");
  647. packet->setArrayDataByName(sTrait, 65535, index); // FF FF
  648. strcpy(sTrait, temp);
  649. strcat(sTrait, "_id");
  650. packet->setArrayDataByName(sTrait, 0xFFFFFFFF, index);
  651. strcpy(sTrait, temp);
  652. strcat(sTrait, "_unknown");
  653. packet->setArrayDataByName(sTrait, 0xFFFFFFFF, index);
  654. strcpy(sTrait, temp);
  655. strcat(sTrait, "_name");
  656. packet->setArrayDataByName(sTrait, "", index);
  657. count++;
  658. }
  659. }
  660. // Racial Traits
  661. packet->setArrayLengthByName("num_sections", RaceTraits->size());
  662. index = 0;
  663. string tempStr;
  664. int8 num_selections = 0;
  665. for (itr2 = RaceTraits->begin(); itr2 != RaceTraits->end(); itr2++, index++) {
  666. count = 0;
  667. Spell* tmp_spell = 0;
  668. switch (itr2->first)
  669. {
  670. case TRAITS_ATTRIBUTES:
  671. tempStr = "Attributes";
  672. break;
  673. case TRAITS_COMBAT:
  674. tempStr = "Combat";
  675. break;
  676. case TRAITS_NONCOMBAT:
  677. tempStr = "Noncombat";
  678. break;
  679. case TRAITS_POOLS:
  680. tempStr = "Pools";
  681. break;
  682. case TRAITS_RESIST:
  683. tempStr = "Resist";
  684. break;
  685. case TRAITS_TRADESKILL:
  686. tempStr = "Tradeskill";
  687. break;
  688. default:
  689. tempStr = "Unknown";
  690. break;
  691. }
  692. packet->setArrayDataByName("section_name", tempStr.c_str(), index);
  693. packet->setSubArrayLengthByName("num_traditions", (itr2->second).size(), index);
  694. for (itr3 = itr2->second.begin(); itr3 != itr2->second.end(); itr3++, count++) {
  695. if (client->GetPlayer()->HasSpell((*itr3)->spellID, (*itr3)->tier)) {
  696. num_selections++;
  697. packet->setSubArrayDataByName("tradition_selected", 1, index, count);
  698. }
  699. else {
  700. packet->setSubArrayDataByName("tradition_selected", 0, index, count);
  701. }
  702. tmp_spell = master_spell_list.GetSpell((*itr3)->spellID, (*itr3)->tier);
  703. if (tmp_spell){
  704. packet->setSubArrayDataByName("tradition_icon", tmp_spell->GetSpellIcon(), index, count);
  705. packet->setSubArrayDataByName("tradition_icon2", tmp_spell->GetSpellIconBackdrop(), index, count);
  706. packet->setSubArrayDataByName("tradition_id", (*itr3)->spellID, index, count);
  707. packet->setSubArrayDataByName("tradition_name", tmp_spell->GetName(), index, count);
  708. packet->setSubArrayDataByName("tradition_unknown_58617_MJ1", 1, index, count);
  709. }
  710. else
  711. LogWrite(SPELL__ERROR, 0, "Traits", "Could not find SpellID %u and Tier %i in Master Spell List (line: %i)", (*itr3)->spellID, (*itr3)->tier, __LINE__);
  712. }
  713. }
  714. int8 num_available_selections = client->GetPlayer()->GetLevel() / 10;
  715. if (num_selections < num_available_selections)
  716. packet->setDataByName("allow_select", num_available_selections - num_selections);
  717. else
  718. packet->setDataByName("allow_select", 0);
  719. // Innate Racial Traits
  720. index = 0;
  721. // total number of Innate traits
  722. num_traits = 0;
  723. for (itr2 = InnateRaceTraits->begin(); itr2 != InnateRaceTraits->end(); itr2++) {
  724. num_traits += (itr2->second).size();
  725. }
  726. packet->setArrayLengthByName("num_abilities", num_traits);
  727. for (itr2 = InnateRaceTraits->begin(); itr2 != InnateRaceTraits->end(); itr2++) {
  728. for (itr3 = itr2->second.begin(); itr3 != itr2->second.end(); itr3++, index++) {
  729. Spell* innate_spell = master_spell_list.GetSpell((*itr3)->spellID, (*itr3)->tier);
  730. if (innate_spell) {
  731. packet->setArrayDataByName("ability_icon", innate_spell->GetSpellIcon(), index);
  732. packet->setArrayDataByName("ability_icon2", innate_spell->GetSpellIconBackdrop(), index);
  733. packet->setArrayDataByName("ability_id", (*itr3)->spellID, index);
  734. packet->setArrayDataByName("ability_name", innate_spell->GetName(), index);
  735. }
  736. else
  737. LogWrite(SPELL__ERROR, 0, "Traits", "Could not find SpellID %u and Tier %i in Master Spell List (line: %i)", (*itr3)->spellID, (*itr3)->tier, __LINE__);
  738. }
  739. }
  740. if (client->GetVersion() >= 1188) {
  741. // total number of Focus Effects
  742. num_selections = 0;
  743. num_focuseffects = 0;
  744. index = 0;
  745. for (itr2 = FocusEffects->begin(); itr2 != FocusEffects->end(); itr2++) {
  746. num_focuseffects += (itr2->second).size();
  747. }
  748. packet->setArrayLengthByName("num_focuseffects", num_focuseffects);
  749. for (itr2 = FocusEffects->begin(); itr2 != FocusEffects->end(); itr2++) {
  750. for (itr3 = itr2->second.begin(); itr3 != itr2->second.end(); itr3++, index++) {
  751. Spell* spell = master_spell_list.GetSpell((*itr3)->spellID, (*itr3)->tier);
  752. if (client->GetPlayer()->HasSpell((*itr3)->spellID, (*itr3)->tier)) {
  753. num_selections++;
  754. packet->setArrayDataByName("focus_selected", 1, index);
  755. }
  756. else {
  757. packet->setArrayDataByName("focus_selected", 0, index);
  758. }
  759. if (spell) {
  760. packet->setArrayDataByName("focus_unknown2", 1, index);
  761. packet->setArrayDataByName("focus_icon", spell->GetSpellIcon(), index);
  762. packet->setArrayDataByName("focus_icon2", spell->GetSpellIconBackdrop(), index);
  763. packet->setArrayDataByName("focus_spell_crc", (*itr3)->spellID, index);
  764. packet->setArrayDataByName("focus_name", spell->GetName(), index);
  765. packet->setArrayDataByName("focus_unknown_58617_MJ1", 1, index);
  766. }
  767. else
  768. LogWrite(SPELL__ERROR, 0, "Traits", "Could not find SpellID %u and Tier %i in Master Spell List (line: %i)", (*itr3)->spellID, (*itr3)->tier, __LINE__);
  769. }
  770. }
  771. num_available_selections = client->GetPlayer()->GetLevel() / 9;
  772. if (num_selections < num_available_selections)
  773. packet->setDataByName("focus_allow_select", num_available_selections - num_selections);
  774. else
  775. packet->setDataByName("focus_allow_select", 0);
  776. }
  777. LogWrite(SPELL__PACKET, 0, "Traits", "Dump/Print Packet in func: %s, line: %i", __FUNCTION__, __LINE__);
  778. #if EQDEBUG >= 9
  779. packet->PrintPacket();
  780. #endif
  781. EQ2Packet* data = packet->serialize();
  782. EQ2Packet* outapp = new EQ2Packet(OP_ClientCmdMsg, data->pBuffer, data->size);
  783. //DumpPacket(outapp);
  784. safe_delete(packet);
  785. safe_delete(data);
  786. return outapp;
  787. }
  788. // Jabantiz: Probably a better way to do this but can't think of it right now
  789. TraitData* MasterTraitList::GetTrait(int32 spellID) {
  790. vector<TraitData*>::iterator itr;
  791. TraitData* data = NULL;
  792. MMasterTraitList.readlock(__FUNCTION__, __LINE__);
  793. for (itr = TraitList.begin(); itr != TraitList.end(); itr++) {
  794. if ((*itr)->spellID == spellID) {
  795. data = (*itr);
  796. break;
  797. }
  798. }
  799. MMasterTraitList.releasereadlock(__FUNCTION__, __LINE__);
  800. return data;
  801. }
  802. TraitData* MasterTraitList::GetTraitByItemID(int32 itemID) {
  803. vector<TraitData*>::iterator itr;
  804. TraitData* data = NULL;
  805. MMasterTraitList.readlock(__FUNCTION__, __LINE__);
  806. for (itr = TraitList.begin(); itr != TraitList.end(); itr++) {
  807. if ((*itr)->item_id == itemID) {
  808. data = (*itr);
  809. break;
  810. }
  811. }
  812. MMasterTraitList.releasereadlock(__FUNCTION__, __LINE__);
  813. return data;
  814. }
  815. void MasterTraitList::DestroyTraits(){
  816. MMasterTraitList.writelock(__FUNCTION__, __LINE__);
  817. vector<TraitData*>::iterator itr;
  818. for (itr = TraitList.begin(); itr != TraitList.end(); itr++)
  819. safe_delete((*itr));
  820. TraitList.clear();
  821. MMasterTraitList.releasewritelock(__FUNCTION__, __LINE__);
  822. }