vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2 Healers Assist by Kiki - European Cho'gall
3  
4 TODO :
5  
6 GESTION/CONFIG :
7 - Handle Talent/Armor bonus in _HA_GetDuration function
8 - Handle pets (with config option to show them in emergency)
9 - For the buf request, add an option to refuse requests when in combat (for some bufs, maybe configurable per buf with 3 options, deny:not-in-combat:always)
10  
11 GUI :
12 - Possibility to configure color used in the HA_GUI_Process_Announce function
13 - Possibility to only show hots I can cast in emergency list
14 - Emergency list : Show in another color, people out of my healing range (CheckInteractDistance() is 30yards max, maybe show in yellow)
15 - Show an estimated time of remaining regen time for a healer in Resting mode (dans le champ de casting) (new field EndRegenTime to compute in HA_Healers struct)
16 - Show remaining effect time of a cooldown spell casted on a healer
17  
18 SORTING :
19 [Quote from Freddy] Some feature ideas I heard about the emergency list:
20 - Mark players which are in your party (different color of HP bar?) + priority option
21 - Option to limit filter the estimated heal value that is added to the HP by casting time (e.g. only add heal which should have finished in <1.5sek)
22 - Make an option which allowes it to set a fixed value of HP difference to show up the player in the list (not percentage)
23 - Add something which put's the tanks at a higher priorty (class specific settings for casting tiem filter?)
24  
25 TODO WARRIOR PLUGIN :
26 - Show for "YOU", current casts on yourself, with casting times (sorted by casting time)
27 -> Prints a warning to yourself (use healing potion !! for example) if no spell is coming, and you have the agro, or gonna die soon ^^
28  
29 BUGS :
30 - Unit IDs changing during a SpellRequest (raid changes while the SpellRequest popup is shown -> possible wrong unitid) (NOT SURE IF THIS IS POSSIBLE)
31 -> In _HA_SetRaiderVariables function (and GA callback)
32 -> Callback a function in GUI to update all internal variables using IDs (like SpellRequest)
33 - RaiderDeath not always correct (maybe check with hp==0 ?)
34 - Hunter that Feign death will be counted as "dead/rezzed"
35 - Druid shifting back to humain with full mana, will show empty mana (need to grab mp/mpmax after a shift)
36 - Without GroupAnalyse, if I'm in Group Mode (not raid) all members are set as GroupLeader
37 - Heal on an external player or NPC (out of raid), will trigger overheal status after spell complete -> Well... nevermind right now
38  
39 ChangeLog :
40 - 2006/09/12 : 1.1
41 - Fixed incorrect healing debuff detection of Nefarius encounter
42 - 2006/08/31 :
43 - Fixed Item Bonuses not correct with latest BonusScanner addon installed (Scan not done, if no other addon ask BS for a scan)
44 - 2006/08/30 :
45 - Fixed minor xml error (minimap button not highlighting correctly (thanks Kagar)
46 - Fixed initialization issue, if not using GroupAnalyse addon
47  
48 [Full ChangeLog in readme.txt file]
49 ]]
50  
51  
52  
53 --------------- Shared Constantes ---------------
54 HA_MODE_NONE = 0;
55 HA_MODE_SOLO = 1
56 HA_MODE_GROUP = 2
57 HA_MODE_RAID = 3;
58  
59 --------------- Shared variables ---------------
60 HA_VERSION = "1.1";
61 HA_PlayerName = nil;
62 HA_CurrentTarget = nil;
63 HA_LibramItem = nil;
64 HA_Healers = {};
65 HA_Raiders = {};
66 HA_RaidersByID = {};
67 HA_CurrentGroupMode = HA_MODE_NONE;
68 HA_MyselfHealer = nil;
69 HA_MyselfRaider = nil;
70 HA_CurrentZone = nil;
71 HA_SpellCooldowns = {};
72 HA_AFK_Mode = false;
73 HA_ClassesID = {
74 ["DRUID"] = 1;
75 ["HUNTER"] = 2;
76 ["MAGE"] = 3;
77 ["PRIEST"] = 4;
78 ["ROGUE"] = 5;
79 ["WARLOCK"] = 6;
80 ["WARRIOR"] = 7;
81 ["PALADIN"] = 8;
82 ["SHAMAN"] = 8;
83 };
84  
85 --------------- Local Constantes ---------------
86 local HA_COOLDOWN_SPELLS_UPDATE_DELAY = 60; -- Every 60sec
87 local HA_VERSION_SEND_DELAY = 60; -- Every 60sec
88 local HA_MAX_HEAL_UPDATES = 10; -- Keep max 10 incoming heal per raider
89 local _HA_LastTimeEmergency = 0;
90  
91 --------------- Local variables ---------------
92 local HA_NeedInit = true;
93 local HA_CastingSpell = nil;
94 local HA_HookActionName = nil;
95 local HA_HookActionRank = 0;
96 local HA_StopCommandSent = true;
97 local HA_StopCommandLastTime = 0;
98 local HA_CastingInstantSpell = nil;
99 local HA_CastingInstantRank = 0;
100 local HA_SpellTargetName = nil;
101 local HA_PotentialSpellTargetName = nil;
102 local HA_LastRegrowthRank = 0;
103 local HA_MaxRanks = {};
104 local _HA_UseBonusScannerAddon = false;
105 local _HA_BonusScanScheduled = false;
106 local _HA_LastSendVersion = 0;
107  
108  
109 --------------- Internal functions ---------------
110  
111 function HA_SetNewLock(printmsg)
112 if(HA_Config.Lock)
113 then
114 HealersAssistMainFrame:EnableMouse("false");
115 if(printmsg)
116 then
117 HA_ChatPrint(HA_CHAT_LOCK_ON);
118 end
119 else
120 HealersAssistMainFrame:EnableMouse("true");
121 if(printmsg)
122 then
123 HA_ChatPrint(HA_CHAT_LOCK_OFF);
124 end
125 end
126 end
127  
128 local function _HA_GetMaxSpellRanks()
129 local maxRanks = {};
130 local index = 1;
131 while(1) do
132 local spellName, spellRank = GetSpellName(index, BOOKTYPE_SPELL)
133 if(not spellName) then
134 break;
135 elseif(HA_Spells[spellName]) then
136 local _,_,ranknum = string.find(spellRank,HA_RANK.." (%d+)");
137 ranknum = tonumber(ranknum,10);
138 if(not maxRanks[spellName]) then
139 maxRanks[spellName] = ranknum;
140 elseif(ranknum > maxRanks[spellName]) then
141 maxRanks[spellName] = ranknum;
142 end
143 end
144 index = index + 1
145 end
146 return maxRanks;
147 end
148  
149 local function _HA_SetRaiderAsHealer(raider)
150 if(not raider.ishealer)
151 then
152 raider.ishealer = true;
153 -- Call plugins
154 for n,pl in HA_ActivePlugins
155 do
156 if(pl.OnEvent)
157 then
158 pl.OnEvent(HA_EVENT_HEALER_JOINED,{raider.name});
159 end
160 end
161 end
162 end
163  
164 local function _HA_UpdateOtherVariables(raider)
165 raider.percent = floor(raider.hp / raider.hpmax * 100);
166 raider.mppercent = floor(raider.mp / raider.mpmax * 100);
167  
168 local healer = HA_Healers[raider.name];
169 if(healer and (raider.class == "PRIEST" or raider.class == "DRUID" or raider.class == "PALADIN" or raider.class == "SHAMAN")) -- This is a healer
170 then
171 _HA_SetRaiderAsHealer(raider);
172 healer.id = raider.id;
173 healer.Raider = raider;
174 end
175 end
176  
177 local function _HA_SetMinimalRaiderVariables(raider,raidid,rank,subgrp)
178 raider.id = raidid;
179 HA_RaidersByID[raidid] = raider;
180 raider.rank = rank;
181 raider.subgrp = subgrp;
182 end
183  
184 local function _HA_SetFullRaiderVariables(raider,raidid,rank,subgrp)
185 _HA_SetMinimalRaiderVariables(raider,raidid,rank,subgrp);
186 _,raider.class = UnitClass(raider.id);
187 raider.classid = HA_ClassesID[raider.class];
188 raider.isdead = UnitIsDeadOrGhost(raider.id);
189 raider.oldisdead = raider.isdead;
190 raider.hp_real = UnitHealth(raider.id);
191 raider.hpmax = UnitHealthMax(raider.id);
192 raider.ischarmed = UnitIsCharmed(raider.id);
193 raider.mp = UnitMana(raider.id);
194 raider.mpmax = UnitManaMax(raider.id);
195 raider.isconnected = UnitIsConnected(raider.id);
196  
197 raider.hp = raider.hp_real;
198 raider.hp_estim = raider.hp_real;
199 raider.ignore_next_wound = 0;
200 raider.life_updates = {};
201 raider.heal_updates = {};
202  
203 _HA_UpdateOtherVariables(raider);
204 end
205  
206 local function _HA_CreateRaider(name)
207 local infos = {};
208 infos.name = name;
209 infos.count = 0;
210 infos.estimates = {};
211 infos.estimate_ratio = 1;
212 infos.overtime = {};
213 return infos;
214 end
215  
216 local function _HA_AddHealer(pl_name)
217 if(HA_Healers[pl_name] == nil)
218 then
219 local infos = {};
220 local raider = HA_Raiders[pl_name];
221 infos.Cooldown = {};
222 infos.Name = pl_name;
223 infos.State = HA_STATE_NOTHING;
224 infos.OverHealPercent = 0;
225 infos.EstimateRatio = 1;
226 infos.Estimate = 0;
227 infos.StartTime = 0;
228 infos.Raider = raider;
229  
230 if(raider)
231 then
232 local class = raider.class;
233 infos.id = raider.id;
234 if(class == "PRIEST" or class == "DRUID" or class == "PALADIN" or class == "SHAMAN")
235 then
236 HA_ChatDebug(HA_DEBUG_MEMBERS,"HA_CheckPlayerJoined : "..pl_name.." is in my Raiders list and is a Healer");
237 _HA_SetRaiderAsHealer(raider);
238 else
239 HA_ChatDebug(HA_DEBUG_MEMBERS,"HA_CheckPlayerJoined : "..pl_name.." is in my Raiders list but is not a Healer");
240 end
241 else
242 HA_ChatDebug(HA_DEBUG_MEMBERS,"HA_CheckPlayerJoined : "..pl_name.." is not in my Raiders list");
243 end
244  
245 HA_Healers[pl_name] = infos;
246 if(pl_name == HA_PlayerName)
247 then
248 HA_MyselfHealer = infos;
249 HA_ChatDebug(HA_DEBUG_GLOBAL,"Setting MYSELF as HEALER : "..tostring(infos));
250 HA_GUI_Process_Version(HA_PlayerName,HA_VERSION);
251 end
252 end
253 end
254  
255 local function _HA_AddRaider(name,tab)
256 HA_Raiders[name] = tab;
257 HA_RaidersByID[tab.id] = tab;
258 HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_AddRaider : Adding raider "..name.." with id="..tab.id);
259 if(name == HA_PlayerName) -- Myself
260 then
261 HA_MyselfRaider = tab;
262 HA_ChatDebug(HA_DEBUG_GLOBAL,"Setting MYSELF as RAIDER : "..tostring(tab));
263 end
264 -- Call plugins
265 for _,pl in HA_ActivePlugins
266 do
267 if(pl.OnEvent)
268 then
269 pl.OnEvent(HA_EVENT_RAIDER_JOINED,{name});
270 end
271 end
272 if(tab.class == "PRIEST" or tab.class == "DRUID" or tab.class == "PALADIN" or tab.class == "SHAMAN")
273 then
274 _HA_AddHealer(name);
275 end
276 end
277  
278 local function _HA_RemoveHealer(pl_name)
279 if(HA_Healers[pl_name])
280 then
281 -- Call plugins
282 for n,pl in HA_ActivePlugins
283 do
284 if(pl.OnEvent)
285 then
286 pl.OnEvent(HA_EVENT_HEALER_LEFT,{pl_name});
287 end
288 end
289 HA_Healers[pl_name] = nil;
290 end
291 end
292  
293 local function _HA_RemoveRaider(name)
294 local raider = HA_Raiders[name];
295 if(raider)
296 then
297 HA_RaidersByID[raider.id] = nil;
298 end
299 HA_Raiders[name] = nil;
300 HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_RemoveRaider : Removed raider "..name);
301 -- Call plugins
302 for _,pl in HA_ActivePlugins
303 do
304 if(pl.OnEvent)
305 then
306 pl.OnEvent(HA_EVENT_RAIDER_LEFT,{name});
307 end
308 end
309 _HA_RemoveHealer(name); -- Check for healer removal
310 end
311  
312 local function _HA_AnalyseGroupRaidMembers()
313 local newmode = HA_MODE_NONE;
314 local new_raiders = {};
315 HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_AnalyseGroupRaidMembers : Start Analyse");
316  
317 if(GetNumRaidMembers() ~= 0) -- In a raid
318 then
319 if(HA_CurrentGroupMode ~= HA_MODE_RAID) -- But was not
320 then
321 HealersAssistMainFrame:UnregisterEvent("PARTY_MEMBERS_CHANGED");
322 end
323 HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_AnalyseGroupRaidMembers : I'm in a RAID");
324 newmode = HA_MODE_RAID;
325 local count = GetNumRaidMembers();
326 for i = 1, count do
327 local name,rank,subgrp = GetRaidRosterInfo(i);
328 if(name)
329 then
330 new_raiders[name] = _HA_CreateRaider(name);
331 _HA_SetMinimalRaiderVariables(new_raiders[name],"raid"..i,rank,subgrp);
332 end
333 end
334 else
335 if(HA_CurrentGroupMode == HA_MODE_RAID) -- Was in a RAID
336 then
337 HealersAssistMainFrame:RegisterEvent("PARTY_MEMBERS_CHANGED");
338 end
339 new_raiders[HA_PlayerName] = _HA_CreateRaider(HA_PlayerName);
340 _HA_SetMinimalRaiderVariables(new_raiders[HA_PlayerName],"player",2,1);
341 if(GetNumPartyMembers() ~= 0) -- In a group
342 then
343 HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_AnalyseGroupRaidMembers : I'm in a GROUP");
344 newmode = HA_MODE_GROUP;
345 for i = 1,4 do
346 local name = UnitName("party"..i);
347 if(name and (name ~= UNKNOWNOBJECT) and (name ~= UKNOWNBEING))
348 then
349 new_raiders[name] = _HA_CreateRaider(name);
350 _HA_SetMinimalRaiderVariables(new_raiders[name],"party"..i,2,1);
351 end
352 end
353 else
354 HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_AnalyseGroupRaidMembers : I'm not grouped");
355 newmode = HA_MODE_SOLO;
356 end
357 end
358  
359 -- Update list of Raiders
360 for n,tab in HA_Raiders -- Remove old raiders
361 do
362 if(new_raiders[n] == nil) -- No longer in the raid
363 then
364 _HA_RemoveRaider(n);
365 end
366 end
367  
368 HA_RaidersByID = {};
369 for n,tab in new_raiders -- Add new raiders
370 do
371 local raider = HA_Raiders[n];
372 if(raider == nil) -- New member
373 then
374 _HA_SetFullRaiderVariables(tab,tab.id,tab.rank,tab.subgrp);
375 _HA_AddRaider(n,tab);
376 else -- Was already here -> Update variables
377 _HA_SetMinimalRaiderVariables(raider,tab.id,tab.rank,tab.subgrp);
378 _HA_UpdateOtherVariables(raider);
379 end
380 end
381  
382 if(HA_Config.Auto and newmode ~= HA_CurrentGroupMode)
383 then
384 if(newmode == HA_MODE_SOLO)
385 then
386 HealersAssistMainFrame:Hide();
387 else
388 HealersAssistMainFrame:Show();
389 end
390 end
391 HA_CurrentGroupMode = newmode;
392  
393 HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_AnalyseGroupRaidMembers : Analyse Completed");
394 end
395  
396 local function _HA_GroupAnalyseCallback(event,param)
397 local infos;
398 if(event == GA_EVENT_INFOS_CHANGED)
399 then
400 HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_GroupAnalyseCallback : Start members data update");
401 HA_RaidersByID = {};
402 debugprofilestart();
403 for name,member in GA_Members
404 do
405 infos = HA_Raiders[name];
406 if(infos)
407 then
408 infos.id = member.unitid;
409 HA_RaidersByID[member.unitid] = infos;
410 infos.rank = member.rank;
411 infos.subgrp = member.subgrp;
412 infos.ischarmed = member.ischarmed;
413 infos.isconnected = member.isconnected;
414 _HA_UpdateOtherVariables(infos);
415 end
416 end
417 HA_PROFILE_AnalyseRaidersRoutine = debugprofilestop();
418 HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_GroupAnalyseCallback : Update Completed");
419 elseif(event == GA_EVENT_MEMBER_JOINED)
420 then
421 infos = _HA_CreateRaider(param);
422 local member = GA_Members[param];
423 _HA_SetFullRaiderVariables(infos,member.unitid,member.rank,member.subgrp);
424 _HA_AddRaider(param,infos);
425 elseif(event == GA_EVENT_MEMBER_LEFT)
426 then
427 _HA_RemoveRaider(param);
428 elseif(event == GA_EVENT_GROUP_MODE_CHANGED)
429 then
430 HA_ChatDebug(HA_DEBUG_GLOBAL,"_HA_GroupAnalyseCallback : Group mode changed to "..param);
431 if(HA_Config.Auto)
432 then
433 if(param == GA_MODE_SOLO)
434 then
435 HealersAssistMainFrame:Hide();
436 else
437 HealersAssistMainFrame:Show();
438 end
439 end
440 HA_CurrentGroupMode = param;
441 end
442 end
443  
444 local function _HA_CooldownUpdateScheduleRoutine()
445 local serv_time = GetTime();
446 local tim = time();
447 -- Update my cooldown status
448 if(HA_MyselfHealer)
449 then
450 for spell,infos in HA_SpellCooldowns
451 do
452 -- Update my cooldown
453 local startTime,cd_value = GetSpellCooldown(infos.id,"spell");
454 local Cooldown = 0;
455 if(startTime ~= 0 and cd_value > 30) -- (Prevent global cooldown and silence spells to interfere)
456 then
457 Cooldown = floor((startTime+cd_value) - serv_time);
458 end
459  
460 -- Check for update send
461 if(not HA_AFK_Mode and ((Cooldown == 0 and infos.last ~= 0) or -- Was in cooldown, but not anymore
462 (Cooldown ~= 0 and infos.last == 0) or -- Was up, but in cooldown now
463 (tim > (infos.lastSend+HA_COOLDOWN_SPELLS_UPDATE_DELAY)))) -- Delay expired
464 then
465 HA_GUI_Process_CooldownUpdate(HA_PlayerName,infos.ispell,Cooldown);
466 HA_COM_CooldownUpdate(infos.ispell,Cooldown);
467 infos.lastSend = tim;
468 infos.last = Cooldown;
469 end
470 end
471 -- Check for HA version send
472 if(not HA_AFK_Mode and (tim > (_HA_LastSendVersion+HA_VERSION_SEND_DELAY))) -- Delay expired
473 then
474 HA_ChatDebug(HA_DEBUG_GLOBAL,"Too long since last Version send");
475 HA_COM_SendVersion();
476 _HA_LastSendVersion = tim;
477 end
478 end
479  
480 -- Update other cooldown status
481 for name,tab in HA_Healers
482 do
483 for spell,infos in tab.Cooldown
484 do
485 if(infos.Remain ~= 0) -- Player having a Cooldown spell
486 then
487 infos.Remain = infos.Remain - (serv_time - infos.Start);
488 if(infos.Remain <= 0)
489 then
490 infos.Remain = 0;
491 end
492 infos.Start = serv_time;
493 end
494 end
495 end
496  
497 -- Re schedule
498 HASystem_Schedule(1,_HA_CooldownUpdateScheduleRoutine);
499 end
500  
501 local function _HA_SetDefaultConfig(param,value)
502 if(HA_Config[param] == nil)
503 then
504 HA_Config[param] = value;
505 end
506 end
507  
508 local function _HA_ScheduledInit()
509 HA_InitializeCooldownSpells();
510 HASystem_Schedule(1,_HA_CooldownUpdateScheduleRoutine); -- Special cooldown routine
511 HASystem_Schedule(1,HA_OvertimeScheduleRoutine); -- Special overtime routine
512 HASystem_Schedule(1,HA_StatusScheduleRoutine); -- Special status routine
513 end
514  
515 local function HA_StartupInitVars()
516 local playerName = UnitName("player");
517 if((playerName) and (playerName ~= UNKNOWNOBJECT) and (playerName ~= UKNOWNBEING))
518 then
519 -- Initialize Toon specific stuff
520 HA_PlayerName = playerName;
521 HA_NeedInit = false;
522 _HA_SetDefaultConfig("MinEmergencyPercent",90);
523 _HA_SetDefaultConfig("KeepValue",3);
524 _HA_SetDefaultConfig("ShowInstants",false);
525 _HA_SetDefaultConfig("ShowHoT",false);
526 _HA_SetDefaultConfig("ButtonPosition",190);
527 _HA_SetDefaultConfig("EmergencyGroups",{ true,true,true,true,true,true,true,true });
528 _HA_SetDefaultConfig("EmergencyClasses",{ true,true,true,true,true,true,true,true });
529 _HA_SetDefaultConfig("HealersClasses",{ true,true,true,true,true,true,true,true });
530 _HA_SetDefaultConfig("AllowSpellRequest",{});
531 _HA_SetDefaultConfig("HealersLines",10);
532 _HA_SetDefaultConfig("EmergLines",5);
533 _HA_SetDefaultConfig("Scale",100);
534 _HA_SetDefaultConfig("Alpha",100);
535 _HA_SetDefaultConfig("BackdropAlpha",100);
536 _HA_SetDefaultConfig("GUIRefresh",0.1);
537 _HA_SetDefaultConfig("Plugins",{});
538 _HA_SetDefaultConfig("PluginOrder",{});
539 _HA_SetDefaultConfig("PluginAuto",{});
540 _HA_SetDefaultConfig("UseEstimatedHealth",true);
541 HA_CheckLoadPlugins();
542 HA_SetNewLock(false);
543 HASystem_Schedule(5,_HA_ScheduledInit);
544 HA_MoveMinimapButton();
545 -- Setup Default function
546 if(CastParty_OnClickByUnit and type(CastParty_OnClickByUnit) == "function") -- CastParty
547 then
548 HA_CustomOnClickFunction = CastParty_OnClickByUnit;
549 end
550 if(WatchDog_OnClick and type(WatchDog_OnClick) == "function") -- WatchDog
551 then
552 HA_CustomOnClickFunction = WatchDog_OnClick;
553 end
554 if(JC_CatchKeyBinding and type(JC_CatchKeyBinding) == "function") -- JustClick
555 then
556 HA_CustomOnClickFunction = JC_CatchKeyBinding;
557 end
558 end
559 end
560  
561 function HA_PlayerHasBuf(texture)
562 for i=1,16
563 do
564 local t = UnitBuff("player",i);
565 if(t == nil) then break; end;
566 if(t == texture)
567 then
568 return true;
569 end
570 end
571 return false;
572 end
573  
574 local function HA_BuildRankString(ranknum)
575 local rankstr = "";
576  
577 if(ranknum and ranknum > 1)
578 then
579 for i=1,ranknum do
580 rankstr = rankstr.."I";
581 end
582 end
583  
584 return rankstr;
585 end
586  
587 local function HA_Commands(command)
588 local i,j, cmd, param = string.find(command, "^([^ ]+) (.+)$");
589 if(not cmd) then cmd = command; end
590 if(not cmd) then cmd = ""; end
591 if(not param) then param = ""; end
592  
593 if((cmd == "") or (cmd == "help"))
594 then
595 local lock = "off";
596 if(HA_Config.Lock) then lock = "on"; end
597 local auto = "off";
598 if(HA_Config.Auto) then auto = "on"; end
599 HA_ChatPrint("Usage:");
600 HA_ChatPrint(" |cffffffff/ha show|r - "..HA_CHAT_HELP_SHOW);
601 HA_ChatPrint(" |cffffffff/ha lock (on|off)|r |cff2040ff["..lock.."]|r - "..HA_CHAT_HELP_LOCK);
602 HA_ChatPrint(" |cffffffff/ha auto (on|off)|r |cff2040ff["..auto.."]|r - "..HA_CHAT_HELP_AUTO);
603 HA_ChatPrint(" |cffffffff/ha versions |r - "..HA_CHAT_HELP_VERSIONS);
604 HA_ChatPrint(" |cffffffff/ha msg <Msg to send>|r - "..HA_CHAT_HELP_MSG);
605 HA_ChatPrint(" ---- DEBUG ----");
606 HA_ChatPrint(" |cffffffff/ha debug (on|off)|r - "..HA_CHAT_HELP_DEBUG);
607 elseif(cmd == "show")
608 then
609 HealersAssistMainFrame:Show();
610 elseif(cmd == "lock")
611 then
612 if(param == "off")
613 then
614 HA_Config.Lock = nil;
615 elseif(param == "on")
616 then
617 HA_Config.Lock = true;
618 else
619 HA_ChatPrint(HA_CHAT_CMD_PARAM_ERROR.."lock");
620 return;
621 end
622 HA_SetNewLock(true);
623 elseif(cmd == "auto")
624 then
625 if(param == "off")
626 then
627 HA_Config.Auto = nil;
628 elseif(param == "on")
629 then
630 HA_Config.Auto = true;
631 else
632 HA_ChatPrint(HA_CHAT_CMD_PARAM_ERROR.."auto");
633 return;
634 end
635 elseif(cmd == "debug")
636 then
637 if(param == "off")
638 then
639 HA_DBG_SetDebugMode(0);
640 elseif(param == "on")
641 then
642 HA_DBG_SetDebugMode(1)
643 else
644 HA_ChatPrint(HA_CHAT_CMD_PARAM_ERROR.."debug");
645 return;
646 end
647 elseif(cmd == "versions")
648 then
649 HA_ShowVersions();
650 elseif(cmd == "msg" and param and param ~= "")
651 then
652 HA_GUI_Process_Announce(HA_PlayerName,param);
653 HA_COM_Announce(param);
654 else
655 HA_ChatPrint(HA_CHAT_CMD_UNKNOWN);
656 end
657 end
658  
659 function CheckSpellTarget(FunctionHook)
660 if(SpellIsTargeting())
661 then
662 HA_PotentialSpellTargetName = UnitName("mouseover");
663 HA_ChatDebug(HA_DEBUG_ACTIONS,FunctionHook.." : PotentialTarget="..tostring(HA_PotentialSpellTargetName));
664 elseif(UnitIsFriend("player","target"))
665 then
666 HA_SpellTargetName = UnitName("target");
667 HA_ChatDebug(HA_DEBUG_ACTIONS,FunctionHook.." : Target="..tostring(HA_SpellTargetName));
668 end
669 end
670  
671 --------------- Hooked functions ---------------
672  
673 function HealersAssist_SpellTargetUnit(unit)
674 if(SpellIsTargeting() or not HA_SpellTargetName)
675 then
676 HA_SpellTargetName = UnitName(unit);
677 HA_ChatDebug(HA_DEBUG_ACTIONS,"SpellTargetUnit : Target="..tostring(HA_SpellTargetName));
678 end
679 return HA_Old_SpellTargetUnit(unit);
680 end
681  
682 HA_Old_SpellTargetUnit = SpellTargetUnit;
683 SpellTargetUnit = HealersAssist_SpellTargetUnit;
684  
685 --
686  
687 function HealersAssist_CastShapeshiftForm(index)
688 local ret_val = HA_Old_CastShapeshiftForm(index);
689  
690 HA_ChatDebug(HA_DEBUG_ACTIONS,"CastShapeshiftForm : Index="..tostring(index));
691  
692 HA_CastingInstantSpell = nil;
693 HA_CastingInstantRank = 0;
694 return ret_val;
695 end
696  
697 HA_Old_CastShapeshiftForm = CastShapeshiftForm;
698 CastShapeshiftForm = HealersAssist_CastShapeshiftForm;
699  
700 --
701  
702 function HealersAssist_UseAction(id, val, onSelf)
703 HealersAssistTooltip:SetOwner(UIParent, "ANCHOR_NONE");
704 HealersAssistTooltip:ClearLines();
705 HealersAssistTooltip:SetAction(id);
706 local spellName = tostring(HealersAssistTooltipTextLeft1:GetText());
707 local rank = HealersAssistTooltipTextRight1:GetText();
708 local rankstr = "";
709 HealersAssistTooltip:Hide();
710  
711 local ret_val = HA_Old_UseAction(id,val,onSelf);
712  
713 if(rank and rank ~= "")
714 then
715 local _,_,ranknum = string.find(rank,HA_RANK.." (%d+)");
716 rank = tonumber(ranknum,10);
717 elseif(HA_MaxRanks[spellName])
718 then
719 rank = HA_MaxRanks[spellName];
720 else
721 rank = 1;
722 end
723  
724 -- Call plugins
725 for n,pl in HA_ActivePlugins
726 do
727 if(pl.OnEvent)
728 then
729 pl.OnEvent(HA_EVENT_USE_ACTION,{id,val,onSelf,spellName,rank});
730 end
731 end
732  
733 if(HA_Spells[spellName])
734 then
735 CheckSpellTarget("UseAction");
736 HA_ChatDebug(HA_DEBUG_ACTIONS,"UseAction : SpellName="..tostring(spellName).." Rank="..tostring(rank));
737 HA_HookActionName = spellName;
738 HA_HookActionRank = rank;
739 end
740  
741 if(HA_InstantSpells[spellName])
742 then
743 CheckSpellTarget("UseAction");
744 HA_CastingInstantSpell = spellName;
745 HA_CastingInstantRank = rank;
746 end
747  
748 -- If onSelf
749 if(onSelf and onSelf == 1)
750 then
751 HA_SpellTargetName = HA_PlayerName;
752 HA_ChatDebug(HA_DEBUG_ACTIONS,"UseAction on Self : Target="..tostring(HA_SpellTargetName));
753 end
754 return ret_val;
755 end
756  
757 HA_Old_UseAction = UseAction;
758 UseAction = HealersAssist_UseAction;
759  
760 --
761  
762 function HealersAssist_CastSpell(id,spellBook)
763 local ret_val = HealersAssist_Old_CastSpell(id,spellBook);
764  
765 local spellName,rank = GetSpellName(id,spellBook);
766  
767 if(rank and rank ~= "")
768 then
769 local _,_,ranknum = string.find(rank,HA_RANK.." (%d+)");
770 rank = tonumber(ranknum,10);
771 elseif(HA_MaxRanks[spellName])
772 then
773 rank = HA_MaxRanks[spellName];
774 else
775 rank = 1;
776 end
777  
778 -- Call plugins
779 for n,pl in HA_ActivePlugins
780 do
781 if(pl.OnEvent)
782 then
783 pl.OnEvent(HA_EVENT_CAST_SPELL,{id,spellBook,spellName,rank});
784 end
785 end
786  
787 if(HA_Spells[spellName])
788 then
789 CheckSpellTarget("CastSpell");
790 HA_ChatDebug(HA_DEBUG_ACTIONS,"CastSpell : SpellName="..tostring(spellName).." Rank="..tostring(rank));
791 HA_HookActionName = spellName;
792 HA_HookActionRank = rank;
793 end
794  
795 if(HA_InstantSpells[spellName])
796 then
797 CheckSpellTarget("CastSpell");
798 HA_CastingInstantSpell = spellName;
799 HA_CastingInstantRank = rank;
800 end
801 return ret_val;
802 end
803  
804 HealersAssist_Old_CastSpell = CastSpell;
805 CastSpell = HealersAssist_CastSpell;
806  
807 function HealersAssist_CastSpellByName(spell, onSelf)
808 local ret_val = HealersAssist_Old_CastSpellByName(spell, onSelf);
809  
810 if(spell)
811 then
812 local _,_,spellName,rank = string.find(spell,"(.+)%("..HA_RANK.." (%d+)%)");
813 if(spellName == nil) -- Spell without rank
814 then
815 spellName = spell;
816 end
817  
818 if(rank)
819 then
820 rank = tonumber(rank,10);
821 elseif(HA_MaxRanks[spellName])
822 then
823 rank = HA_MaxRanks[spellName];
824 else
825 rank = 1;
826 end
827  
828 -- Call plugins
829 for n,pl in HA_ActivePlugins
830 do
831 if(pl.OnEvent)
832 then
833 pl.OnEvent(HA_EVENT_CAST_SPELL_BY_NAME,{spell,onSelf,spellName,rank});
834 end
835 end
836  
837 if(HA_Spells[spellName])
838 then
839 CheckSpellTarget("CastSpellByName");
840 HA_ChatDebug(HA_DEBUG_ACTIONS,"CastSpellByName : SpellName="..tostring(spellName).." Rank="..tostring(rank));
841 HA_HookActionName = spellName;
842 HA_HookActionRank = rank;
843 end
844  
845 if(HA_InstantSpells[spellName])
846 then
847 CheckSpellTarget("CastSpellByName");
848 HA_CastingInstantSpell = spellName;
849 HA_CastingInstantRank = rank;
850 end
851 end
852 -- If onSelf
853 if(onSelf and onSelf == 1)
854 then
855 HA_SpellTargetName = HA_PlayerName;
856 HA_ChatDebug(HA_DEBUG_ACTIONS,"CastSpellByName on Self : Target="..tostring(HA_SpellTargetName));
857 end
858 return ret_val;
859 end
860  
861 HealersAssist_Old_CastSpellByName = CastSpellByName;
862 CastSpellByName = HealersAssist_CastSpellByName;
863  
864 --
865  
866 local function _HA_GetISpell(SpellName)
867 if(HA_Spells[SpellName])
868 then
869 return HA_Spells[SpellName].iname;
870 elseif(HA_InstantSpells[SpellName])
871 then
872 return HA_InstantSpells[SpellName].iname;
873 end
874 return nil;
875 end
876  
877 function HA_UnitHasBlessingOfLight(TargetName)
878 local id = nil;
879 if(TargetName)
880 then
881 if(HA_Raiders[TargetName] and HA_Raiders[TargetName].id)
882 then
883 id = HA_Raiders[TargetName].id;
884 elseif(UnitName("target") == TargetName)
885 then
886 id = "target";
887 end
888 end
889 if(id)
890 then
891 for i=1,16
892 do
893 local t = UnitBuff(id,i,1);
894 if(t == nil) then break; end;
895 if(t == HA_BLESSING_OF_LIGHT_TEXTURE or t == HA_GREATER_BLESSING_OF_LIGHT_TEXTURE)
896 then
897 return true;
898 end
899 end
900 end
901 return false;
902 end
903  
904 local function _HA_GetEstimated(ISpell,IHookName,HookRank,TargetName,CastTime)
905 local estimated = 0;
906 local spiritadd = 0;
907 local willcrit = false;
908  
909 if(IHookName and HookRank and ISpell == IHookName and HA_SpellRanks[ISpell] and HA_SpellRanks[ISpell][HookRank])
910 then
911 local infos = HA_SpellRanks[ISpell][HookRank];
912  
913 estimated = infos.base;
914  
915 -- Add Talent values
916 local tinfos = HA_SpellTalents[ISpell];
917 if(tinfos)
918 then
919 if(tinfos.ratios) -- Ratio
920 then
921 --[[ -- Old code (if talent bonus are applied one after another, and not all at once)
922 for _,tid in tinfos.ratios
923 do
924 if(HA_Talents[tid] and HA_Talents[tid].rank) -- Talent found, and player's rank set
925 then
926 if(HA_Talents[tid].rankratio) -- Direct ratios
927 then
928 estimated = estimated * HA_Talents[tid].rankratio[HA_Talents[tid].rank];
929 elseif(HA_Talents[tid].spiritratio) -- Spirit based ratios
930 then
931 spiritadd = HA_Talents[tid].spiritratio[HA_Talents[tid].rank] * UnitStat("player",5);
932 end
933 end
934 end
935 ]]
936 local total_percent = 1.0;
937 for _,tid in tinfos.ratios
938 do
939 if(HA_Talents[tid] and HA_Talents[tid].rank) -- Talent found, and player's rank set
940 then
941 if(HA_Talents[tid].rankratio) -- Direct ratios
942 then
943 total_percent = total_percent + HA_Talents[tid].rankratio[HA_Talents[tid].rank];
944 elseif(HA_Talents[tid].spiritratio) -- Spirit based ratios
945 then
946 spiritadd = HA_Talents[tid].spiritratio[HA_Talents[tid].rank] * UnitStat("player",5);
947 end
948 end
949 end
950 estimated = estimated * total_percent;
951 end
952 end
953  
954 -- Add +heal values
955 local itembonus;
956 if(_HA_UseBonusScannerAddon)
957 then
958 itembonus = tonumber(BonusScanner.bonuses["HEAL"]);
959 else
960 itembonus = tonumber(HA_BonusScanner_bonuses["HEAL"]);
961 end
962 if(itembonus)
963 then
964 if(HA_LibramItem) -- Check for librams
965 then
966 if(ISpell == HA_SPELL_FLASH_OF_LIGHT and HA_LibramItem == 23201) -- Libram of Divinity
967 then
968 itembonus = itembonus + 53;
969 elseif(ISpell == HA_SPELL_FLASH_OF_LIGHT and HA_LibramItem == 23006) -- Libram of Light
970 then
971 itembonus = itembonus + 83;
972 elseif(ISpell == HA_SPELL_LESSER_HEALING_WAVE and HA_LibramItem == 23200) -- Totem of Sustaining
973 then
974 itembonus = itembonus + 53;
975 elseif(ISpell == HA_SPELL_LESSER_HEALING_WAVE and HA_LibramItem == 22396) -- Totem of Life
976 then
977 itembonus = itembonus + 80;
978 elseif(ISpell == HA_SPELL_REJUVENATION and HA_LibramItem == 22398) -- Idol of Rejuvenation
979 then
980 itembonus = itembonus + 62.5; -- Real value is 50, but this value will be 80% applied below, but the game does not apply the 80% rule on the idol
981 end
982 end
983 estimated = estimated + itembonus * infos.castratio * infos.levelratio;
984 end
985  
986 -- Add + Blessing of light value
987 if(HA_SpellTalents[ISpell] and HA_SpellTalents[ISpell].blessing) -- Check for Blessing of light
988 then
989 if(HA_UnitHasBlessingOfLight(TargetName))
990 then
991 estimated = estimated + HA_SpellTalents[ISpell].blessing;
992 end
993 end
994  
995 -- Check for WillCrit
996 local index = 0;
997 local texture;
998 while(GetPlayerBuffTexture(index))
999 do
1000 texture = GetPlayerBuffTexture(index);
1001 applications = GetPlayerBuffApplications(index);
1002 if(texture == HA_DIVINE_FAVOR_TEXTURE)
1003 then
1004 willcrit = true;
1005 elseif(texture == HA_UNSTABLE_POWER_TEXTURE)
1006 then
1007 estimated = estimated + (70 * applications * infos.castratio * infos.levelratio);
1008 elseif(texture == HA_HEALING_OF_THE_AGES_TEXTURE)
1009 then
1010 estimated = estimated + (350 * infos.castratio * infos.levelratio);
1011 end
1012 index = index + 1;
1013 end
1014  
1015 if(willcrit)
1016 then
1017 estimated = estimated * 1.5;
1018 end
1019 end
1020  
1021 return floor(estimated+spiritadd),willcrit;
1022 end
1023  
1024 local function _HA_GetDuration(ISpell)
1025 local duration = 0;
1026  
1027 if(HA_SpellOvertime[ISpell])
1028 then
1029 local infos = HA_SpellOvertime[ISpell];
1030  
1031 duration = infos.duration;
1032  
1033 -- Add Talent values for duration
1034 -- Add armor values for duration
1035  
1036 end
1037  
1038 return duration;
1039 end
1040  
1041 local function _HA_DoStop(resetSpell,msg)
1042 HA_ChatDebug(HA_DEBUG_ACTIONS,"HA_DoStop : Event="..msg.." CmdSend="..tostring(HA_StopCommandSent).." LastTime="..HA_StopCommandLastTime.." CurTime="..HA_CurrentTime);
1043 if(not HA_StopCommandSent and (HA_StopCommandLastTime+0.10 < HA_CurrentTime))
1044 then
1045 HA_GUI_Process_SpellStop(HA_PlayerName);
1046 HA_COM_SpellStop();
1047 HA_StopCommandSent = true;
1048 HA_StopCommandLastTime = HA_CurrentTime;
1049 if(resetSpell)
1050 then
1051 HA_CastingSpell = nil;
1052 end
1053 end
1054 end
1055  
1056 local function _HA_RegExpr_CallBack(event, Source, Target, SpellName, Value, Crit)
1057 -- Check for HoT Ticks
1058 if(event == "CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS" or
1059 event == "CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS" or
1060 event == "CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS")
1061 then
1062 local ispell = HA_HotSpells[SpellName];
1063 if(ispell)
1064 then
1065 HA_ChatDebug(HA_DEBUG_SPELLS,"Callback HOT : Source="..tostring(Source).." Target="..tostring(Target).." Spell="..tostring(SpellName).." ("..ispell..") Value="..tostring(Value));
1066 HA_GUI_Process_SpellHotTick(Source,ispell,Target,Value);
1067 HA_COM_SpellHotTick(ispell,Target,Value);
1068 end
1069  
1070 else -- Ok, check for direct heal spells
1071 local spell = HA_Spells[SpellName];
1072 local instaspell = false;
1073 if(spell == nil) -- Not casted, check instant
1074 then
1075 spell = HA_InstantSpells[SpellName];
1076 instaspell = true;
1077 end
1078 if(spell)
1079 then
1080 if(event == "CHAT_MSG_SPELL_SELF_BUFF") -- Heal from self
1081 then
1082 local ispell = _HA_GetISpell(SpellName);
1083 if(not instaspell)
1084 then
1085 _HA_DoStop(true,"HIT");
1086 end
1087 HA_GUI_Process_SpellHit(Source,ispell,Target,Value,Crit);
1088 HA_COM_SpellComplete(ispell,Target,Value,Crit);
1089 -- Special HA_SPELL_REGROWTH case
1090 if(ispell == HA_SPELL_REGROWTH)
1091 then
1092 ispell = HA_SPELL_REGROWTH_HOT;
1093 local estimated = _HA_GetEstimated(ispell,ispell,HA_LastRegrowthRank,Target,0);
1094 local duration = _HA_GetDuration(ispell);
1095 if(duration ~= 0)
1096 then
1097 HA_GUI_Process_SpellOvertime(HA_PlayerName,ispell,Target,duration,estimated,HA_LastRegrowthRank);
1098 HA_COM_SpellOvertime(ispell,Target,duration,estimated,HA_LastRegrowthRank);
1099 end
1100 end
1101 else -- Heal from other
1102 if(not spell.group)
1103 then
1104 local ispell = _HA_GetISpell(SpellName);
1105 HA_GUI_Process_SpellHit(Source,ispell,Target,Value,Crit);
1106 end
1107 end
1108 end
1109 end
1110 end
1111  
1112 local function _HA_CheckRegenModeFromCast()
1113 local healer = HA_MyselfHealer;
1114 if(healer and healer.State == HA_STATE_RESTING)
1115 then
1116 HA_SetRegenMode(false);
1117 end
1118 end
1119  
1120 local function _HA_ScanScheduleRoutine()
1121 HA_ChatDebug(HA_DEBUG_GLOBAL,"_HA_ScanScheduleRoutine : Scanning bonuses");
1122 -- Check for Items bonuses
1123 if(not _HA_UseBonusScannerAddon)
1124 then
1125 HA_ChatDebug(HA_DEBUG_GLOBAL,"_HA_ScanScheduleRoutine : Not using BonusScanner addon - Scanning equipment !");
1126 HA_BonusScanner_ScanAll();
1127 end
1128 -- Check for Relic
1129 local libramlink = GetInventoryItemLink("player",18);
1130 if(libramlink)
1131 then
1132 for iItemId, iEnchantId, iPropertieId, sItemName in string.gfind(libramlink, "|c%x+|Hitem:(%d+):(%d+):(%d+):%d+|h%[(.-)%]|h|r")
1133 do
1134 HA_LibramItem = tonumber(iItemId);
1135 end
1136 else
1137 HA_LibramItem = nil;
1138 end
1139 -- Ok done
1140 _HA_BonusScanScheduled = false;
1141 end
1142  
1143 local function _HA_GetReasonCode(Reason)
1144 for i,n in HA_FailReasons
1145 do
1146 if(Reason == n)
1147 then
1148 return i,"";
1149 end
1150 end
1151 return 0,Reason;
1152 end
1153  
1154 local function _HA_CheckForBonusScan()
1155 if(not _HA_BonusScanScheduled)
1156 then
1157 _HA_BonusScanScheduled = true;
1158 HASystem_Schedule(2,_HA_ScanScheduleRoutine); -- Schedule scan in 2 sec
1159 end
1160 end
1161  
1162 local function _HA_RegisterEvents()
1163 HealersAssistMainFrame:RegisterEvent("UNIT_INVENTORY_CHANGED");
1164 end
1165  
1166 local function _HA_UnregisterEvents()
1167 HealersAssistMainFrame:UnregisterEvent("UNIT_INVENTORY_CHANGED");
1168 end
1169  
1170 local function _HA_CheckLifeUpdates(raider,new_hp)
1171 if(HA_Config.UseEstimatedHealth)
1172 then
1173 local estim = raider.hp_real;
1174 local updates = raider.life_updates;
1175  
1176 --[[if(updates[1] == nil and new_hp == estim)
1177 then
1178 HA_ChatDebug(HA_DEBUG_GLOBAL,"UNIT_HEALTH Diff OK : NewHP="..new_hp.." - Got no updates");
1179 end]]
1180  
1181 while(updates[1])
1182 do
1183 estim = estim + updates[1].value;
1184 table.remove(updates,1);
1185 if(estim == new_hp)
1186 then
1187 --HA_ChatDebug(HA_DEBUG_GLOBAL,"UNIT_HEALTH Diff Matching ("..table.getn(updates).." remaning updates) : NewHP="..new_hp);
1188 break;
1189 end
1190 end
1191 if(estim > raider.hpmax) then estim = raider.hpmax; end
1192 if(new_hp ~= estim)
1193 then
1194 --HA_ChatDebug(HA_DEBUG_GLOBAL,"UNIT_HEALTH Diff Error : NewHP="..new_hp.." Ignoring next Wound Event");
1195 raider.ignore_next_wound = HA_CurrentTime;
1196 end
1197 end
1198 raider.hp_estim = new_hp;
1199 end
1200  
1201 -- Currently don't handle "STOP because of failure" for casted request-spell
1202 local function _HA_CheckSpellRequestSuccess(spell,instant)
1203 if(HA_SpellRequest and HA_SpellRequest.spell == spell)
1204 then
1205 HA_ChatDebug(HA_DEBUG_ACTIONS,"_HA_CheckSpellRequestSuccess : SpellRequest successfully casted (or stopped for casted ones) : "..tostring(spell).." : "..tostring(instant));
1206 HA_SpellRequest = nil;
1207 StaticPopup_Hide("HA_REQUEST_FOR_SPELL");
1208 end
1209 end
1210  
1211 local function _HA_CheckSpellRequestFailed(spell,reason)
1212 if(HA_SpellRequest and HA_SpellRequest.spell == spell)
1213 then
1214 HA_ChatDebug(HA_DEBUG_ACTIONS,"_HA_CheckSpellRequestFailed : SpellRequest failed for "..tostring(spell).." : "..tostring(reason));
1215 HA_SpellRequest.failure = reason;
1216 end
1217 end
1218  
1219 local function _HA_ProcessEvent_SpellCastStart()
1220 _HA_CheckRegenModeFromCast();
1221 if(HA_Spells[arg1])
1222 then
1223 _HA_DoStop(false,"START");
1224 HA_CastingSpell = arg1;
1225 HA_StopCommandSent = false; -- Set *waiting for stop* state
1226 HA_StopCommandLastTime = HA_CurrentTime; -- And reset time
1227 if(HA_SpellTargetName == nil)
1228 then
1229 HA_SpellTargetName = HA_PotentialSpellTargetName;
1230 HA_ChatDebug(HA_DEBUG_ACTIONS,"SPELLCAST_START : Target="..tostring(HA_SpellTargetName));
1231 end
1232 local casttime = tonumber(arg2,10)
1233 local target = HA_SpellTargetName;
1234 local ispell = _HA_GetISpell(HA_CastingSpell);
1235 local ihook = _HA_GetISpell(HA_HookActionName);
1236 local estimated,willcrit = _HA_GetEstimated(ispell,ihook,HA_HookActionRank,target,casttime);
1237 HA_GUI_Process_SpellStart(HA_PlayerName,ispell,target,casttime,estimated,willcrit,HA_HookActionRank);
1238 HA_COM_SpellStart(ispell,target,casttime,estimated,willcrit,HA_HookActionRank);
1239 -- Special HA_SPELL_REGROWTH case
1240 if(ispell == HA_SPELL_REGROWTH)
1241 then
1242 HA_LastRegrowthRank = HA_HookActionRank;
1243 end
1244 end
1245 HA_SpellTargetName = nil;
1246 end
1247  
1248 local function _HA_ProcessEvent_SpellCastStop()
1249 _HA_CheckRegenModeFromCast();
1250 if(HA_CastingInstantSpell) -- An instant spell ?
1251 then
1252 if(HA_SpellTargetName == nil)
1253 then
1254 HA_SpellTargetName = HA_PotentialSpellTargetName;
1255 HA_ChatDebug(HA_DEBUG_ACTIONS,"SPELLCAST_STOP (INSTANT SPELL) : Target="..tostring(HA_SpellTargetName));
1256 end
1257 local target = HA_SpellTargetName;
1258 local ispell = _HA_GetISpell(HA_CastingInstantSpell);
1259 if(HA_InstantSpells[HA_CastingInstantSpell].cooldown) -- A cooldown instant spell
1260 then
1261 HA_GUI_Process_SpellCooldown(HA_PlayerName,ispell,target);
1262 HA_COM_SpellCooldown(ispell,target);
1263 elseif(HA_InstantSpells[HA_CastingInstantSpell].nonheal) -- A non-heal spell
1264 then
1265 HA_GUI_Process_SpellInstant(HA_PlayerName,ispell,target,HA_CastingInstantRank);
1266 HA_COM_SpellInstant(ispell,target,HA_CastingInstantRank);
1267 else
1268 local estimated = _HA_GetEstimated(ispell,ispell,HA_CastingInstantRank,target,0);
1269 local duration = _HA_GetDuration(ispell);
1270 if(duration ~= 0)
1271 then
1272 HA_GUI_Process_SpellOvertime(HA_PlayerName,ispell,target,duration,estimated,HA_CastingInstantRank);
1273 HA_COM_SpellOvertime(ispell,target,duration,estimated,HA_CastingInstantRank);
1274 end
1275 end
1276 _HA_CheckSpellRequestSuccess(HA_CastingInstantSpell,true);
1277 HA_CastingInstantSpell = nil;
1278 elseif(HA_CastingSpell)
1279 then
1280 HA_ChatDebug(HA_DEBUG_ACTIONS,"SPELLCAST_STOP : Spell="..tostring(HA_CastingSpell));
1281 _HA_CheckSpellRequestSuccess(HA_CastingSpell,false);
1282 _HA_DoStop(false,"STOP");
1283 end
1284 HA_SpellTargetName = nil;
1285 end
1286  
1287  
1288 --------------- Shared functions ---------------
1289  
1290 function HA_GetSpellIDFromName(spellName,spellRank)
1291 local i = 1;
1292 local name,rank = GetSpellName(i,BOOKTYPE_SPELL);
1293 local searched;
1294  
1295 if(spellRank) -- With rank
1296 then
1297 if(type(spellRank) == "number") -- Number
1298 then
1299 searched = spellName.."("..HA_RANK.." "..tostring(spellRank)..")";
1300 else -- String
1301 searched = spellName.."("..spellRank..")";
1302 end
1303 else -- No rank
1304 searched = spellName;
1305 end
1306  
1307 while(name)
1308 do
1309 local current;
1310 if(rank ~= "") -- With rank
1311 then
1312 current = name.."("..rank..")";
1313 else -- No rank
1314 current = name;
1315 end
1316 if(searched == current) -- Found
1317 then
1318 return i;
1319 end
1320 i = i + 1;
1321 name,rank = GetSpellName(i,BOOKTYPE_SPELL);
1322 end
1323 return 0;
1324 end
1325  
1326 local function HA_CheckForEmergencyListUpdate()
1327 -- Update Emergency every 30 msec
1328 if(HA_CurrentTime > (_HA_LastTimeEmergency+0.030))
1329 then
1330 HA_UpdateListEmergency();
1331 _HA_LastTimeEmergency = HA_CurrentTime;
1332 end
1333 end
1334  
1335 --------------- XML functions ---------------
1336  
1337 function HA_OnEvent()
1338 if(event == "VARIABLES_LOADED")
1339 then
1340 if(HA_NeedInit)
1341 then
1342 HA_StartupInitVars();
1343 end
1344 return;
1345 elseif(event == "PLAYER_LOGIN")
1346 then
1347 HA_MaxRanks = _HA_GetMaxSpellRanks();
1348 elseif(event == "PLAYER_ENTERING_WORLD")
1349 then
1350 _HA_AnalyseGroupRaidMembers(); -- Force group analyse now
1351 HA_CurrentZone = GetRealZoneText(); -- Get it here too, because when you /reloadui the ZONE_CHANGED is not called - Thanks blizzard
1352 _HA_RegisterEvents();
1353 _HA_CheckForBonusScan();
1354 HA_CurrentTarget = nil
1355 if(HA_Config.Auto and HA_CurrentGroupMode ~= HA_MODE_SOLO)
1356 then
1357 HealersAssistMainFrame:Show();
1358 end
1359 elseif(event == "PLAYER_LEAVING_WORLD")
1360 then
1361 _HA_UnregisterEvents();
1362 elseif(event == "ZONE_CHANGED_NEW_AREA")
1363 then
1364 HA_CurrentZone = GetRealZoneText();
1365 end
1366  
1367 if(event == "UNIT_COMBAT")
1368 then
1369 if(HA_Config.UseEstimatedHealth)
1370 then
1371 local raider = HA_RaidersByID[arg1];
1372 if(raider and arg1 ~= "target")
1373 then
1374 if(arg2 == "WOUND")
1375 then
1376 if(HA_CurrentTime > (raider.ignore_next_wound+0.5))
1377 then
1378 --HA_ChatDebug(HA_DEBUG_GLOBAL,"UNIT_COMBAT '"..tostring(arg1).."' ("..tostring(raider.name)..") : DEGATS : "..tostring(arg4).." : UnitHealth="..UnitHealth(arg1).." hp="..raider.hp_real);
1379 raider.hp_estim = raider.hp_estim - arg4;
1380 if(raider.hp_estim < 0) then raider.hp_estim = 0; end
1381 tinsert(raider.life_updates,{ value=-arg4; stamp=HA_CurrentTime });
1382 --[[else
1383 HA_ChatDebug(HA_DEBUG_GLOBAL,"UNIT_COMBAT '"..tostring(arg1).."' ("..tostring(raider.name)..") : DEGATS : "..tostring(arg4).." : UnitHealth="..UnitHealth(arg1).." hp="..raider.hp_real.." - IGNORED");]]
1384 end
1385 raider.ignore_next_wound = 0;
1386 elseif(arg2 == "HEAL")
1387 then
1388 --HA_ChatDebug(HA_DEBUG_GLOBAL,"UNIT_COMBAT '"..tostring(arg1).."' ("..tostring(raider.name)..") : SOINS : "..tostring(arg4));
1389 tinsert(raider.heal_updates,{ value=arg4, oldhp=raider.hp_estim }); -- Store health before applying heal
1390 raider.hp_estim = raider.hp_estim + arg4;
1391 if(raider.hp_estim > raider.hpmax) then raider.hp_estim = raider.hpmax; end
1392 tinsert(raider.life_updates,{ value=arg4; stamp=HA_CurrentTime });
1393 if(getn(raider.heal_updates) > HA_MAX_HEAL_UPDATES) -- Remove a heal update, if too many in the array
1394 then
1395 table.remove(raider.heal_updates,1);
1396 end
1397 else
1398 --HA_ChatDebug(HA_DEBUG_GLOBAL,"UNIT_COMBAT '"..tostring(arg1).."' ("..tostring(raider.name)..") : "..tostring(arg2).." : "..tostring(arg4).." : "..tostring(arg3));
1399 end
1400 raider.hp = raider.hp_estim; -- Update HP value
1401 end
1402 end
1403 return;
1404  
1405 -- Health/Mana events
1406 elseif(event == "UNIT_HEALTH")
1407 then
1408 local raider = HA_RaidersByID[arg1];
1409 if(raider)
1410 then
1411 local new_hp = UnitHealth(arg1);
1412 if(arg1 ~= "target")
1413 then
1414 _HA_CheckLifeUpdates(raider,new_hp); -- Parse "updates" array, and update hp_estim to 'new_hp' value
1415 end
1416 raider.hp_real = new_hp;
1417 raider.hp = new_hp;
1418 raider.percent = floor(raider.hp / raider.hpmax * 100);
1419 HA_CheckForEmergencyListUpdate();
1420 end
1421 return;
1422 elseif(event == "UNIT_MAXHEALTH")
1423 then
1424 local raider = HA_RaidersByID[arg1];
1425 if(raider)
1426 then
1427 raider.hpmax = UnitHealthMax(arg1);
1428 raider.percent = floor(raider.hp / raider.hpmax * 100);
1429 HA_CheckForEmergencyListUpdate();
1430 end
1431 return;
1432 elseif(event == "UNIT_MANA")
1433 then
1434 local raider = HA_RaidersByID[arg1];
1435 if(raider and raider.ishealer)
1436 then
1437 raider.mp = UnitMana(arg1);
1438 raider.mppercent = floor(raider.mp / raider.mpmax * 100);
1439 end
1440 return;
1441 elseif(event == "UNIT_MAXMANA")
1442 then
1443 local raider = HA_RaidersByID[arg1];
1444 if(raider and raider.ishealer)
1445 then
1446 raider.mpmax = UnitManaMax(arg1);
1447 raider.mppercent = floor(raider.mp / raider.mpmax * 100);
1448 end
1449 return;
1450 elseif(event == "PLAYER_AURAS_CHANGED")
1451 then
1452 if(HA_MyselfHealer and not HA_RaiderInfused(HA_PlayerName) and HA_PlayerHasBuf(HA_POWER_INFUSION_TEXTURE))
1453 then
1454 HA_GUI_Process_GotPowerInfusion(HA_PlayerName);
1455 HA_COM_GotPowerInfusion();
1456 end
1457 return;
1458 end
1459  
1460 -- Group events
1461 if(event == "PARTY_MEMBERS_CHANGED" or event == "RAID_ROSTER_UPDATE")
1462 then
1463 HA_ChatDebug(HA_DEBUG_RAIDERS,event.." : Party members changed. Analysing Raiders...");
1464 debugprofilestart();
1465 _HA_AnalyseGroupRaidMembers();
1466 HA_PROFILE_AnalyseRaidersRoutine = debugprofilestop();
1467 return;
1468 end
1469  
1470 -- Inventory events
1471 if(HA_NeedInit == false and event == "UNIT_INVENTORY_CHANGED")
1472 then
1473 _HA_CheckForBonusScan();
1474 return;
1475 end
1476  
1477 -- Mouse over event - Get target
1478 if(event == "UPDATE_MOUSEOVER_UNIT")
1479 then
1480 if(SpellIsTargeting() and UnitIsFriend("player", "mouseover"))
1481 then
1482 HA_PotentialSpellTargetName = UnitName("mouseover");
1483 HA_ChatDebug(HA_DEBUG_ACTIONS,"MouseOverUpdate : PotentialTarget="..tostring(HA_PotentialSpellTargetName));
1484 end
1485 return;
1486 end
1487  
1488 -- Target changed
1489 if(event == "PLAYER_TARGET_CHANGED" )
1490 then
1491 if(UnitExists("target") and UnitIsFriend("player","target")) -- Add target if don't exist
1492 then
1493 local name = UnitName("target");
1494 local raider = HA_Raiders[name];
1495 if(raider == nil) -- Don't exist, add it as subgrp 0
1496 then
1497 raider = _HA_CreateRaider(name);
1498 HA_Raiders[name] = raider;
1499 _HA_SetFullRaiderVariables(raider,"target",0,0);
1500 HA_UpdateListEmergency();
1501 -- Call plugins
1502 for n,pl in HA_ActivePlugins
1503 do
1504 if(pl.OnEvent)
1505 then
1506 pl.OnEvent(HA_EVENT_RAIDER_JOINED,{name});
1507 end
1508 end
1509 end
1510 HA_RaidersByID["target"] = raider;
1511 HA_CurrentTarget = name;
1512 else -- No valid target
1513 HA_RaidersByID["target"] = nil;
1514 HA_CurrentTarget = nil
1515 end
1516 HA_PotentialSpellTargetName = HA_CurrentTarget;
1517 return;
1518 end
1519  
1520 -- System events
1521 if(event == "CHAT_MSG_SYSTEM")
1522 then
1523 if(arg1 == MARKED_AFK or string.find(arg1,string.format(MARKED_AFK_MESSAGE,".*")))
1524 then
1525 HA_AFK_Mode = true;
1526 end
1527 if(arg1 == CLEARED_AFK)
1528 then
1529 HA_AFK_Mode = false;
1530 end
1531 elseif(event == "CHAT_MSG_ADDON" and (arg3 == "RAID" or arg3 == "PARTY") and arg1 == HA_ADDON_PREFIX)
1532 then
1533 HA_COM_ParseMessage(arg4,arg2);
1534  
1535 -- Spell Events
1536 elseif(event == "SPELLCAST_START")
1537 then
1538 _HA_ProcessEvent_SpellCastStart();
1539 elseif(event == "SPELLCAST_STOP")
1540 then
1541 _HA_ProcessEvent_SpellCastStop();
1542 elseif(event == "SPELLCAST_DELAYED")
1543 then
1544 if(HA_CastingSpell)
1545 then
1546 HA_GUI_Process_SpellDelayed(HA_PlayerName,arg1);
1547 HA_COM_SpellDelayed(arg1);
1548 end
1549 elseif(event == "CHAT_MSG_SPELL_FAILED_LOCALPLAYER")
1550 then
1551 local _,_,spell,reason = string.find(arg1,HA_PARSE_SPELL_FAILED_REASON);
1552 if(reason == nil) then reason = "???"; end;
1553 HA_ChatDebug(HA_DEBUG_ACTIONS,"SPELLCAST_FAILED : ParsedSpell="..tostring(spell).." Casting="..tostring(HA_CastingSpell).." Instant="..tostring(HA_CastingInstantSpell).." Reason="..reason);
1554 if(reason ~= SPELL_FAILED_SPELL_IN_PROGRESS and HA_CastingSpell and HA_CastingSpell == spell)
1555 then
1556 local ispell = _HA_GetISpell(HA_CastingSpell);
1557 local ireason,sreason = _HA_GetReasonCode(reason);
1558 _HA_DoStop(true,"FAILED");
1559 if(HA_StopCommandSent)
1560 then
1561 HA_GUI_Process_SpellFailed(HA_PlayerName,ispell,ireason,sreason);
1562 HA_COM_SpellFailed(ispell,ireason,sreason);
1563 end
1564 end
1565 _HA_CheckSpellRequestFailed(spell,reason);
1566 HA_CastingInstantSpell = nil;
1567 else
1568 HA_findEventMatch(event,_HA_RegExpr_CallBack); -- Check remaing events
1569 end
1570 end
1571  
1572 --------------- Exported functions ---------------
1573  
1574 --[[
1575 Function HA_QuerySpell
1576 - SpellName : String - Spell to request.
1577 - PlayerName : String - Player to request spell to.
1578 Sends a request for spell to this player.
1579 Returns true is spell was found, ready, and queried. False otherwise.
1580 ]]
1581 function HA_QuerySpell(SpellName,PlayerName)
1582 local healer = HA_Healers[PlayerName];
1583 if(healer)
1584 then
1585 local spell = healer.Cooldown[SpellName];
1586 if(spell and spell.Remain == 0)
1587 then
1588 HA_COM_SpellRequest(spell.ispell,PlayerName);
1589 return true;
1590 else
1591 if(spell == nil)
1592 then
1593 HA_ChatPrint("HA_QuerySpell Failed : "..SpellName.." is not in "..PlayerName.."'s list of spells");
1594 else
1595 HA_ChatPrint("HA_QuerySpell Failed : "..SpellName.." is not ready ("..spell.Remain.." sec remaining)");
1596 end
1597 end
1598 else
1599 HA_ChatPrint("HA_QuerySpell Failed : "..PlayerName.." is not in my group/raid");
1600 end
1601 return false;
1602 end
1603  
1604 --[[
1605 Function HA_ShowVersions
1606 Prints HA's version for all Healers in your group/raid (if available).
1607 ]]
1608 function HA_ShowVersions()
1609 for name,raider in HA_Raiders
1610 do
1611 if(raider.Version)
1612 then
1613 HA_ChatPrint(name.." : "..raider.Version);
1614 end
1615 end
1616 end
1617  
1618  
1619 --------------- Init functions ---------------
1620  
1621 local function _HA_CheckSpellCooldown(spell,ispell,rank)
1622 local id = HA_GetSpellIDFromName(spell,rank);
1623 if(id == 0 and type(rank) == "number")
1624 then
1625 while(id == 0)
1626 do
1627 rank = rank - 1;
1628 if(rank == 0) -- Really don't have this spell
1629 then
1630 break;
1631 end
1632 id = HA_GetSpellIDFromName(spell,rank);
1633 end
1634 end
1635 if(id ~= 0)
1636 then
1637 HA_SpellCooldowns[spell] = { ispell=ispell; id=id; last=0; lastSend=0 };
1638 HA_ChatDebug(HA_DEBUG_SPELLS,"_HA_CheckSpellCooldown : Found spell "..spell.." rank "..tostring(rank).." as ID "..id);
1639 return true;
1640 end
1641 return false;
1642 end
1643  
1644 local function _HA_CheckTalents(talentids)
1645 local numTabs = GetNumTalentTabs();
1646 for t=1, numTabs
1647 do
1648 local numTalents = GetNumTalents(t);
1649 for i=1, numTalents
1650 do
1651 local name,icon,x,y,rank,max = GetTalentInfo(t,i);
1652 for _,tid in talentids
1653 do
1654 if(HA_Talents[tid] and HA_Talents[tid].texture == icon)
1655 then
1656 HA_Talents[tid].rank = rank;
1657 HA_ChatDebug(HA_DEBUG_SPELLS,"_HA_CheckTalents : Found talent "..name.." at rank "..rank.."/"..max);
1658 end
1659 end
1660 end
1661 end
1662 end
1663  
1664 function HA_InitializeCooldownSpells()
1665 -- Reset infos
1666 HA_SpellCooldowns = {};
1667 for id,tab in HA_Talents
1668 do
1669 tab.rank = nil;
1670 end
1671  
1672 -- Fill infos
1673 local _,clas = UnitClass("player");
1674 if(clas == "DRUID")
1675 then
1676 _HA_CheckSpellCooldown(HA_INNERVATE,HA_SPELL_INNERVATE);
1677 _HA_CheckSpellCooldown(HA_REBIRTH,HA_SPELL_REBIRTH,5);
1678 _HA_CheckTalents({HA_TALENT_GIF_OF_NATURE,HA_TALENT_IMPROVED_REJUVINATION});
1679 elseif(clas == "PALADIN")
1680 then
1681 _HA_CheckSpellCooldown(HA_DIVINE_INTERVENTION,HA_SPELL_DIVINE_INTERVENTION);
1682 _HA_CheckSpellCooldown(HA_BLESSING_OF_PROTECTION,HA_SPELL_BLESSING_OF_PROTECTION,3);
1683 _HA_CheckTalents({HA_TALENT_HEALING_LIGHT});
1684 elseif(clas == "SHAMAN")
1685 then
1686 _HA_CheckSpellCooldown(HA_REINCARNATION,HA_SPELL_REINCARNATION,HA_PASSIVE);
1687 _HA_CheckSpellCooldown(HA_MANA_TIDE,HA_SPELL_MANA_TIDE,2);
1688 _HA_CheckTalents({HA_TALENT_PURIFICATION});
1689 elseif(clas == "PRIEST")
1690 then
1691 _HA_CheckSpellCooldown(HA_LIGHTWELL,HA_SPELL_LIGHTWELL,3);
1692 _HA_CheckSpellCooldown(HA_POWER_INFUSION,HA_SPELL_POWER_INFUSION);
1693 _HA_CheckTalents({HA_TALENT_SPIRITUAL_HEALING,HA_TALENT_IMPROVED_RENEW,HA_TALENT_SPIRITUAL_GUIDANCE});
1694 end
1695 end
1696  
1697 function HA_OnLoad()
1698 -- Print init message
1699 HA_ChatPrint("Version "..HA_VERSION.." "..HA_CHAT_MISC_LOADED);
1700  
1701 -- Register events
1702 this:RegisterEvent("VARIABLES_LOADED");
1703 this:RegisterEvent("PLAYER_LOGIN");
1704 this:RegisterEvent("PLAYER_ENTERING_WORLD");
1705 this:RegisterEvent("PLAYER_LEAVING_WORLD");
1706 this:RegisterEvent("ZONE_CHANGED_NEW_AREA");
1707  
1708 this:RegisterEvent("UNIT_COMBAT");
1709 this:RegisterEvent("UNIT_HEALTH");
1710 this:RegisterEvent("UNIT_MAXHEALTH");
1711 this:RegisterEvent("UNIT_MANA");
1712 this:RegisterEvent("UNIT_MAXMANA");
1713 this:RegisterEvent("PLAYER_AURAS_CHANGED");
1714 this:RegisterEvent("UPDATE_MOUSEOVER_UNIT");
1715 this:RegisterEvent("PLAYER_TARGET_CHANGED");
1716  
1717 this:RegisterEvent("CHAT_MSG_SYSTEM");
1718 this:RegisterEvent("CHAT_MSG_ADDON");
1719  
1720 this:RegisterEvent("SPELLCAST_START");
1721 this:RegisterEvent("SPELLCAST_STOP");
1722 this:RegisterEvent("SPELLCAST_DELAYED");
1723 this:RegisterEvent("CHAT_MSG_SPELL_SELF_BUFF");
1724 this:RegisterEvent("CHAT_MSG_SPELL_PARTY_BUFF");
1725 this:RegisterEvent("CHAT_MSG_SPELL_FRIENDLYPLAYER_BUFF");
1726 this:RegisterEvent("CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF");
1727 this:RegisterEvent("CHAT_MSG_SPELL_FAILED_LOCALPLAYER");
1728 this:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"); -- Self hots
1729 this:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS"); -- Group hots
1730 this:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS"); -- Raid hots
1731  
1732 HealersAssistTitle:SetText("HealersAssist v"..HA_VERSION);
1733 -- Initialize Slash commands
1734 SLASH_HA1 = "/ha";
1735 SlashCmdList["HA"] = function(msg)
1736 HA_Commands(msg);
1737 end
1738  
1739 tinsert(UISpecialFrames, "HAConfFrame");
1740  
1741 -- Initialiaze RegExprs
1742 HA_CreateRegexFromGlobals();
1743  
1744 -- Build ISpell structure
1745 HA_BuildLocalNames();
1746  
1747 -- Check for GroupAnalyse AddOn
1748 if(GA_RegisterForEvents ~= nil)
1749 then
1750 GA_RegisterForEvents(_HA_GroupAnalyseCallback);
1751 else
1752 this:RegisterEvent("PARTY_MEMBERS_CHANGED");
1753 this:RegisterEvent("RAID_ROSTER_UPDATE");
1754 end
1755 -- Check for BonusScanner AddOn
1756 if(BonusScanner and BONUSSCANNER_VERSION and BONUSSCANNER_VERSION >= "v1.0")
1757 then
1758 _HA_UseBonusScannerAddon = true;
1759 end
1760 end
1761  
1762  
1763 --/script HA_RequestSpell("Blessing of Protection")
1764 --/script SendAddonMessage("HealAss","<HA7>09"..'\30'.."16"..'\30'.."Kiki","RAID")