PageRenderTime 76ms CodeModel.GetById 43ms RepoModel.GetById 0ms app.codeStats 0ms

/Main/Mag-Tools/Trackers/Equipment/EquipmentTrackedItem.cs

#
C# | 350 lines | 225 code | 80 blank | 45 comment | 85 complexity | 9f298dc86b78ffd9cdafc74259a8b44b MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using Mag.Shared;
  4. using Decal.Adapter;
  5. using Decal.Adapter.Wrappers;
  6. using Decal.Filters;
  7. namespace MagTools.Trackers.Equipment
  8. {
  9. class EquipmentTrackedItem : IDisposable, IEquipmentTrackedItem
  10. {
  11. /// <summary>
  12. /// This is raised when an item the tracker is watching has been changed.
  13. /// </summary>
  14. public event Action<IEquipmentTrackedItem> Changed;
  15. public int Id { get; private set; }
  16. private DateTime timeOfLastManaIdent = DateTime.MinValue;
  17. private readonly System.Windows.Forms.Timer burnTimer = new System.Windows.Forms.Timer();
  18. public EquipmentTrackedItem(int id)
  19. {
  20. Id = id;
  21. WorldObject wo = CoreManager.Current.WorldFilter[Id];
  22. if (wo == null)
  23. return;
  24. CoreManager.Current.Actions.RequestId(Id);
  25. CoreManager.Current.WorldFilter.ChangeObject += new EventHandler<ChangeObjectEventArgs>(WorldFilter_ChangeObject);
  26. CoreManager.Current.ChatBoxMessage += new EventHandler<ChatTextInterceptEventArgs>(Current_ChatBoxMessage);
  27. burnTimer.Tick += new EventHandler(burnTimer_Tick);
  28. }
  29. private bool disposed;
  30. public void Dispose()
  31. {
  32. Dispose(true);
  33. // Use SupressFinalize in case a subclass
  34. // of this type implements a finalizer.
  35. GC.SuppressFinalize(this);
  36. }
  37. protected virtual void Dispose(bool disposing)
  38. {
  39. // If you need thread safety, use a lock around these
  40. // operations, as well as in your methods that use the resource.
  41. if (!disposed)
  42. {
  43. if (disposing)
  44. {
  45. CoreManager.Current.WorldFilter.ChangeObject -= new EventHandler<ChangeObjectEventArgs>(WorldFilter_ChangeObject);
  46. CoreManager.Current.ChatBoxMessage -= new EventHandler<ChatTextInterceptEventArgs>(Current_ChatBoxMessage);
  47. burnTimer.Tick -= new EventHandler(burnTimer_Tick);
  48. burnTimer.Dispose();
  49. }
  50. // Indicate that the instance has been disposed.
  51. disposed = true;
  52. }
  53. }
  54. void burnTimer_Tick(object sender, EventArgs e)
  55. {
  56. try
  57. {
  58. if (Changed != null)
  59. Changed(this);
  60. }
  61. catch (Exception ex) { Debug.LogException(ex); }
  62. }
  63. void WorldFilter_ChangeObject(object sender, ChangeObjectEventArgs e)
  64. {
  65. try
  66. {
  67. if (e.Changed.Id != Id)
  68. return;
  69. if (e.Change == WorldChangeType.IdentReceived && ItemState == EquipmentTrackedItemState.Active && SecondsPerBurn > 0)
  70. {
  71. burnTimer.Interval = (int)(SecondsPerBurn * 1000);
  72. burnTimer.Start();
  73. }
  74. // Change object between IdentReceived and ManaChange will take some explaning.
  75. // It clarifies why we store timeOfLastManaIdent instead of just using wo.LastIdTime.
  76. // When an IdentReceived is received on an item, it updates the LastIdTime as well as the LongValueKey.CurrentMana value.
  77. // However, when ManaChange is received, LongValueKey.CurrentMana is updated but LastIdTime is not.
  78. // Because of this, if we received a ManaChange and used LastIdTime to recalculate against LongValueKey.CurrentMana, it would obviously be off.
  79. // So, we simply cache the time we received an ident OR a mana change, that way we have the time our LongValueKey.CurrentMana was last updated,
  80. // and for our calculations, thats all we need.
  81. if (e.Change == WorldChangeType.IdentReceived || e.Change == WorldChangeType.ManaChange)
  82. {
  83. timeOfLastManaIdent = DateTime.UtcNow;
  84. if (Changed != null)
  85. Changed(this);
  86. }
  87. }
  88. catch (Exception ex) { Debug.LogException(ex); }
  89. }
  90. void Current_ChatBoxMessage(object sender, ChatTextInterceptEventArgs e)
  91. {
  92. try
  93. {
  94. if (e.Text.StartsWith("You say, ") || e.Text.Contains("says, \""))
  95. return;
  96. WorldObject wo = CoreManager.Current.WorldFilter[Id];
  97. if (wo == null)
  98. return;
  99. // The Mana Stone is destroyed.
  100. // The Mana Stone gives 11,376 points of mana to the following items: Satin Flared Shirt, Steel Chainmail Bracers, Velvet Trousers, Leather Loafers, Copper Chainmail Greaves, Enhanced White Empyrean Ring, Enhanced Red Empyrean Ring, Iron Diforsa Pauldrons, Bronze Chainmail Tassets, Copper Heavy Bracelet, Silver Olthoi Amuli Gauntlets, Ivory Heavy Bracelet, Steel Coronet, Emerald Amulet, Silver Puzzle Box, Sunstone Fire Sceptre
  101. // Your items are fully charged.
  102. if (e.Text.Contains("The Mana Stone gives "))
  103. {
  104. if (e.Text.Contains(wo.Name))
  105. CoreManager.Current.Actions.RequestId(Id);
  106. }
  107. // This is triggered when an item has 2 minutes left of mana.
  108. // Your Gold Olthoi Koujia Sleeves is low on Mana.
  109. // Your Bronze Haebrean Breastplate is out of Mana.
  110. if (e.Text.Contains("Your ") && (e.Text.Contains(" is low on Mana.") || e.Text.Contains(" is out of Mana.")))
  111. {
  112. if (e.Text.Contains(wo.Name))
  113. CoreManager.Current.Actions.RequestId(Id);
  114. }
  115. }
  116. catch (Exception ex) { Debug.LogException(ex); }
  117. }
  118. public int MaximumMana
  119. {
  120. get
  121. {
  122. WorldObject wo = CoreManager.Current.WorldFilter[Id];
  123. if (wo == null)
  124. return 0;
  125. return wo.Values(LongValueKey.MaximumMana, 0);
  126. }
  127. }
  128. private DateTime lastIdTime = DateTime.MinValue;
  129. private EquipmentTrackedItemState itemState = EquipmentTrackedItemState.Unknown;
  130. public EquipmentTrackedItemState ItemState
  131. {
  132. get
  133. {
  134. WorldObject wo = CoreManager.Current.WorldFilter[Id];
  135. if (wo == null)
  136. return EquipmentTrackedItemState.Unknown;
  137. if (lastIdTime != timeOfLastManaIdent || lastIdTime == DateTime.MinValue)
  138. {
  139. lastIdTime = timeOfLastManaIdent;
  140. itemState = RecaclulteState(wo);
  141. }
  142. return itemState;
  143. }
  144. }
  145. private EquipmentTrackedItemState RecaclulteState(WorldObject wo)
  146. {
  147. // We need basic IdData to determine if an item is active
  148. if (!wo.HasIdData)
  149. return EquipmentTrackedItemState.Unknown;
  150. // If this item has no spells, its not activateable
  151. if (wo.SpellCount == 0 || wo.Values(LongValueKey.MaximumMana) == 0)
  152. return EquipmentTrackedItemState.NotActivatable;
  153. // If this item has no mana in it, it's not active
  154. if (wo.Values(LongValueKey.CurrentMana, 0) == 0)
  155. return EquipmentTrackedItemState.NotActive;
  156. // Go through and find all of our current active spells (enchantments)
  157. List<int> activeSpellsOnChar = new List<int>();
  158. foreach (EnchantmentWrapper wrapper in CoreManager.Current.CharacterFilter.Enchantments)
  159. {
  160. // Only add ones that are cast by items (have no duration)
  161. if (wrapper.TimeRemaining <= 0)
  162. activeSpellsOnChar.Add(wrapper.SpellId);
  163. }
  164. FileService service = CoreManager.Current.Filter<FileService>();
  165. // Lets check if the item is not active We check to see if this item has any spells that are not activated.
  166. bool inactiveSpellFound = false;
  167. // Go through all of this items spells to determine if all are active.
  168. for (int i = 0 ; i < wo.SpellCount ; i++)
  169. {
  170. int spellOnItemId = wo.Spell(i);
  171. if (wo.Exists(LongValueKey.AssociatedSpell) && (wo.Values(LongValueKey.AssociatedSpell) == spellOnItemId))
  172. continue;
  173. Spell spellOnItem = service.SpellTable.GetById(spellOnItemId);
  174. // If it is offensive, it is probably a cast on strike spell
  175. if (spellOnItem.IsDebuff || spellOnItem.IsOffensive)
  176. continue;
  177. // Check if this particular spell is active
  178. bool thisSpellIsActive = false;
  179. // Check to see if this item cast any spells on itself.
  180. for (int j = 0 ; j < wo.ActiveSpellCount ; j++)
  181. {
  182. int activeSpellOnItemId = wo.ActiveSpell(j);
  183. if ((service.SpellTable.GetById(activeSpellOnItemId).Family == spellOnItem.Family) && (service.SpellTable.GetById(activeSpellOnItemId).Difficulty >= spellOnItem.Difficulty))
  184. {
  185. thisSpellIsActive = true;
  186. break;
  187. }
  188. }
  189. if (thisSpellIsActive)
  190. continue;
  191. // Check to see if this item cast any spells on the player.
  192. foreach (int j in activeSpellsOnChar)
  193. {
  194. if (service.SpellTable.GetById(j) != null && (service.SpellTable.GetById(j).Family == spellOnItem.Family) && (service.SpellTable.GetById(j).Difficulty >= spellOnItem.Difficulty))
  195. {
  196. thisSpellIsActive = true;
  197. break;
  198. }
  199. }
  200. if (thisSpellIsActive)
  201. continue;
  202. // This item has not cast this particular spell.
  203. inactiveSpellFound = true;
  204. break;
  205. }
  206. if (inactiveSpellFound)
  207. return EquipmentTrackedItemState.NotActive;
  208. return EquipmentTrackedItemState.Active;
  209. }
  210. private double SecondsPerBurn
  211. {
  212. get
  213. {
  214. WorldObject wo = CoreManager.Current.WorldFilter[Id];
  215. if (wo == null || !wo.HasIdData)
  216. return 0;
  217. if (wo.Values(DoubleValueKey.ManaRateOfChange, 0) == 0)
  218. return 0;
  219. // This doesn't take into account any luminance augs. It should, so that fix would go here.
  220. // Items can burn mana in 5 sec increments. If it says 1 every 18 sec, its really 1 every 20.
  221. return ((int)Math.Ceiling(-0.2 / wo.Values(DoubleValueKey.ManaRateOfChange))) * 5;
  222. }
  223. }
  224. /// <summary>
  225. /// This will factor in item.Values(DoubleValueKey.ManaRateOfChange) with item.Values(LongValueKey.CurrentMana) and item.LastIdTime to come up with how much
  226. /// mana this item should have right now.
  227. /// </summary>
  228. public int CalculatedCurrentMana
  229. {
  230. get
  231. {
  232. WorldObject wo = CoreManager.Current.WorldFilter[Id];
  233. if (wo == null || ItemState == EquipmentTrackedItemState.Unknown || ItemState == EquipmentTrackedItemState.NotActivatable)
  234. return 0;
  235. if (ItemState == EquipmentTrackedItemState.NotActive || SecondsPerBurn == 0)
  236. return wo.Values(LongValueKey.CurrentMana);
  237. //DateTime utcLastIdTime = new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(wo.LastIdTime);
  238. TimeSpan timeSinceLastIdent = DateTime.UtcNow - timeOfLastManaIdent;
  239. // Calculate how much mana has been burned off since we last identified the object.
  240. int burnedMana = (int)(timeSinceLastIdent.TotalSeconds / SecondsPerBurn);
  241. if (burnedMana > wo.Values(LongValueKey.CurrentMana))
  242. burnedMana = wo.Values(LongValueKey.CurrentMana);
  243. return Math.Max(wo.Values(LongValueKey.CurrentMana) - burnedMana, 0);
  244. }
  245. }
  246. public int ManaNeededToRefill
  247. {
  248. get
  249. {
  250. return Math.Max(MaximumMana - CalculatedCurrentMana, 0);
  251. }
  252. }
  253. /// <summary>
  254. /// This will return the amount of time that the CurrentMana will keep this item activated.
  255. /// If the item is not active, it will return a timespan of 0.
  256. /// </summary>
  257. public TimeSpan ManaTimeRemaining
  258. {
  259. get
  260. {
  261. WorldObject wo = CoreManager.Current.WorldFilter[Id];
  262. if (wo == null || ItemState != EquipmentTrackedItemState.Active)
  263. return new TimeSpan();
  264. if (SecondsPerBurn == 0)
  265. return new TimeSpan(99, 99, 0);
  266. return TimeSpan.FromSeconds(CalculatedCurrentMana * SecondsPerBurn);
  267. }
  268. }
  269. }
  270. }