PageRenderTime 50ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/Services/WCell.RealmServer/Talents/TalentCollection.cs

https://github.com/enjoii/WCell
C# | 490 lines | 329 code | 59 blank | 102 comment | 38 complexity | 2d1095c28c7eac716f46b1cfd4abe5db MD5 | raw file
  1. /*************************************************************************
  2. *
  3. * file : TalentCollection.cs
  4. * copyright : (C) The WCell Team
  5. * email : info@wcell.org
  6. * last changed : $LastChangedDate: 2009-12-21 18:48:36 +0100 (ma, 21 dec 2009) $
  7. * last author : $LastChangedBy: dominikseifert $
  8. * revision : $Rev: 1149 $
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or
  13. * (at your option) any later version.
  14. *
  15. *************************************************************************/
  16. using System;
  17. using System.Collections;
  18. using System.Collections.Generic;
  19. using System.Linq;
  20. using NLog;
  21. using WCell.Constants;
  22. using WCell.Constants.NPCs;
  23. using WCell.Constants.Pets;
  24. using WCell.Constants.Spells;
  25. using WCell.Constants.Talents;
  26. using WCell.Constants.Updates;
  27. using WCell.RealmServer.Database;
  28. using WCell.RealmServer.Entities;
  29. using WCell.RealmServer.Handlers;
  30. using WCell.RealmServer.NPCs.Pets;
  31. namespace WCell.RealmServer.Talents
  32. {
  33. /// <summary>
  34. /// Represents all Talents of a Character or Pet
  35. /// </summary>
  36. public abstract class TalentCollection : IEnumerable<Talent>
  37. {
  38. internal Dictionary<TalentId, Talent> ById = new Dictionary<TalentId, Talent>();
  39. internal readonly int[] m_treePoints;
  40. internal TalentCollection(Unit unit)
  41. {
  42. Owner = unit;
  43. m_treePoints = new int[Trees.Length];
  44. }
  45. internal void CalcSpentTalentPoints()
  46. {
  47. foreach (var talent in this)
  48. {
  49. m_treePoints[talent.Entry.Tree.TabIndex] += talent.Rank;
  50. }
  51. }
  52. #region Default Properties
  53. public TalentTree[] Trees
  54. {
  55. get { return TalentMgr.GetTrees(Owner.Class); }
  56. }
  57. public int TotalPointsSpent
  58. {
  59. get { return m_treePoints.Sum(); }
  60. }
  61. public int Count
  62. {
  63. get { return ById.Count; }
  64. }
  65. public Unit Owner
  66. {
  67. get;
  68. internal set;
  69. }
  70. #endregion
  71. #region CanLearn
  72. /// <summary>
  73. /// Whether the given talent can be learned by this Character
  74. /// </summary>
  75. public bool CanLearn(TalentId id, int rank)
  76. {
  77. var talent = TalentMgr.GetEntry(id);
  78. return talent != null && CanLearn(talent, rank);
  79. }
  80. /// <summary>
  81. /// Whether the given talent can be learned by an average player.
  82. /// Does not check for available Talent points, since that is checked when the Rank is changed.
  83. /// </summary>
  84. public virtual bool CanLearn(TalentEntry entry, int rank)
  85. {
  86. var tree = entry.Tree;
  87. var diff = rank - GetRank(entry.Id);
  88. if (tree.Class != Owner.Class || m_treePoints[tree.TabIndex] < entry.RequiredTreePoints ||
  89. rank > entry.Spells.Length || diff < 1)
  90. {
  91. return false;
  92. }
  93. // requires another talent?
  94. Talent reqTalent;
  95. return
  96. FreeTalentPoints >= diff &&
  97. (entry.RequiredId == TalentId.None || (ById.TryGetValue(entry.RequiredId, out reqTalent) &&
  98. (entry.RequiredRank == 0 || reqTalent.Rank >= entry.RequiredRank)));
  99. }
  100. #endregion
  101. #region Learn
  102. public Talent Learn(TalentId id, int rank)
  103. {
  104. var entry = TalentMgr.GetEntry(id);
  105. if (entry != null)
  106. {
  107. return Learn(entry, rank);
  108. }
  109. return null;
  110. }
  111. /// <summary>
  112. /// Tries to learn the given talent on the given rank
  113. /// </summary>
  114. /// <returns>Whether it was learnt</returns>
  115. public Talent Learn(TalentEntry entry, int rank)
  116. {
  117. if (!CanLearn(entry, rank))
  118. {
  119. return null;
  120. }
  121. return Set(entry, rank);
  122. }
  123. /// <summary>
  124. /// Learn all talents of your own class
  125. /// </summary>
  126. public void LearnAll()
  127. {
  128. LearnAll(Owner.Class);
  129. }
  130. /// <summary>
  131. /// Learns all talents of the given class
  132. /// </summary>
  133. public void LearnAll(ClassId clss)
  134. {
  135. var points = FreeTalentPoints;
  136. FreeTalentPoints = 300; // need extra Talent points to avoid internal checks to fail
  137. foreach (var talentTree in TalentMgr.TreesByClass[(int)clss])
  138. {
  139. if (talentTree == null) continue;
  140. foreach (var entry in talentTree)
  141. {
  142. if (entry == null) continue;
  143. Learn(entry, entry.MaxRank);
  144. }
  145. }
  146. FreeTalentPoints = points;
  147. }
  148. #endregion
  149. #region Set & Add
  150. /// <summary>
  151. /// Sets the given talent to the given rank without any checks.
  152. /// Make sure that the given TalentId is valid for this Character's class.
  153. /// </summary>
  154. public Talent Set(TalentId id, int rank)
  155. {
  156. var entry = TalentMgr.GetEntry(id);
  157. return Set(entry, rank);
  158. }
  159. /// <summary>
  160. /// Sets the given talent to the given rank without any checks
  161. /// </summary>
  162. public Talent Set(TalentEntry entry, int rank)
  163. {
  164. Talent talent;
  165. if (!ById.TryGetValue(entry.Id, out talent))
  166. {
  167. ById.Add(entry.Id, talent = new Talent(this, entry, rank));
  168. }
  169. else
  170. {
  171. talent.Rank = rank;
  172. }
  173. return talent;
  174. }
  175. internal void AddExisting(TalentEntry entry, int rank)
  176. {
  177. var talent = new Talent(this, entry);
  178. rank = Math.Max(0, rank - 1);
  179. talent.SetRankSilently(rank);
  180. ById[entry.Id] = talent;
  181. }
  182. #endregion
  183. #region Getters
  184. /// <summary>
  185. /// Returns the current rank that this player has of this talent
  186. /// </summary>
  187. public Talent GetTalent(TalentId id)
  188. {
  189. Talent talent;
  190. ById.TryGetValue(id, out talent);
  191. return talent;
  192. }
  193. /// <summary>
  194. /// Returns the current rank that this player has of this talent
  195. /// </summary>
  196. public int GetRank(TalentId id)
  197. {
  198. Talent talent;
  199. if (ById.TryGetValue(id, out talent))
  200. {
  201. return talent.Rank;
  202. }
  203. return -1;
  204. }
  205. /// <summary>
  206. /// Whether this Owner has a certain Talent.
  207. /// </summary>
  208. /// <param name="id">The TalentId of the Talent</param>
  209. /// <returns>True if the Owner has the specified Talent</returns>
  210. public bool HasTalent(TalentId id)
  211. {
  212. return ById.ContainsKey(id);
  213. }
  214. /// <summary>
  215. /// Whether this Owner has a certain Talent.
  216. /// </summary>
  217. /// <param name="talent">The Talent in question.</param>
  218. /// <returns>True if the Owner has the specified Talent</returns>
  219. public bool HasTalent(Talent talent)
  220. {
  221. return ById.ContainsKey(talent.Entry.Id);
  222. }
  223. #endregion
  224. #region Remove & Reset
  225. public bool Remove(TalentId id)
  226. {
  227. var talent = GetTalent(id);
  228. if (talent != null)
  229. {
  230. talent.Remove();
  231. return true;
  232. }
  233. return false;
  234. }
  235. /// <summary>
  236. /// Removes the given amount of arbitrarily selected talents (always removes higher level talents first)
  237. /// </summary>
  238. public void RemoveTalents(int count)
  239. {
  240. var trees = Trees;
  241. // TODO: Remove depth-first, instead of breadth-first
  242. for (var i = 0; i < trees.Length; i++)
  243. {
  244. var tree = trees[i];
  245. while (m_treePoints[i] > 0 && count > 0)
  246. {
  247. for (var r = tree.TalentTable.Length - 1; r >= 0; r--)
  248. {
  249. var row = tree.TalentTable[r];
  250. foreach (var entry in row)
  251. {
  252. if (entry != null)
  253. {
  254. var talent = GetTalent(entry.Id);
  255. if (talent != null)
  256. {
  257. if (count >= talent.ActualRank)
  258. {
  259. count -= talent.ActualRank;
  260. talent.Remove();
  261. }
  262. else
  263. {
  264. talent.ActualRank -= count;
  265. count = 0;
  266. TalentHandler.SendTalentGroupList(this);
  267. return;
  268. }
  269. }
  270. }
  271. }
  272. }
  273. }
  274. }
  275. TalentHandler.SendTalentGroupList(this);
  276. }
  277. /// <summary>
  278. /// Resets all talents for free
  279. /// </summary>
  280. public void ResetAllForFree()
  281. {
  282. foreach (var talent in ById.Values)
  283. {
  284. talent.Remove();
  285. }
  286. ById.Clear();
  287. }
  288. /// <summary>
  289. ///
  290. /// </summary>
  291. public int CalculateCurrentResetTier()
  292. {
  293. if (OwnerCharacter.GodMode)
  294. {
  295. return 0;
  296. }
  297. var tier = CurrentResetTier;
  298. var lastResetTime = LastResetTime;
  299. if (lastResetTime == null)
  300. {
  301. return 0;
  302. }
  303. // count down the tier
  304. var timeLapse = DateTime.Now - lastResetTime.Value;
  305. var numDiscounts = (int)timeLapse.TotalHours / ResetTierDecayHours;
  306. var newPriceTier = tier - numDiscounts;
  307. if (newPriceTier < 0)
  308. {
  309. return 0;
  310. }
  311. return newPriceTier;
  312. }
  313. /// <summary>
  314. /// The amount of copper that it costs to reset all talents.
  315. /// Updates the current tier, according to the amount of time passed.
  316. /// </summary>
  317. public uint GetResetPrice()
  318. {
  319. var tier = CalculateCurrentResetTier();
  320. var tiers = ResetPricesPerTier;
  321. if (tier >= tiers.Length)
  322. {
  323. tier = tiers.Length - 1;
  324. }
  325. return tiers[tier];
  326. }
  327. /// <summary>
  328. /// Returns whether reseting succeeded or if it failed (due to not having enough gold)
  329. /// </summary>
  330. public bool ResetTalents()
  331. {
  332. var tier = CalculateCurrentResetTier();
  333. var tiers = ResetPricesPerTier;
  334. if (tier >= tiers.Length)
  335. {
  336. tier = tiers.Length - 1;
  337. }
  338. var price = tiers[tier];
  339. var chr = OwnerCharacter;
  340. if (price > chr.Money || chr.GodMode)
  341. {
  342. ResetAllForFree();
  343. FreeTalentPoints = GetFreeTalentPointsForLevel(Owner.Level);
  344. LastResetTime = DateTime.Now;
  345. chr.Money -= price;
  346. CurrentResetTier = tier + 1;
  347. return true;
  348. }
  349. return false;
  350. }
  351. #endregion
  352. #region Abstract & Virtual
  353. /// <summary>
  354. /// The Owner of this TalentCollection or the owning pet's Master
  355. /// </summary>
  356. public abstract Character OwnerCharacter
  357. {
  358. get;
  359. }
  360. public abstract int FreeTalentPoints
  361. {
  362. get;
  363. set;
  364. }
  365. public virtual int SpecProfileCount
  366. {
  367. get { return 1; }
  368. internal set
  369. {
  370. /* don't do nothing */
  371. LogManager.GetCurrentClassLogger().Warn("Tried to set Talents.TalentGroupCount for: {0}", Owner);
  372. }
  373. }
  374. /// <summary>
  375. /// The index of the currently used group of talents (one can have multiple groups due to multi spec)
  376. /// </summary>
  377. public abstract int CurrentSpecIndex
  378. {
  379. get;
  380. }
  381. public abstract uint[] ResetPricesPerTier
  382. {
  383. get;
  384. }
  385. protected abstract int CurrentResetTier
  386. {
  387. get;
  388. set;
  389. }
  390. public abstract DateTime? LastResetTime
  391. {
  392. get;
  393. set;
  394. }
  395. /// <summary>
  396. /// Amount of hours that it takes the reset price tier to go down by one
  397. /// </summary>
  398. public abstract int ResetTierDecayHours
  399. {
  400. get;
  401. }
  402. public abstract int GetFreeTalentPointsForLevel(int level);
  403. //TODO
  404. public abstract void UpdateFreeTalentPointsSilently(int delta);
  405. #endregion
  406. #region Enumerator
  407. /// <summary>
  408. /// Returns an enumerator that iterates through the collection.
  409. /// </summary>
  410. /// <returns>
  411. /// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
  412. /// </returns>
  413. /// <filterpriority>1</filterpriority>
  414. public IEnumerator<Talent> GetEnumerator()
  415. {
  416. return ById.Values.GetEnumerator();
  417. }
  418. /// <summary>
  419. /// Returns an enumerator that iterates through a collection.
  420. /// </summary>
  421. /// <returns>
  422. /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
  423. /// </returns>
  424. /// <filterpriority>2</filterpriority>
  425. IEnumerator IEnumerable.GetEnumerator()
  426. {
  427. return GetEnumerator();
  428. }
  429. #endregion
  430. }
  431. }