vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 SA_LIST_BOXES = {};
2  
3 -- TODO: incombat interval ja outofcombat interval! performance++ =)
4  
5 SA_REFRESHRATE = 1.5;
6  
7 SA_BASERATE = 0.6;
8 SA_INCREASE = 0.1; -- how much to decrease refresh rate per mob
9 SA_LASTREFRESH = SA_BASERATE;
10  
11 SA_SEMIALERT = "|cffffa303";
12 SA_ALERTCOL = "|cffff0000";
13 SA_TANKCOL = "|cff00ff00";
14  
15 -- title modes
16  
17 MODE_OOC = 1;
18 MODE_NORMAL = 2;
19 MODE_FILTERED = 3;
20 MODE_PAUSED = 4;
21 MODE_NEAREST = 5;
22 MODE_OVERLOADED = 10;
23  
24 -- contains all data from last update
25  
26 SA_PREV = {
27 ["aggro_count"] = 0,
28 ["mob_count"] = 0,
29 ["wb_count"] = 0,
30 ["title_mode"] = -1,
31 ["dirty"] = false
32 };
33  
34 -- class icon texture coordinates
35  
36 SA_TEXTCOORDS={};
37 SA_TEXTCOORDS["PRIEST"] = { left=0.50, right=0.75, top=0.25, bottom=0.50 };
38 SA_TEXTCOORDS["MAGE"] = { left=0.25, right=0.50, top=0.00, bottom=0.25 };
39 SA_TEXTCOORDS["WARLOCK"] = { left=0.75, right=1.00, top=0.25, bottom=0.50 };
40 SA_TEXTCOORDS["DRUID"] = { left=0.75, right=1.00, top=0.00, bottom=0.25 };
41 SA_TEXTCOORDS["HUNTER"] = { left=0.00, right=0.25, top=0.25, bottom=0.50 };
42 SA_TEXTCOORDS["ROGUE"] = { left=0.50, right=0.75, top=0.00, bottom=0.25 };
43 SA_TEXTCOORDS["WARRIOR"] = { left=0.00, right=0.25, top=0.00, bottom=0.25 };
44 SA_TEXTCOORDS["SHAMAN"] = { left=0.25, right=0.50, top=0.25, bottom=0.50 };
45 SA_TEXTCOORDS["PALADIN"] = { left=0.00, right=0.25, top=0.50, bottom=0.75 };
46  
47 -- predefined icon locations
48  
49 SA_ICONPOS={n=8};
50 SA_ICONPOS[1] = { name="Disabled", point="TOPLEFT", relativePoint="", x=-26, y=-28, width=25, height=25 };
51 SA_ICONPOS[2] = { name="Left bottom", point="TOPLEFT", relativePoint="", x=-26, y=-28, width=25, height=25 };
52 SA_ICONPOS[3] = { name="Left top", point="TOPLEFT", relativePoint="", x=-26, y=-3, width=25, height=25};
53 SA_ICONPOS[4] = { name="Inside box", point="BOTTOMRIGHT", relativePoint="", x=0, y=0, width=25, height=25};
54 SA_ICONPOS[5] = { name="Right bottom", point="TOPRIGHT", relativePoint="", x=26, y=-28, width=25, height=25 };
55 SA_ICONPOS[6] = { name="Right top", point="TOPRIGHT", relativePoint="", x=26, y=-3, width=25, height=25};
56 SA_ICONPOS[7] = { name="Left big", point="TOPLEFT", relativePoint="", x=-51, y=-3, width=50, height=50};
57 SA_ICONPOS[8] = { name="Right big", point="TOPRIGHT", relativePoint="", x=51, y=-3, width=50, height=50};
58  
59  
60 ------------------------------------------------------------------------------
61  
62 function SA_InitBar(bar, text)
63 bar:SetMinMaxValues(0, 100);
64 bar:SetValue(100);
65 text:SetHeight(12);
66 text:SetPoint("CENTER", bar:GetName(), "CENTER", 0, 0);
67 SetTextStatusBarText(bar, text);
68 ShowTextStatusBarText(bar);
69 end
70  
71 function SA_List_OnLoad()
72 -- initialize boxes
73 for i=1, 10 do
74 local box = {};
75 box["frame"] = getglobal("Target"..i);
76 box["mobText"] = getglobal("Target"..i.."MobText");
77 box["targetText"] = getglobal("Target"..i.."TargetText");
78 box["targetOf"] = getglobal("Target"..i.."TargetOf");
79 box["mobBar"] = getglobal("Target"..i.."MobBar");
80 box["targetBar"] = getglobal("Target"..i.."TargetBar");
81 box["classIcon"] = getglobal("Target"..i.."ClassIcon");
82 box["targetIcon"] = getglobal("Target"..i.."TargetIcon");
83 box["huntersMarkIcon"] = getglobal("Target"..i.."HuntersMarkIcon");
84 SA_LIST_BOXES[i] = box;
85 end
86 end
87  
88 function SA_List_OnShow()
89 -- TODO: this should be done only if options altered from xml defaults
90 SA_List_UpdateAppearance();
91  
92 if (SA_OPTIONS.ListScale ~= 1.0) then
93 SAListFrameScaler:SetScale(SA_OPTIONS.ListScale);
94 end
95  
96 -- set title button to correct mode
97 SA_List_SetTitleButton(MODE_OOC);
98 end
99  
100 ------------------------------------------------------------------------------
101 -- initialize list dropdown menu
102 ------------------------------------------------------------------------------
103  
104 function SA_List_FrameDropDown_OnLoad()
105 UIDropDownMenu_Initialize(this, SA_List_FrameDropDown_Initialize, "MENU");
106 end
107  
108 ------------------------------------------------------------------------------
109 -- note: this is called always when dropdown is shown!
110 ------------------------------------------------------------------------------
111  
112 function SA_List_FrameDropDown_Initialize()
113 if (not SA_OPTIONS) then
114 -- options not available yet (onload event) -> abort
115 return;
116 end
117  
118 item = {};
119 item.text = "Filter targets";
120 if (SA_OPTIONS.Filter) then
121 item.checked = 1;
122 end
123 item.func = SA_List_Menu_Filter;
124 UIDropDownMenu_AddButton(item);
125  
126 item = {};
127 item.text = "Assist with pet";
128 if (SA_OPTIONS.AutoPetAttack) then
129 item.checked = 1;
130 end
131 item.keepShownOnClick = 1;
132 item.func = SA_List_Menu_AssistWithPet;
133 UIDropDownMenu_AddButton(item);
134  
135 item = {};
136 item.text = "Assist only players nearby (28 yards)";
137 if (SA_OPTIONS.AssistOnlyNearest) then
138 item.checked = 1;
139 end
140 item.func = SA_List_Menu_AssistOnlyNearest;
141 UIDropDownMenu_AddButton(item);
142  
143 item = {};
144 item.text = "Display out of combat targets";
145 item.checked = SA_OPTIONS.OutOfCombat;
146 item.func = SA_List_Menu_ShowOOC;
147 UIDropDownMenu_AddButton(item);
148  
149 item = {};
150 item.text = "Lock list position";
151 item.func = SA_List_Menu_Lock;
152 item.checked = SA_OPTIONS.LockList;
153 UIDropDownMenu_AddButton(item);
154  
155 item = {};
156 item.text = "SmartAssist options";
157 item.notCheckable = 1;
158 item.func = SA_ShowOptions;
159 UIDropDownMenu_AddButton(item);
160 end
161  
162 function SA_List_Menu_Lock()
163 SA_ToggleOption("LockList");
164 end
165  
166 function SA_List_Menu_AssistWithPet()
167 SA_ToggleOption("AutoPetAttack");
168 end
169  
170 function SA_List_Menu_AssistOnlyNearest()
171 SA_ToggleOption("AssistOnlyNearest");
172 if (SA_OPTIONS.AssistOnlyNearest) then
173 printInfo("Assisting only players nearby");
174 end
175 end
176  
177 function SA_List_Menu_ShowOOC()
178 SA_ToggleOption("OutOfCombat");
179 end
180  
181 function SA_List_Menu_Filter()
182 if (SA_OPTIONS.Filter == nil) then
183 SAFilterFrame:Show();
184 else
185 SA_OPTIONS.Filter = nil;
186 printInfo("Filtering disabled");
187 SA_List_SetTitleButton(MODE_OOC);
188 end;
189 end
190  
191 function SA_List_FilterButtonOK_OnClick()
192 local text = SAFilterEditBox:GetText();
193 if (string.len(text) > 0) then
194 SA_OPTIONS.Filter = string.lower(text);
195 printInfo("SmartAssist will now ignore targets which name doesn't contain text '"..text.."' until you disable filtering. Note that filtering applies only assisting, it doesn't include fallback to nearest.");
196 SA_List_SetTitleButton(MODE_FILTERED);
197 else
198 SA_OPTIONS.Filter = nil;
199 printInfo("Filtering disabled");
200 SA_List_SetTitleButton(MODE_OOC);
201 end
202 SAFilterFrame:Hide();
203 end
204  
205 ------------------------------------------------------------------------------
206 -- Title button clicked (show menu) or drag
207 -- kudos goes for DamageMeters
208 ------------------------------------------------------------------------------
209  
210 function SA_List_TitleButton_OnClick()
211 local button = arg1;
212 if ( button == "LeftButton" and not SA_OPTIONS.LockList) then
213 -- drag frame
214 if ( this:GetButtonState() == "PUSHED" ) then
215 SAListFrame:StopMovingOrSizing();
216 else
217 SAListFrame:StartMoving();
218 end
219 elseif ( button == "RightButton" ) then
220 -- show menu
221 ToggleDropDownMenu(1, nil, SAListFrameDropDown, "SAListFrameDropDown", -33, 25);
222 end
223 end
224  
225 ------------------------------------------------------------------------------
226 -- change outlook for the list depending options
227 ------------------------------------------------------------------------------
228  
229 function SA_List_HasSideIcons()
230 if (SA_OPTIONS.ClassIconMode==2 or SA_OPTIONS.ClassIconMode==3 or
231 SA_OPTIONS.TargetIconMode==2 or SA_OPTIONS.TargetIconMode==3 or
232 SA_OPTIONS.HuntersMarkIconMode==2 or SA_OPTIONS.HuntersMarkIconMode==3)
233 then
234 return true;
235 end
236 end
237  
238 function SA_List_UpdateAppearance()
239 local width = SA_OPTIONS.ListWidth;
240  
241 local x = 0;
242 local y = 0;
243 if (SA_OPTIONS.ListHorizontal) then
244 -- horizontal
245 local gap = 10;
246 if (SA_List_HasSideIcons()) then
247 gap = gap - 30;
248 end
249 if (SA_OPTIONS.ListSpacing > 0) then
250 x = width - gap + SA_OPTIONS.ListSpacing;
251 else
252 x = - (width - gap - SA_OPTIONS.ListSpacing);
253 end
254 else
255 -- vertical
256 local height = ceil(Target1:GetHeight()) - 8;
257 if (SA_OPTIONS.ListSpacing > 0) then
258 y = height + SA_OPTIONS.ListSpacing;
259 else
260 y = - (height-SA_OPTIONS.ListSpacing);
261 end
262 end
263  
264 SAListFrame:SetWidth(width);
265 SAListTitleButton:SetWidth(width);
266  
267 local anchor="TOPLEFT";
268 for i,box in SA_LIST_BOXES do
269 if (i==1) then
270 -- first box is ancored to title
271 if (y<=0) then
272 box.frame:SetPoint(anchor, "SAListFrame", anchor, 0, -17);
273 else
274 box.frame:SetPoint(anchor, "SAListFrame", anchor, 0, 54);
275 end
276 elseif (i==6 and SA_OPTIONS.ListTwoRow) then
277 -- two rows
278 if (SA_OPTIONS.ListHorizontal) then
279 local gap = SA_OPTIONS.ListSpacing;
280 if (gap>0) then
281 gap = -gap;
282 end
283 gap = gap + 10;
284 box.frame:SetPoint(anchor, "Target1", "BOTTOMLEFT", 0, gap);
285 else
286 local gap = SA_OPTIONS.ListSpacing;
287 if (gap<0) then
288 gap = -gap;
289 end
290 gap = gap - 8;
291 -- if icons visible next to box
292 if (SA_List_HasSideIcons()) then
293 gap = gap + 30;
294 end
295 box.frame:ClearAllPoints();
296 box.frame:SetPoint(anchor, "Target1", "TOPRIGHT", gap, 0);
297 end
298 else
299 -- rest are anchored to previous box
300 box.frame:ClearAllPoints();
301 box.frame:SetPoint(anchor, "Target"..i-1, anchor, x, y);
302 end
303  
304 box.frame:SetWidth(width);
305 -- scale inside elements width
306 box.mobBar:SetWidth(width-20);
307 box.targetBar:SetWidth(width-20);
308 box.mobText:SetWidth(width-30);
309 box.targetText:SetWidth(width-30);
310  
311 -- update display targeted by stuff
312 box.targetOf:SetWidth(width-20);
313  
314 -- set texts alpha
315 box.mobText:SetAlpha(SA_OPTIONS.TextAlpha);
316 box.targetText:SetAlpha(SA_OPTIONS.TextAlpha);
317  
318 -- set icon positions
319 if (SA_OPTIONS.ClassIconMode>1) then
320 local icon = SA_ICONPOS[SA_OPTIONS.ClassIconMode];
321 SA_List_UpdateIconAppearance(box.classIcon, i, icon);
322 else
323 box.classIcon:Hide();
324 end
325  
326 if (SA_OPTIONS.TargetIconMode>1) then
327 local icon = SA_ICONPOS[SA_OPTIONS.TargetIconMode];
328 SA_List_UpdateIconAppearance(box.targetIcon, i, icon);
329 else
330 box.targetIcon:Hide();
331 end
332  
333 if (SA_OPTIONS.HuntersMarkIconMode>1) then
334 local icon = SA_ICONPOS[SA_OPTIONS.HuntersMarkIconMode];
335 SA_List_UpdateIconAppearance(box.huntersMarkIcon, i, icon);
336 else
337 box.huntersMarkIcon:Hide();
338 end
339  
340 end
341 end
342  
343 -- helper function, updates icon appearance
344 function SA_List_UpdateIconAppearance(frame, i, icon)
345 frame:ClearAllPoints();
346 frame:SetPoint(icon.point, "Target"..i, icon.point, icon.x, icon.y);
347 frame:SetWidth(icon.width);
348 frame:SetHeight(icon.height);
349 frame:Show();
350 end
351  
352 ------------------------------------------------------------------------------
353 -- Refresh the list if enough time has passed since last refresh
354 ------------------------------------------------------------------------------
355  
356 function SA_List_OnUpdate(elapsed)
357 SA_LASTREFRESH = SA_LASTREFRESH - elapsed;
358 if (SA_LASTREFRESH > 0) then
359 return;
360 end
361 SA_LASTREFRESH = SA_BASERATE + (SA_INCREASE * SA_PREV.mob_count);
362 SA_List_Update();
363 end
364  
365 local TARGET_CLICKED = time()
366 function SA_List_Target_OnClick(arg)
367 SA_Debug("TargetClick");
368 TARGET_CLICKED = time();
369 local target = arg.obj;
370 if (not target) then printInfo("SmartAssist: Unknown target?"); return; end;
371 -- if ctrl+alt is down paste all targetters names (except tanks) to chat
372 if (IsControlKeyDown() and IsAltKeyDown()) then
373 local names = "";
374 local i = 0;
375 for _,c in target.players do
376 if (not SA_IsTank(c.unitName)) then
377 names = names..c.unitName..", ";
378 i = i + 1;
379 end;
380 end
381 if (i>0) then
382 names = string.sub(names, 0, -3);
383 ChatFrameEditBox:SetText(names.." ");
384 ChatFrameEditBox:Show();
385 end
386 return;
387 end
388  
389 -- todo: problem is that target.players[1].unitName may have changed target between updating the list --> click
390 -- however we could impove situation by assisting the player that has the MOST common target amon all targetters
391 -- ie. iterate all targetters and check if UnitIsUnit(unit, others) the player who has most highest count is
392 -- most likelly the correct one! That would however do 40*39 (1590) checks at worst!
393 -- additionally / alternatively we could check target name (if only one player for example) and disaply
394 -- error (+sound?) if it has been changed
395  
396 -- initial implementation (just verboses when debug on)
397 --local counts = {};
398 local hicount = 0;
399 local unitId = "";
400 for _,player in target.players do
401 --counts[player.unitId] = SA_List_TargetShareCount(player, target.players);
402 local count = SA_List_TargetShareCount(player, target.players);
403 if (count > hicount) then
404 hicount = count;
405 unitId = player.unitId;
406 end
407 end
408 --for unitId,count in counts do
409 --SA_Debug(unitId.." shares target with "..tostring(count), 1);
410 --end
411 SA_Debug("Assisting "..unitId.." which has highest common count of "..hicount);
412 AssistUnit(unitId);
413  
414 -- initiate assist
415 --SA_Debug("assisting "..target.players[1].unitName);
416 --AssistUnit(target.players[1].unitId);
417  
418 SA_PostAssist();
419 -- update the list immediattely
420 SA_PREV.title_mode = -1; -- resets title mode on next refresh
421 SA_List_Refresh();
422 end
423  
424 -- return number of players in list that have same target as player
425 function SA_List_TargetShareCount(player, list)
426 local count = 0;
427 for _,compare in list do
428 if (UnitIsUnit(player.unitId.."target", compare.unitId.."target")) then
429 count = count + 1;
430 end
431 end
432 return count;
433 end
434  
435 function SA_List_Target_OnEnter(arg)
436 local text = SA_List_Target_GetTooltip(arg);
437 GameTooltip:SetOwner(arg, "ANCHOR_LEFT");
438 GameTooltip:SetText(text,1,1,1,1,1);
439  
440 -- prevent going to pausemode if target has just been clicked
441 if (time() - TARGET_CLICKED > 3) then
442 -- Add some "sleep" to refreshing the list. It's not good to update the list while user is trying to select something.
443 SA_LASTREFRESH = SA_BASERATE * 3;
444 SA_PREV.title_mode = -1; -- resets title mode on next refresh
445 SA_List_SetTitleButton(MODE_PAUSED);
446 end
447 end
448  
449 function SA_List_Target_OnLeave(arg)
450 GameTooltip:Hide();
451 end
452  
453 function SA_List_Target_GetTooltip(arg)
454 local target = arg.obj;
455 if (not target) then return "Unknown?"; end;
456 local text = target.fullName.."\nTargetted by:\n";
457 for k,v in target.players do
458 -- colorize text by class
459 local cv = RAID_CLASS_COLORS[string.upper(v.class)];
460 local color="";
461 if (not cv) then
462 color = "|cff888888";
463 else
464 color = SA_ToTextCol(cv.r, cv.g, cv.b);
465 end
466 text = text .. color..v.unitName .. "|r" .. "\n";
467 end
468 return text;
469 end
470  
471 ------------------------------------------------------------------------------
472 -- Request list to be refreshed immediattely
473 ------------------------------------------------------------------------------
474  
475 function SA_List_Refresh()
476 --SA_Debug("Requesting refresh now, target="..tostring(UnitName("target")));
477 SA_LASTREFRESH = 0;
478 end
479  
480 ------------------------------------------------------------------------------
481 -- get puller from candidate list
482 ------------------------------------------------------------------------------
483  
484 function SA_GetPuller(candidates)
485 for _,v in candidates do
486 if (v.unitName == SA_OPTIONS.puller) then
487 return v;
488 end
489 end
490 end
491  
492 ------------------------------------------------------------------------------
493 -- check if candidates target is already targetted by someone
494 -- return the target if found and nil if not
495 ------------------------------------------------------------------------------
496  
497 function SA_GetExistingTarget(candidate, targets)
498 for _,target in targets do
499 if (UnitIsUnit(target.players[1].target, candidate.target)) then
500 return target;
501 end
502 end
503 return nil;
504 end
505  
506 function SA_List_NameSplit(str)
507 local t = {n=0}
508 local function helper(word) table.insert(t, word) end
509 if not string.find(string.gsub(str, "%w+", helper), "%S") then return t end
510 end
511  
512 ------------------------------------------------------------------------------
513 -- construct target from candidate
514 ------------------------------------------------------------------------------
515  
516 function SA_GetTarget(candidate, pullerId)
517 target = {};
518 target["players"] = { candidate };
519 target["name"] = UnitName(candidate.target);
520 target["fullName"] = target.name;
521 -- intelligent name split
522 if (SA_OPTIONS.IntelligentSplit and target.name) then
523 local parts = SA_List_NameSplit(target.name);
524 if (parts) then
525 if (table.getn(parts)>2) then
526 if (parts[2]~="of") then -- splitting "shade of naxxramas" -> "of naxxramas" is stupid, dont split if second word is "of"
527 target.name = string.sub(target.name, string.len(parts[1])+1 );
528 end
529 end
530 end
531 end
532 target["health"] = UnitHealth(candidate.target);
533 target["targetName"] = UnitName(candidate.target.."target");
534 -- test fix for "shang's problem"
535 -- this seems to happend when unit is near edge of known area and it's target is outide of it. Hence my client doesn't know anything about this unit ..
536 if (target.targetName=="Unknown Entity" or target.targetName=="Unknown") then
537 target.targetName=nil;
538 end
539 -- target.self gives us a reference to mob, example: party<n>target
540 target["self"] = candidate.target;
541 target["targetHealth"] = ceil( UnitHealth( candidate.target.."target" ) / UnitHealthMax( candidate.target.."target" ) * 100 );
542 local _,targetClass = UnitClass(target.self);
543 target["targetClass"] = targetClass;
544  
545 target["playersCount"] = 1;
546 if (SA_IsMarked(candidate.target)) then
547 target["marked"] = true;
548 else
549 target["marked"] = false;
550 end
551  
552 if (isUnitCC(candidate.target)) then
553 target["cced"] = true;
554 else
555 target["cced"] = false;
556 end
557  
558 -- todo: xxx, only if enabled
559 target["icon"] = GetRaidTargetIndex(target.self);
560 if (not target.icon) then
561 target["icon"] = 0;
562 end
563  
564 -- is world boss, elite etc
565 target["classification"] = UnitClassification(candidate.target);
566  
567 -- set myTarget to true if this is my target
568 target["myTarget"] = UnitIsUnit(target.self, "target");
569  
570 if (pullerId~="") then
571 target["pullerTarget"] = UnitIsUnit(target.self, pullerId.."target");
572 end
573 -- is unit in combat, 20.6.2006 - if it has target, treat as in combat!
574 target["inCombat"] = UnitAffectingCombat(target.self) or target.targetName;
575  
576 return target;
577 end
578  
579 -- return true if this candidate target should be added to list
580 function SA_Add_To_List(candidate)
581 if( UnitCanAssist("player", candidate.unitId) and
582 UnitExists(candidate.target) and
583 ((UnitAffectingCombat(candidate.target) or SA_OPTIONS.OutOfCombat) and not UnitIsDead(candidate.target)) and
584 UnitCanAttack("player", candidate.target))
585 then
586 return true;
587 else
588 return false;
589 end
590 end
591  
592 function SA_List_SortTarget(a,b)
593 -- marked first
594 if (a.marked and not b.marked) then return true; end;
595 if (b.marked and not a.marked) then return false; end;
596  
597 -- puller target second
598 --if (a.pullerTarget and not b.pullerTarget) then return true; end;
599 --if (b.pullerTarget and not a.pullerTarget) then return false; end;
600  
601 -- ooc last
602 if (a.inCombat and not b.inCombat) then return true; end;
603 if (b.inCombat and not a.inCombat) then return false; end;
604  
605 -- cced second lastest
606 if (a.cced and not b.cced) then return false; end;
607 if (b.cced and not a.cced) then return true; end;
608  
609 -- if both unmarked, then sort by name
610 if (a.name == b.name) then
611 return a.playersCount > b.playersCount;
612 else
613 return a.name > b.name;
614 end
615 end
616  
617 ------------------------------------------------------------------------------
618 -- updates the available assists list
619 ------------------------------------------------------------------------------
620  
621 -- this table contains all variables used in incoming detection & printing
622 local incoming = { prev_iter=0, currently=0, prev_time=0 };
623  
624 function SA_List_Update()
625  
626 -- if disabled, stop immediattely
627 if (not SA_OPTIONS.ShowAvailable) then return; end
628  
629 local candidates, members= SA_GetCandidates(false)
630 table.sort(candidates, function(a,b) return SA_SortCandidate(a,b,members) end);
631  
632 -- filter out candidates out of range if assisting only members nearby
633 -- TODO: might not be good since causes overheading AND we should still add those players outside range to target?
634 if (SA_OPTIONS.AssistOnlyNearest) then
635 candidates = SA_FilterCandidatesByDistance(candidates, false);
636 end
637  
638 -- add player to candidates if enabled
639 if (UnitExists("target") and SA_OPTIONS.AddMyTarget) then
640 table.insert(candidates, SA_GetPlayerAsCandidate());
641 end
642  
643 local aggro_count = 0;
644 local wb_count = 0;
645  
646 local pullerId = "";
647 if (SA_OPTIONS.puller) then
648 local puller = SA_GetPuller(candidates);
649 -- puller might be null if it doesn't exist in group
650 if (puller) then
651 pullerId = puller.unitId;
652 end
653 end
654  
655 -- construct list of available assists / targets
656 local targets = {};
657 for _,candidate in candidates do
658 if (SA_Add_To_List(candidate)) then
659 local target = SA_GetExistingTarget(candidate, targets);
660 if (target) then
661 -- append targetting candidate to target
662 table.insert(target.players, candidate);
663 target.playersCount = target.playersCount + 1;
664 else
665 -- this candidate target is not targetted by anyone else, construct new target to the list
666 local target = SA_GetTarget(candidate, pullerId);
667  
668 -- filtering if enabled
669 if (SA_OPTIONS.Filter) then
670 if string.find(string.lower(target.fullName), SA_OPTIONS.Filter) then
671 table.insert(targets, target);
672 end
673 else
674 table.insert(targets, target);
675 end
676 end
677 end
678 end
679  
680 -- update non-aggro cache and mark cache, this is used when SORTING candidates (see SA_SortCandidate)
681 SA_MARKEDCACHE = {};
682 SA_PASSIVECACHE = {};
683 local dirty = false;
684 local i = 0;
685 local mc = 0;
686 local ooc_seen = false;
687 for _,target in targets do
688 i = i + 1;
689 if (target.marked) then
690 if (i ~= 1 and mc == 0) then
691 -- marked target not in first place, list is tainted
692 SA_Debug("mark dirty list "..target.name, 1);
693 dirty = true;
694 end
695 mc = mc + 1;
696 for _,c in target.players do
697 SA_MARKEDCACHE[c.unitName] = true;
698 end
699 end
700 if (not target.inCombat or target.cced) then
701 ooc_seen = true;
702 for _,c in target.players do
703 SA_PASSIVECACHE[c.unitName] = true;
704 end
705 else
706 -- incombat targets after ooc targets, list is tainted
707 if (ooc_seen) then
708 SA_Debug("ooc dirty list "..target.name, 1);
709 dirty = true;
710 end
711 end
712 end
713  
714 -- if list is dirty abort refresh and request new one with correct cache,
715 -- update 22.4.2006 cache is not correct on next refresh either, in fact it may pass long time it is okay.
716 -- hence added PREV_DIRTY flag until problem is investigated
717 -- update 20.6.2006 sort does not seem to work correctly with buff (ie. hunters mark) priorization!
718 -- update 09.7.2006 sorting seems to work fine now, cleanup this + futile flag?
719 if (dirty and not SA_PREV.dirty) then
720 -- try again soon
721 SA_LASTREFRESH = 0.2;
722 SA_PREV.dirty = true;
723 return;
724 end
725 SA_PREV.dirty = false;
726  
727 -- sort targets depending options
728 if (SA_OPTIONS.PreserveOrder) then
729 table.sort(targets, function(a,b) return SA_List_SortTarget(a,b) end);
730 end
731  
732 ------------------------------------------------------------------------------
733  
734 local i = 0;
735 local overloaded = false;
736  
737 for _,target in targets do
738 i = i + 1;
739 if (i>10) then
740 overloaded = true;
741 break;
742 end
743  
744 local box = SA_LIST_BOXES[i];
745 box.frame.obj = target; -- store the target data to box (used ie. in tooltips)
746 box.frame:Show();
747  
748 -- set values & texts
749 local targetText = target.players[1].unitName;
750 if (not SA_OPTIONS.HideTBY) then
751 targetText = "|cffffffffT.by |r"..targetText;
752 end
753 if (target.playersCount > 1) then
754 targetText = targetText.." + "..tostring(target.playersCount-1); -- -1 for the one who targets (who + n)
755 end
756 -- append prefixes to mob name + for elite, WB+ for worldboss
757 local mobText = target.name;
758 if (target.classification == "elite") then
759 mobText = SA_SEMIALERT.."+|r"..mobText;
760 end
761 if (target.classification == "worldboss") then
762 mobText = SA_SEMIALERT.."WB+|r"..mobText;
763 if (target.inCombat) then
764 wb_count = wb_count + 1;
765 end
766 end
767 box.mobBar:SetValue(target.health);
768 box.mobText:SetText(mobText);
769  
770 -- colorize box
771 if (not target.inCombat) then
772 -- if this is out of combat and my target, apply only slight green efect
773 if (target.myTarget) then
774 box.frame:SetBackdropColor(0,0.65,0,0.65);
775 box.frame:SetBackdropBorderColor(0,1,0,0.75);
776 elseif (target.pullerTarget) then
777 -- yellow background on puller target
778 box.frame:SetBackdropColor(1,1,0,0.3);
779 box.frame:SetBackdropBorderColor(0,0,0,0.4); -- regular grey
780 else
781 box.frame:SetBackdropColor(0,0,0,0.4);
782 box.frame:SetBackdropBorderColor(0,0,0,0.4); -- regular grey
783 end
784 else
785 local addinc = true;
786 -- set border color
787 if (target.marked) then
788 box.frame:SetBackdropBorderColor(1,0,0,1);
789 elseif (target.myTarget) then
790 box.frame:SetBackdropBorderColor(0,1,0,1);
791 elseif (target.cced) then
792 box.frame:SetBackdropBorderColor(0,0,0,1);
793 addinc = false;
794 else
795 box.frame:SetBackdropBorderColor(0.8,0.8,0.8,1);
796 end
797 -- add to incoming
798 if (addinc) then
799 incoming.currently = incoming.currently + 1;
800 end
801  
802 -- set background color
803 if (target.myTarget) then
804 box.frame:SetBackdropColor(0,0.75,0,0.85);
805 elseif (target.pullerTarget) then
806 -- yellow background on puller target
807 box.frame:SetBackdropColor(1,1,0,0.3);
808 else
809 box.frame:SetBackdropColor(0,0,0,0.5);
810 end
811 end
812  
813 -- colorize target target text in some situations
814 if (target.targetName) then
815 local col = "";
816 local endcol = "";
817 if (UnitName("player") == target.targetName) then
818 -- targetting player
819 aggro_count = aggro_count + 1;
820 if (SA_OPTIONS.TankMode) then
821 col = SA_SEMIALERT;
822 else
823 col = SA_ALERTCOL;
824 end
825 elseif (not SA_IsTank(target.targetName) and SA_OPTIONS.TankMode) then
826 -- targeting non tank in tank mode
827 col = SA_ALERTCOL;
828 elseif (SA_IsTank(target.targetName)) then
829 -- targeting tank always on tank color, regardless of tank mode
830 col = SA_TANKCOL;
831 end
832 -- append end of color code marking
833 if (col~="") then
834 endcol = "|r";
835 end
836 box.targetBar:SetValue(target.targetHealth);
837 box.targetText:SetText("T:"..col..target.targetName..endcol);
838 else
839 box.targetBar:SetValue(0);
840 box.targetText:SetText("?");
841 end
842  
843 -- colorize mob bar
844 if (UnitIsTapped(target.self) and not UnitIsTappedByPlayer(target.self)) then
845 -- target is "grey" to us
846 box.mobBar:SetStatusBarColor(0.8, 0.8, 0.8);
847 elseif (UnitPlayerControlled(target.self)) then
848 -- pvp target
849 box.mobBar:SetStatusBarColor(1, 0, 0);
850 elseif (not target.inCombat) then
851 -- out of combat target
852 box.mobBar:SetStatusBarColor(0.38, 0.38, 0.38);
853 else
854 -- normal green
855 box.mobBar:SetStatusBarColor(0, 1, 0);
856 end
857  
858 -- make texts visible always (bugs on some systems)
859 -- code peeked from TextStatusBar_UpdateTextString sources
860 -- perhaps I should've called that method but it does some other unwanted things
861 box.mobBar.TextString:Show();
862 box.targetBar.TextString:Show();
863 box.targetOf:SetText(targetText);
864  
865 -- set class icon (texture coordinates)
866 local coords = SA_TEXTCOORDS[target.targetClass];
867 box.classIcon:SetTexCoord(coords.left, coords.right, coords.top, coords.bottom);
868  
869 -- set target icon (texture coordinates)
870 -- TODO: xxx, only if feature enabled
871 if (target.icon>0) then
872 local icon = UnitPopupButtons["RAID_TARGET_"..target.icon];
873 box.targetIcon:SetTexCoord(icon.tCoordLeft, icon.tCoordRight, icon.tCoordTop, icon.tCoordBottom );
874 box.targetIcon:Show();
875 else
876 box.targetIcon:Hide();
877 end
878  
879 -- hunters mark icon
880 if (target.marked and SA_OPTIONS.HuntersMarkIconMode>1) then
881 box.huntersMarkIcon:Show();
882 else
883 box.huntersMarkIcon:Hide();
884 end
885  
886 end
887  
888 ------------------------------------------------------------------------------
889  
890 -- if count has been changed update visibility
891 -- TODO: apparently there is no performance loss calling hide/show .. so this is partially futile now
892 if (i ~= SA_PREV.mob_count) then
893 for j = i + 1, SA_PREV.mob_count do
894 if (j>10) then
895 break;
896 end
897 SA_LIST_BOXES[j].frame:Hide();
898 end
899 end
900  
901 -- if player have a ACQUIRED aggro, play optional sound to alert player
902 if (aggro_count > SA_PREV.aggro_count) then
903 if (SA_OPTIONS.AudioWarning) then PlaySound(SA_OPTIONS.SoundGainAggro); end;
904 if (SA_OPTIONS.VerboseAcquiredAggro) then
905 SA_Verbose("Acquired aggro!", COLOR_ALERT);
906 end
907 end
908 -- if player have a LOST aggro, play optional sound to alert player
909 if (aggro_count < SA_PREV.aggro_count and i >= SA_PREV.mob_count) then
910 if (SA_OPTIONS.LostAudioWarning) then PlaySoundFile(SA_OPTIONS.SoundLoseAggro); end;
911 if (SA_OPTIONS.VerboseLostAggro) then
912 SA_Verbose("Lost aggro!", COLOR_ALERT);
913 end
914 end
915 -- if incoming worldbosses, play optional sound to alert player, TODO: OPTION!
916 if (wb_count > SA_PREV.wb_count) then
917 SA_Verbose("Incoming worldboss!", COLOR_ALERT);
918 PlaySoundFile(SA_OPTIONS.SoundIncomingWoldBoss);
919 end
920  
921 -- if we have new mobs in combat
922 -- TODO: on incoming worldboss level unit play alert sound!
923 if (SA_OPTIONS.VerboseIncoming) then
924 local now = time();
925 if (incoming.currently > incoming.prev_iter and now - incoming.prev_time > 4) then
926  
927 local amount = incoming.currently - incoming.prev_iter;
928 if (amount > 1) then
929 SA_Verbose("Incoming x"..amount.."!", COLOR_ALERT);
930 else
931 SA_Verbose("Incoming!", COLOR_ALERT);
932 end
933  
934 incoming.prev_time = now;
935 end
936 incoming.prev_iter = incoming.currently;
937 incoming.currently = 0;
938 end
939  
940 -- update title to correct mode
941 if (i>0) then
942 title_mode = MODE_NORMAL;
943 else
944 title_mode = MODE_OOC;
945 end
946 if (SA_OPTIONS.Filter) then
947 title_mode = MODE_FILTERED;
948 end
949 if (SA_OPTIONS.AssistOnlyNearest) then
950 title_mode = MODE_NEAREST;
951 end
952 if (overloaded) then
953 title_mode = MODE_OVERLOADED;
954 end
955  
956 -- update title button if mode has been changed
957 if (title_mode ~= SA_PREV.title_mode) then
958 SA_List_SetTitleButton(title_mode);
959 end
960  
961 -- set prev data for next iteration
962 SA_PREV.title_mode = title_mode;
963 SA_PREV.mob_count = i;
964 SA_PREV.wb_count = wb_count;
965 SA_PREV.aggro_count = aggro_count;
966 end
967  
968 ------------------------------------------------------------------------------
969 -- Title button handling
970 ------------------------------------------------------------------------------
971  
972 function SA_List_SetTitleButton(mode)
973 if (mode == MODE_NORMAL or mode == MODE_OOC) then
974 if (mode == MODE_NORMAL) then
975 SAListTitleButton:SetAlpha(1);
976 else
977 if (SA_OPTIONS.HideTitle) then
978 SAListTitleButton:SetAlpha(0);
979 else
980 SAListTitleButton:SetAlpha(SA_OPTIONS.ListOOCAlpha);
981 end
982 end
983 SAListTitleButton:SetBackdropColor(0, 0, 0, 0.3);
984 SAListTitleButton:SetBackdropBorderColor(1,1,1,1)
985 SAListTitle:SetText("Available assists");
986 elseif (mode == MODE_FILTERED or mode == MODE_NEAREST) then
987 SAListTitleButton:SetAlpha(1);
988 SAListTitleButton:SetBackdropColor(0,0.6,0.8,1);
989 SAListTitleButton:SetBackdropBorderColor(0,0.6,0.8,1);
990 local text = "";
991 if (mode == MODE_FILTERED) then
992 text = "Filtered";
993 end
994 if (mode == MODE_NEAREST) then
995 text = "Nearest";
996 end
997 SAListTitle:SetText(text);
998 elseif (mode == MODE_PAUSED) then
999 SAListTitleButton:SetAlpha(1);
1000 SAListTitleButton:SetBackdropColor(0.3,0.3,0.3,1);
1001 SAListTitleButton:SetBackdropBorderColor(0.6,0.6,0.6,1);
1002 SAListTitle:SetText("Paused");
1003 elseif (mode == MODE_OVERLOADED) then
1004 SAListTitleButton:SetAlpha(1);
1005 SAListTitleButton:SetBackdropColor(1,0,0,1);
1006 SAListTitleButton:SetBackdropBorderColor(1,0,0,1);
1007 SAListTitle:SetText("OVERLOADED");
1008 else
1009 printInfo("smartassist error: unknown title mode "..tostring(mode));
1010 end
1011 end
1012  
1013 SA_PREV_ALPHA = 1;
1014 function SA_List_TitleButton_OnEnter()
1015 SA_PREV_ALPHA = SAListTitleButton:GetAlpha();
1016 SAListTitleButton:SetAlpha(1);
1017 end
1018  
1019 function SA_List_TitleButton_OnLeave()
1020 SAListTitleButton:SetAlpha(SA_PREV_ALPHA);
1021 end