vanilla-wow-addons – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | -- AUTHOR: Paranoidi |
2 | |||
3 | -- $LastChangedDate: 2006-08-14 18:34:44 +0300 (Mon, 14 Aug 2006) $ |
||
4 | -- $LastChangedBy: marko $ |
||
5 | -- $Rev: 247 $ |
||
6 | |||
7 | --[[ |
||
8 | |||
9 | local px,py = GetPlayerMapPosition("player"); |
||
10 | local tx,ty = GetPlayerMapPosition("raid"..idx); |
||
11 | local xdist = tx-px; |
||
12 | local ydist = ty-py; |
||
13 | local dist = sqrt(math.pow(xdist,2)+math.pow(ydist,2)); |
||
14 | |||
15 | http://intern.darklegion.de/interface/profiler.jpg |
||
16 | |||
17 | testaa profileria? |
||
18 | |||
19 | BUG: jonku pelaajan targettaaminen ei nollaa järjestystä (kuten tyhjän valinta) ! |
||
20 | |||
21 | --]] |
||
22 | |||
23 | |||
24 | -- integration to myAddons |
||
25 | SmartAssistDetails = { |
||
26 | name = "SmartAssist", |
||
27 | description = "SmartAssist is an addon which improves default assisting system in groups.", |
||
28 | version = "1.2.9", |
||
29 | releaseDate = string.sub("$LastChangedDate: 2006-08-14 18:34:44 +0300 (Mon, 14 Aug 2006) $", 18, 28), |
||
30 | author = "paranoidi", |
||
31 | email = "paranoidi@gmx.net", |
||
32 | category = MYADDONS_CATEGORY_COMBAT, |
||
33 | frame = "SmartAssistFrame", |
||
34 | optionsframe = "SAOptionsFrame" |
||
35 | }; |
||
36 | |||
37 | -- options |
||
38 | SA_OPTIONS_WRAPPER = {}; |
||
39 | SA_OPTIONS = nil; |
||
40 | |||
41 | SA_MARKEDCACHE = {}; |
||
42 | SA_PASSIVECACHE = {}; |
||
43 | ASSIST_EMOTES = {}; |
||
44 | PREVIOUS_ASSISTS = {}; |
||
45 | PREVIOUS_NEAREST = false; |
||
46 | PREVIOUS_FALLBACK = false; |
||
47 | PREVIOUS_ASSIST_TIME = 0; |
||
48 | PREVIOUS_NEAREST_TIME = 0; |
||
49 | TARGET_CHANGED = false; |
||
50 | |||
51 | SMARTASSIST_CORE_REV = tonumber(string.sub("$Rev: 247 $", 7, -3)); |
||
52 | BINDING_HEADER_SMARTASSIST = "SmartAssist"; |
||
53 | BINDING_NAME_SASET = "Set puller"; |
||
54 | BINDING_NAME_SAASSIST = "Assist"; |
||
55 | |||
56 | SA_RUNNING = false; -- used to disable sounds and detect target changes |
||
57 | PASSIVE_WARN_FLAG = false; -- is passive warning visible |
||
58 | |||
59 | COLOR_DEFAULT = {r=0, g=0.8, b=1}; |
||
60 | COLOR_ALERT = {r=0.9, g=0, b=0}; |
||
61 | |||
62 | StaticPopupDialogs["SA_NO_BINDINGS"] = { |
||
63 | text = TEXT("You must bind SmartAssist key before being able to use this addon!"), |
||
64 | button1 = TEXT(ACCEPT), |
||
65 | OnAccept = function() |
||
66 | printInfo("Tip: Press ESC key and go to 'bind keys' options. SmartAssist keys should be there at somewhere near bottom of the list."); |
||
67 | end, |
||
68 | timeout = 0, |
||
69 | whileDead = 1, |
||
70 | }; |
||
71 | |||
72 | StaticPopupDialogs["SA_UPGRADE_RESETPOS"] = { |
||
73 | text = TEXT("SmartAssist will need to reset frame positons due changes in the way scaling is used."), |
||
74 | button1 = TEXT(ACCEPT), |
||
75 | OnAccept = function() |
||
76 | SA_ResetFramePos(); |
||
77 | end, |
||
78 | timeout = 0, |
||
79 | whileDead = 1, |
||
80 | }; |
||
81 | |||
82 | StaticPopupDialogs["SA_AUTOCONF"] = { |
||
83 | text = TEXT("Auto configure Smart Actions?"), |
||
84 | button1 = TEXT(ACCEPT), |
||
85 | button2 = TEXT(CANCEL), |
||
86 | OnAccept = function() |
||
87 | SA_AutoConfigureSmartActions(); |
||
88 | printInfo("You should see SmartAssist options and verify detected settings. Use command /sa to access configuration."); |
||
89 | end, |
||
90 | timeout = 0, |
||
91 | whileDead = 1, |
||
92 | }; |
||
93 | |||
94 | StaticPopupDialogs["SA_DISABLE_ATTACK"] = { |
||
95 | text = TEXT("You have 'attack on assist' enabled in interface options which will cause problems with SmartAssist.\n\nAllow SmartAssist to turn it off (recommended) ?"), |
||
96 | button1 = TEXT(YES), |
||
97 | button2 = TEXT(NO), |
||
98 | OnAccept = function() |
||
99 | SetCVar("assistAttack", "0"); |
||
100 | end, |
||
101 | OnCancel = function() |
||
102 | SA_OPTIONS.AutoAttackWhineIgnored = true; |
||
103 | end, |
||
104 | timeout = 0, |
||
105 | whileDead = 1, |
||
106 | }; |
||
107 | |||
108 | function SA_OnLoad() |
||
109 | SLASH_SMARTASSIST1 = "/smartassist"; |
||
110 | SLASH_SMARTASSIST2 = "/sa"; |
||
111 | SlashCmdList["SMARTASSIST"] = function(msg) |
||
112 | SA_Command(msg); |
||
113 | end |
||
114 | end |
||
115 | |||
116 | function SA_InitOption(option, value) |
||
117 | if (SA_OPTIONS[option]==nil) then SA_OPTIONS[option]=value; end; |
||
118 | end |
||
119 | |||
120 | ------------------------------------------------------------------------------ |
||
121 | -- event: variables loaded |
||
122 | ------------------------------------------------------------------------------ |
||
123 | |||
124 | function SA_VariablesLoaded() |
||
125 | -- Register the addon in myAddOns |
||
126 | if(myAddOnsFrame_Register) then |
||
127 | myAddOnsFrame_Register(SmartAssistDetails); |
||
128 | end |
||
129 | printInfo("SmartAssist "..SmartAssistDetails.version.." loaded.") |
||
130 | |||
131 | -- add item to every possible context menu |
||
132 | table.insert(UnitPopupMenus["PARTY"], "PULLER"); |
||
133 | table.insert(UnitPopupMenus["PLAYER"], "PULLER"); |
||
134 | table.insert(UnitPopupMenus["RAID"], "PULLER"); |
||
135 | |||
136 | UnitPopupButtons["PULLER"] = { text = TEXT("Set as puller"), dist = 0 }; |
||
137 | |||
138 | SA_Init(); |
||
139 | end |
||
140 | |||
141 | |||
142 | function SA_Init() |
||
143 | |||
144 | -- load realm & char specific configs |
||
145 | local id = SA_GetAccountID(); |
||
146 | if (SA_OPTIONS_WRAPPER[id]==nil) then |
||
147 | printInfo("SmartAssist created new default settings for "..id); |
||
148 | SA_OPTIONS_WRAPPER[id] = {}; |
||
149 | end |
||
150 | SA_OPTIONS = SA_OPTIONS_WRAPPER[id]; |
||
151 | |||
152 | -- set default values to option variables if they do not exist |
||
153 | |||
154 | -- before rev 66 we had no stored rev number so we need to make sure it's in such cases atleast zero |
||
155 | SA_InitOption("Rev", 0); |
||
156 | |||
157 | -- special cases where we need to reset variables when upgrading the addon from older version |
||
158 | if (SA_OPTIONS.Rev<240 and SA_OPTIONS.Rev>0) then |
||
159 | StaticPopup_Show("SA_UPGRADE_RESETPOS"); |
||
160 | end |
||
161 | |||
162 | -- basic options |
||
163 | SA_InitOption("VisualWarning", true); |
||
164 | SA_InitOption("AssistOnEmote", true); |
||
165 | SA_InitOption("PriorizeHealth", true); |
||
166 | SA_InitOption("PriorizeHealthValue", 35); |
||
167 | SA_InitOption("FallbackTargetNearest", true); |
||
168 | SA_InitOption("CheckNearest", false); |
||
169 | SA_InitOption("NearestMustBePvP", false); |
||
170 | SA_InitOption("NearestMustBeTargetting", true); |
||
171 | SA_InitOption("AssistOnlyNearest", false); |
||
172 | -- available assist list |
||
173 | SA_InitOption("ShowAvailable", true); |
||
174 | SA_InitOption("PreserveOrder", false); |
||
175 | SA_InitOption("OutOfCombat", false); |
||
176 | SA_InitOption("TankMode", false); -- todo: class based defaults |
||
177 | SA_InitOption("ClassIconMode", 1); |
||
178 | SA_InitOption("TargetIconMode", 3); |
||
179 | SA_InitOption("HuntersMarkIconMode", 1); |
||
180 | SA_InitOption("AddMyTarget", false); |
||
181 | SA_InitOption("ListWidth", 150); |
||
182 | SA_InitOption("ListScale", 1.0); |
||
183 | SA_InitOption("ListOOCAlpha", 0.4); |
||
184 | SA_InitOption("ListSpacing", -5); |
||
185 | SA_InitOption("ListHorizontal", false); |
||
186 | SA_InitOption("ListTwoRow", false); |
||
187 | SA_InitOption("LockList", false); |
||
188 | SA_InitOption("HideTBY", false); |
||
189 | SA_InitOption("HideTitle", false); |
||
190 | SA_InitOption("AudioWarning", true); -- todo: class based defaults |
||
191 | SA_InitOption("LostAudioWarning", false); -- todo: class based defaults |
||
192 | SA_InitOption("VerboseAcquiredAggro", true); |
||
193 | SA_InitOption("VerboseLostAggro", false); |
||
194 | SA_InitOption("TextAlpha", 0.9); |
||
195 | SA_InitOption("IntelligentSplit", false); |
||
196 | -- advanced |
||
197 | SA_InitOption("DisableSliderValue", 15); |
||
198 | SA_InitOption("DisableTargetNearest", true); |
||
199 | SA_InitOption("DisablePriorityHealth", true); |
||
200 | SA_InitOption("AssistKeyMode", 1); |
||
201 | SA_InitOption("DisableAutoCastKeyMode", 2); |
||
202 | SA_InitOption("VerboseAssist", true); |
||
203 | SA_InitOption("VerboseIncoming", false); |
||
204 | SA_InitOption("VerboseNearest", true); |
||
205 | SA_InitOption("VerboseUnableToAssist", false); |
||
206 | SA_InitOption("DisableAssistWithoutPuller", false); |
||
207 | SA_InitOption("PauseResetsOrder", false); |
||
208 | -- smart actions |
||
209 | SA_InitOption("AssistSpells", {}); |
||
210 | SA_InitOption("TriggerAssist", true); |
||
211 | -- geek (not in options) |
||
212 | SA_InitOption("SoundLoseAggro", "Sound\\Spells\\EyeOfKilroggDeath.wav"); |
||
213 | SA_InitOption("SoundIncomingWoldBoss", "Sound\\Spells\\PVPFlagTaken.wav"); |
||
214 | SA_InitOption("SoundGainAggro", "AuctionWindowClose"); |
||
215 | -- development |
||
216 | SA_InitOption("DebugLevel", 0); |
||
217 | |||
218 | -- display auto configuration dialog on first run |
||
219 | SA_InitOption("AutoConfiguredV2", false); |
||
220 | if (not SA_OPTIONS.AutoConfiguredV2) then |
||
221 | SA_OPTIONS.AutoConfiguredV2 = true; |
||
222 | StaticPopup_Show("SA_AUTOCONF"); |
||
223 | end |
||
224 | |||
225 | -- init auto cast on assist defaults for hunters |
||
226 | if (SA_OPTIONS["AutoAssist"]==nil and UnitClass("player") == CLASSNAME_HUNTER) then |
||
227 | SA_OPTIONS.AutoAssist = true; |
||
228 | SA_OPTIONS.FallbackOnBusy = true; |
||
229 | SA_OPTIONS.AutoAssistTexture = "Interface\\Icons\\INV_Weapon_Rifle_08"; |
||
230 | SA_OPTIONS.AutoAssistName = SPELL_AUTOSHOT; |
||
231 | end |
||
232 | SA_InitOption("AutoAssist", false); |
||
233 | SA_InitOption("AutoPetAttack", false); |
||
234 | SA_InitOption("AutoPetAttackBusy", false); |
||
235 | |||
236 | -- check if should display 'attack on assist' disable dialog if not ignored |
||
237 | SA_InitOption("AutoAttackWhineIgnored", false); |
||
238 | SA_CheckForAssistAttack(); |
||
239 | |||
240 | -- prefered assitance order (just a guess, got better one?) |
||
241 | SA_InitOption("ClassOrder",{ CLASSNAME_WARRIOR, CLASSNAME_HUNTER, |
||
242 | CLASSNAME_ROGUE, CLASSNAME_PALADIN, |
||
243 | CLASSNAME_DRUID, CLASSNAME_SHAMAN, CLASSNAME_MAGE, |
||
244 | CLASSNAME_WARLOCK, CLASSNAME_PRIEST } ); |
||
245 | |||
246 | -- store revision number to configuration, this can be used to detect some special cases where we need to reset |
||
247 | -- exsisting options (ie. bug in assistance order list) in new releases |
||
248 | SA_OPTIONS.Rev = SMARTASSIST_CORE_REV; |
||
249 | |||
250 | ASSIST_EMOTES = { EMOTE_EVERYONE_ATTACK, EMOTE_ATTACK, EMOTE_HELP }; |
||
251 | |||
252 | if (SA_OPTIONS.ShowAvailable) then |
||
253 | SAListFrame:Show(); |
||
254 | else |
||
255 | SAListFrame:Hide(); |
||
256 | end |
||
257 | end |
||
258 | |||
259 | -- check if assist attack is enabled |
||
260 | function SA_CheckForAssistAttack() |
||
261 | if (not SA_OPTIONS.AutoAttackWhineIgnored) then |
||
262 | if (GetCVar("assistAttack")~="0" and SA_OPTIONS.AutoAssist and SA_OPTIONS.AutoAssistName) then |
||
263 | StaticPopup_Show("SA_DISABLE_ATTACK"); |
||
264 | end |
||
265 | end |
||
266 | end |
||
267 | |||
268 | ------------------------------------------------------------------------------- |
||
269 | -- WARNING ICON EVENTS |
||
270 | ------------------------------------------------------------------------------- |
||
271 | |||
272 | SA_WARN_SPEED = 0.07; |
||
273 | SA_WARN_REFRESH = SA_WARN_SPEED; |
||
274 | SA_WARN_ALPHA = 0.4; |
||
275 | SA_WARN_ALPHA_UP = true; |
||
276 | |||
277 | function SA_WarnOnUpdate(elapsed) |
||
278 | if (not PASSIVE_WARN_FLAG) then return; end; |
||
279 | SA_WARN_REFRESH = SA_WARN_REFRESH - elapsed; |
||
280 | if (SA_WARN_REFRESH > 0) then |
||
281 | return; |
||
282 | end |
||
283 | SA_WARN_REFRESH = SA_WARN_SPEED; |
||
284 | |||
285 | -- if target target is != nil (attacking someone) then REMOVE the icon |
||
286 | if (UnitName("targettarget")~=nil) then |
||
287 | SA_Debug("target target != nil .. remove the warning", 1); |
||
288 | SA_ResetWarning(); |
||
289 | SA_AggroedTargetMsg(); |
||
290 | return; |
||
291 | end |
||
292 | |||
293 | if (SA_WARN_ALPHA_UP) then |
||
294 | SA_WARN_ALPHA = SA_WARN_ALPHA + 0.1; |
||
295 | else |
||
296 | SA_WARN_ALPHA = SA_WARN_ALPHA - 0.1; |
||
297 | end |
||
298 | if (SA_WARN_ALPHA<0.4) then |
||
299 | SA_WARN_ALPHA_UP = true; |
||
300 | SA_WARN_ALPHA = 0.5; |
||
301 | end |
||
302 | if (SA_WARN_ALPHA>1.0) then |
||
303 | SA_WARN_ALPHA_UP = false; |
||
304 | SA_WARN_ALPHA = 0.9; |
||
305 | end |
||
306 | SAWarningFrame:SetAlpha(SA_WARN_ALPHA); |
||
307 | end |
||
308 | |||
309 | ------------------------------------------------------------------------------- |
||
310 | -- handles smart assist slash commands |
||
311 | ------------------------------------------------------------------------------- |
||
312 | |||
313 | function SA_Command(msg) |
||
314 | local _,_,value = string.find(msg, "debug (%d)"); |
||
315 | if (value) then |
||
316 | SA_OPTIONS.DebugLevel = tonumber(value); |
||
317 | printInfo("Setting debug level to "..tostring(SA_OPTIONS.DebugLevel)); |
||
318 | elseif (msg=="available") then |
||
319 | SA_ToggleAvailable(); |
||
320 | elseif (msg=="assist") then |
||
321 | DoSmartAssist(); |
||
322 | elseif (msg=="reset" or msg=="reset all") then |
||
323 | -- resets frame positions |
||
324 | SA_ResetFramePos(); |
||
325 | |||
326 | -- clears this character settings and initializes defaults |
||
327 | if (msg=="reset all") then |
||
328 | printInfo("Reseting SmartAssist settings for all characters"); |
||
329 | SA_OPTIONS_WRAPPER = {}; |
||
330 | else |
||
331 | printInfo("Reseting SmartAssist settings for this character"); |
||
332 | local id = SA_GetAccountID(); |
||
333 | SA_OPTIONS_WRAPPER[id] = {}; |
||
334 | end |
||
335 | SA_Init(); |
||
336 | elseif (msg=="rev" or msg=="ver") then |
||
337 | if (SMARTASSIST_REV) then |
||
338 | printInfo("SmartAssist revision "..SMARTASSIST_REV); |
||
339 | else |
||
340 | printInfo("Unofficial or broken build! Revision number missing! Core rev "..SA_OPTIONS.Rev); |
||
341 | end |
||
342 | elseif (msg=="dump") then |
||
343 | if (not DevTools_Dump) then |
||
344 | printInfo("Requires devtools"); |
||
345 | else |
||
346 | printInfo("Dumping internal variables:"); |
||
347 | printInfo("PREVIOUS_ASSISTS: "); |
||
348 | DevTools_Dump(PREVIOUS_ASSISTS); |
||
349 | printInfo("PREVIOUS_NEAREST: "..tostring(PREVIOUS_NEAREST)); |
||
350 | printInfo("TARGET_CHANGED: "..tostring(TARGET_CHANGED)); |
||
351 | printInfo("SA_RUNNING: "..tostring(SA_RUNNING)); |
||
352 | end |
||
353 | else |
||
354 | printInfo("Available parameters:"); |
||
355 | printInfo("ver - Display addon version information."); |
||
356 | printInfo("assist - Execute SmartAssist for macros. Or call DoSmartAssist()"); |
||
357 | printInfo("reset - Reset character settings and frame positions."); |
||
358 | printInfo("reset all - Reset all characters settings and frame positions."); |
||
359 | SA_ShowOptions(); |
||
360 | end |
||
361 | end |
||
362 | |||
363 | function SA_ResetFramePos() |
||
364 | SAListFrame:ClearAllPoints(); |
||
365 | SAListFrame:SetPoint("TOP", "UIParent", "TOP", 0, -20); |
||
366 | SAListFrame:StopMovingOrSizing(); |
||
367 | SAWarningFrame:ClearAllPoints(); |
||
368 | SAWarningFrame:SetPoint("TOP", "UIParent", "TOP", 5, -25); |
||
369 | SAWarningFrame:StopMovingOrSizing(); |
||
370 | SAOptionsFrame:ClearAllPoints(); |
||
371 | SAOptionsFrame:SetPoint("CENTER", "UIParent", "CENTER", 0, 0); |
||
372 | SAOptionsFrame:StopMovingOrSizing(); |
||
373 | end |
||
374 | |||
375 | function SA_ShowOptions() |
||
376 | -- open config, or warn about key bindings |
||
377 | local key1, key2 = GetBindingKey("SAASSIST"); |
||
378 | if (key1==nil and key2==nil) then |
||
379 | StaticPopup_Show("SA_NO_BINDINGS"); |
||
380 | else |
||
381 | SAOptionsFrame:Show(); |
||
382 | end |
||
383 | end |
||
384 | |||
385 | ------------------------------------------------------------------------------- |
||
386 | -- SMART ACTIONS |
||
387 | ------------------------------------------------------------------------------- |
||
388 | |||
389 | local old_UseAction = UseAction; |
||
390 | function UseAction(m1, m2, m3) |
||
391 | |||
392 | local name = SA_GetSlotName(m1); |
||
393 | local assistAction = false; |
||
394 | if (SA_TableIndex(SA_OPTIONS.AssistSpells, name) ~= -1) then |
||
395 | assistAction = true; |
||
396 | end |
||
397 | |||
398 | -- no target selected and attack action |
||
399 | -- treat targetting player as no target |
||
400 | if (SA_OPTIONS.TriggerAssist and (UnitName("target")==nil or UnitIsUnit("target","player")) and assistAction) then |
||
401 | SA_Debug("no target, initiating assist by action!", 1); |
||
402 | FindTarget(false, false); |
||
403 | -- target is not player selected so don't attack unless target already in combat |
||
404 | if (not UnitAffectingCombat("target")) then |
||
405 | SA_Debug("not in combat, aborting action", 1); |
||
406 | SA_ShowWarning(); |
||
407 | return; |
||
408 | end |
||
409 | end |
||
410 | |||
411 | -- if trying to attack friendly unit, initiate smart action |
||
412 | |||
413 | -- UnitIsFriend bugs when dueling people in your party |
||
414 | -- Added UnitPlayerControlled because on pve server as non flagged attacking the opposite faction initiated assist! |
||
415 | if (assistAction and not UnitCanAttack("target", "player") and not (UnitPlayerControlled("target") and UnitFactionGroup("player")~=UnitFactionGroup("target"))) then |
||
416 | SA_Debug("friendly target, assistAction = "..tostring(assistAction), 1); |
||
417 | if (UnitCanAttack("player", "targettarget")) then |
||
418 | SA_Debug("friendly target, assisting", 1); |
||
419 | AssistUnit("target"); |
||
420 | else |
||
421 | -- supresses annoyance when attacking and ally has no target |
||
422 | if (assistAction) then |
||
423 | SA_Debug("aborting action, supressing annoyance", 1); |
||
424 | return; |
||
425 | end |
||
426 | end |
||
427 | end |
||
428 | |||
429 | return old_UseAction(m1, m2, m3); |
||
430 | end |
||
431 | |||
432 | ------------------------------------------------------------------------------- |
||
433 | -- implements shift click assist |
||
434 | -- should work with ANYWHERE where you can select unit |
||
435 | ------------------------------------------------------------------------------- |
||
436 | |||
437 | local SA_PREV_ASSIST_TIME = 0; |
||
438 | function SA_PlayerTargetChanged() |
||
439 | TARGET_CHANGED = true; |
||
440 | |||
441 | -- target lost |
||
442 | if (not UnitExists("target")) then |
||
443 | SA_ResetWarning(); |
||
444 | if (not SA_RUNNING) then |
||
445 | PREVIOUS_ASSISTS = {}; |
||
446 | PREVIOUS_NEAREST = false; |
||
447 | end |
||
448 | SA_List_Refresh(); |
||
449 | return; -- fixes issue when we have target already (fires two events, on lost and on gain) |
||
450 | end |
||
451 | |||
452 | -- asisst by unit selection & modifier key |
||
453 | if ( (IsShiftKeyDown() and SA_OPTIONS.AssistKeyMode==1) or |
||
454 | (IsControlKeyDown() and SA_OPTIONS.AssistKeyMode==2) or |
||
455 | (IsAltKeyDown() and SA_OPTIONS.AssistKeyMode==3) ) |
||
456 | then |
||
457 | -- this time check (hoax) removes looping, when selecting player that has friendly taget. Without this |
||
458 | -- it goes there and starts to assist him |
||
459 | -- todo: this can be done better .. if we check what's the target! |
||
460 | |||
461 | local now = time(); |
||
462 | local dif = now - SA_PREV_ASSIST_TIME; |
||
463 | if (dif == 0) then return; end |
||
464 | SA_PREV_ASSIST_TIME = now; |
||
465 | |||
466 | if (UnitCanAssist("player", "target") and |
||
467 | UnitCanAttack("player", "targettarget")) |
||
468 | then |
||
469 | SAMsgFrame:AddMessage("Assisting "..UnitName("target"), 0.0, 0.8, 0.0, 1, 2.5); |
||
470 | AssistUnit("target"); |
||
471 | SA_PostAssist(); |
||
472 | else |
||
473 | if (UnitExists("target") and not UnitIsDead("target")) then |
||
474 | UIErrorsFrame:AddMessage("Unable to assist", 1.0, 0, 0, 1.0, 1.5); |
||
475 | end |
||
476 | end |
||
477 | end |
||
478 | |||
479 | SA_List_Refresh(); |
||
480 | end |
||
481 | |||
482 | ------------------------------------------------------------------------------ |
||
483 | -- credits to popup assist author, refactored a bit tough :) |
||
484 | ------------------------------------------------------------------------------ |
||
485 | |||
486 | local old_UnitPopup_OnClick = UnitPopup_OnClick; |
||
487 | function UnitPopup_OnClick(index) |
||
488 | local dropdownFrame = getglobal(UIDROPDOWNMENU_INIT_MENU); |
||
489 | local unit = dropdownFrame.unit; |
||
490 | if ( this.value == "PULLER" ) then |
||
491 | SA_SetPuller(unit); |
||
492 | end |
||
493 | return old_UnitPopup_OnClick(index); |
||
494 | end |
||
495 | |||
496 | ------------------------------------------------------------------------------ |
||
497 | -- param: unit - ID to be set as puller |
||
498 | -- set unit as puller |
||
499 | ------------------------------------------------------------------------------ |
||
500 | |||
501 | function SA_SetPuller(unit) |
||
502 | if (UnitIsFriend(unit, "player")) then |
||
503 | local candidates,_ = SA_GetCandidates(true); |
||
504 | -- check that current puller exists in party, if not clear |
||
505 | if (not candidates[UnitName(unit)]) then |
||
506 | SAMsgFrame:AddMessage("Puller must be in party / raid", 1.0, 1.0, 1.0, 1, 3); |
||
507 | else |
||
508 | SA_OPTIONS["puller"] = UnitName(unit); |
||
509 | SAMsgFrame:AddMessage("The assigned puller is now "..SA_OPTIONS["puller"], 1.0, 1.0, 1.0, 1, 1.5); |
||
510 | end |
||
511 | else |
||
512 | SA_OPTIONS["puller"] = nil; |
||
513 | SAMsgFrame:AddMessage("Assigned puller cleared", 1.0, 1.0, 1.0, 1, 1.5); |
||
514 | end |
||
515 | -- just incase that option window is visible, update the text |
||
516 | SA_Options_UpdatePullerText(); |
||
517 | end |
||
518 | |||
519 | ------------------------------------------------------------------------------ |
||
520 | -- credits to mozz |
||
521 | -- this is used to disable some anying unit deselect sounds while assisting |
||
522 | -- (we have to clear the selected unit in some cases) |
||
523 | ------------------------------------------------------------------------------ |
||
524 | |||
525 | local old_PlaySound = PlaySound; |
||
526 | function PlaySound(name) |
||
527 | if (not SA_RUNNING) then |
||
528 | return old_PlaySound(name); |
||
529 | end |
||
530 | if (name ~= "INTERFACESOUND_LOSTTARGETUNIT" and name ~= "igCharacterNPCSelect" ) then |
||
531 | return old_PlaySound(name); |
||
532 | end |
||
533 | end |
||
534 | |||
535 | ------------------------------------------------------------------------------ |
||
536 | -- main method, this is called when smartassist does its thing |
||
537 | ------------------------------------------------------------------------------ |
||
538 | |||
539 | function DoSmartAssist() |
||
540 | SA_RUNNING = true; |
||
541 | -- if has friendly target, straight assist it |
||
542 | local f = true; |
||
543 | if (UnitExists("target")) then |
||
544 | if (not UnitCanAttack("target", "player") and not (UnitPlayerControlled("target") and UnitFactionGroup("player")~=UnitFactionGroup("target"))) then |
||
545 | if (SA_OPTIONS.VerboseAssist) then |
||
546 | SA_Verbose("Assisting selected player "..UnitName("target")); |
||
547 | end |
||
548 | AssistUnit("target"); |
||
549 | f = false; |
||
550 | end |
||
551 | end |
||
552 | if (f) then |
||
553 | FindTarget(true, false); |
||
554 | end |
||
555 | SA_RUNNING = false; |
||
556 | SA_PostAssist(); |
||
557 | SA_List_Refresh(); |
||
558 | end |
||
559 | |||
560 | ------------------------------------------------------------------------------ |
||
561 | -- post assist logic (after target has been selected) |
||
562 | ------------------------------------------------------------------------------ |
||
563 | |||
564 | function SA_PostAssist() |
||
565 | -- auto-assist, disable if certain modifier key is down |
||
566 | if ( ((not IsShiftKeyDown() and SA_OPTIONS.DisableAutoCastKeyMode==1) or |
||
567 | (not IsControlKeyDown() and SA_OPTIONS.DisableAutoCastKeyMode==2) or |
||
568 | (not IsAltKeyDown() and SA_OPTIONS.DisableAutoCastKeyMode==3)) and |
||
569 | UnitExists("target") and |
||
570 | UnitAffectingCombat("target") ) |
||
571 | then |
||
572 | if (SA_OPTIONS.AutoAssist and SA_OPTIONS.AutoAssistName) |
||
573 | then |
||
574 | SA_Debug("Target already in combat, assisting with "..SA_OPTIONS.AutoAssistName, 3); |
||
575 | CastSpellByName(SA_OPTIONS.AutoAssistName); -- casts highest level |
||
576 | end |
||
577 | if ((SA_OPTIONS.AutoPetAttack and UnitExists("pet") and not UnitIsDead("pet") and not UnitExists("pettarget")) or |
||
578 | (SA_OPTIONS.AutoPetAttack and UnitExists("pet") and not UnitIsDead("pet") and SA_OPTIONS.AutoPetAttackBusy)) |
||
579 | then |
||
580 | SA_Debug("Attacking with pet", 3); |
||
581 | PetAttack(); |
||
582 | end |
||
583 | end |
||
584 | -- visual alert, if needed |
||
585 | SA_ShowWarning(); |
||
586 | end |
||
587 | |||
588 | ------------------------------------------------------------------------------ |
||
589 | -- receives all emote events, implements assist by emote |
||
590 | ------------------------------------------------------------------------------ |
||
591 | |||
592 | function SA_EmoteAssist(arg1) |
||
593 | if (not SA_OPTIONS.AssistOnEmote) then |
||
594 | return; |
||
595 | end |
||
596 | -- todo idea: hitting assist key should go back to previous target (if possible) |
||
597 | -- parse emotes |
||
598 | for k,v in ASSIST_EMOTES do |
||
599 | local sp, ep, name = string.find(arg1, "(%w+) "..v); |
||
600 | if (name ~= nil) then |
||
601 | SA_Debug("assist emote detected, name="..name, 1); |
||
602 | end |
||
603 | -- if name exists, we have a match |
||
604 | if name and name ~= EMOTE_OWN then |
||
605 | local candidates,_ = SA_GetCandidates(true); |
||
606 | if (candidates[name]~=nil) then |
||
607 | SAMsgFrame:AddMessage("Assisting "..name, 0.0, 0.8, 0.0, 1, 2.5); |
||
608 | AssistUnit(candidates[name].unitId); |
||
609 | return true; |
||
610 | end |
||
611 | SA_Debug("not assisting "..name..", not in party/raid", 1); |
||
612 | end |
||
613 | end |
||
614 | end |
||
615 | |||
616 | function SA_UnitHealth(arg1) |
||
617 | -- flag is used to reduce overheading, this gets called a lot .. |
||
618 | if (not PASSIVE_WARN_FLAG) then |
||
619 | return; |
||
620 | end |
||
621 | if (arg1=="target") then |
||
622 | SA_ResetWarning(); |
||
623 | -- if someone else attacked the target, show another text saying its ok to start blasting |
||
624 | -- this is not idiot proof check, seems to work fine tough |
||
625 | if (not UnitIsUnit("targettarget", "pet") and not UnitIsUnit("targettarget", "player")) then |
||
626 | SA_Debug("Someone else has attacked our target, show notification", 4); |
||
627 | SA_AggroedTargetMsg(); |
||
628 | else |
||
629 | SA_Debug("player has most likelly attacked the target", 4); |
||
630 | -- todo: perhaps send pull message to party? |
||
631 | end |
||
632 | end |
||
633 | -- todo idea: we could watch other players health here and suggest assist |
||
634 | end |
||
635 | |||
636 | ----------------------------------------------------------------- |
||
637 | -- show warning if target is not in combat and feature is enabled |
||
638 | ----------------------------------------------------------------- |
||
639 | function SA_ShowWarning() |
||
640 | if (not UnitAffectingCombat("target") and UnitExists("target") and |
||
641 | UnitName("targettarget")==nil and SA_OPTIONS.VisualWarning and |
||
642 | not UnitPlayerControlled("target")) |
||
643 | then |
||
644 | PASSIVE_WARN_FLAG = true; |
||
645 | SAWarningFrame:Show(); |
||
646 | return true; |
||
647 | end |
||
648 | end |
||
649 | |||
650 | function SA_AggroedTargetMsg() |
||
651 | -- the old msg was annoying as hell, replaced with SCT message, only if available |
||
652 | -- todo: add as option! |
||
653 | if (SCT_Display) then |
||
654 | SCT_Display(message, COLOR_DEFAULT); |
||
655 | end |
||
656 | end |
||
657 | |||
658 | function SA_ResetWarning() |
||
659 | PASSIVE_WARN_FLAG = false; |
||
660 | SAWarningFrame:Hide(); |
||
661 | end |
||
662 | |||
663 | ------------------------------------------------------------- |
||
664 | -- get distance unit is in, 0 if out of range |
||
665 | -- this funnly looking nesting makes minimal calls to api. |
||
666 | -- ie. No need to check for closer ranges if unit is not even |
||
667 | -- in 28yard range |
||
668 | ------------------------------------------------------------- |
||
669 | |||
670 | -- TODO: NOT USED ATM! |
||
671 | |||
672 | function SA_GetDistance(unit) |
||
673 | if (CheckInteractDistance(unit, 4)) then |
||
674 | if (CheckInteractDistance(unit, 3)) then |
||
675 | if (CheckInteractDistance(unit, 2)) then |
||
676 | if (CheckInteractDistance(unit, 1)) then |
||
677 | return 1; |
||
678 | end |
||
679 | return 2; |
||
680 | end |
||
681 | return 3; |
||
682 | end |
||
683 | return 4; |
||
684 | end |
||
685 | return -1; |
||
686 | end |
||
687 | |||
688 | ----------------------------------------------------------------------------------------- |
||
689 | -- construct and return one candidate |
||
690 | -- if unit has invalid flag it should not be used because it does not contain all fields |
||
691 | -- foexample pets that are very far away do not have all fields present |
||
692 | ----------------------------------------------------------------------------------------- |
||
693 | |||
694 | function SA_GetCandidate(unit, i) |
||
695 | local candidate = {}; |
||
696 | candidate["unitName"] = UnitName(unit..i); |
||
697 | candidate["unitId"] = unit..i; |
||
698 | candidate["target"] = unit..i.."target"; |
||
699 | candidate["health"] = ceil( UnitHealth( unit..i ) / UnitHealthMax( unit..i ) * 100 ); |
||
700 | candidate["class"] = UnitClass(unit..i); |
||
701 | candidate["dead"] = UnitIsDead(unit..i); |
||
702 | if (string.find(unit, "pet") ~= nil) then |
||
703 | candidate["pet"] = true; |
||
704 | end |
||
705 | if (candidate.unitName == nil) then |
||
706 | --SA_DebugCandidate(candidate); |
||
707 | candidate["invalid"] = true; |
||
708 | end |
||
709 | if (candidate.class == nil) then |
||
710 | --SA_DebugCandidate(candidate); |
||
711 | candidate["invalid"] = true; |
||
712 | end |
||
713 | if (candidate.health == nil) then |
||
714 | --printInfo("Problem with smartassist; unit health is unknown!"); |
||
715 | candidate["invalid"] = true; |
||
716 | SA_DebugCandidate(candidate); |
||
717 | end |
||
718 | return candidate; |
||
719 | end |
||
720 | |||
721 | function SA_GetPlayerAsCandidate() |
||
722 | local candidate = {}; |
||
723 | candidate["unitName"] = UnitName("player"); |
||
724 | candidate["unitId"] = "player"; |
||
725 | candidate["target"] = "target"; |
||
726 | candidate["health"] = ceil( UnitHealth( "player" ) / UnitHealthMax( "player" ) * 100 ); |
||
727 | candidate["class"] = UnitClass("player"); |
||
728 | candidate["dead"] = UnitIsDead("player"); |
||
729 | return candidate; |
||
730 | end |
||
731 | |||
732 | ------------------------------------------------------------------ |
||
733 | -- return iteration info: is this party or raid, how many members |
||
734 | ------------------------------------------------------------------ |
||
735 | |||
736 | function SA_GetIterInfo() |
||
737 | local mode, members = nil; |
||
738 | if (GetNumRaidMembers()>0) then |
||
739 | mode = "raid"; |
||
740 | members = GetNumRaidMembers(); |
||
741 | elseif (GetNumPartyMembers()>0) then |
||
742 | mode = "party"; |
||
743 | members = GetNumPartyMembers(); |
||
744 | end |
||
745 | return mode, members; |
||
746 | end |
||
747 | |||
748 | ------------------------------------------------------------------ |
||
749 | -- converts candidate list to map version |
||
750 | -- this is used in some places where we need list AND map versions |
||
751 | ------------------------------------------------------------------ |
||
752 | |||
753 | function SA_ConvertCandidateListToMap(candidates) |
||
754 | local map = {}; |
||
755 | for k,v in candidates do |
||
756 | map[v.unitName] = v; |
||
757 | end |
||
758 | return map; |
||
759 | end |
||
760 | |||
761 | ------------------------------------------------------------- |
||
762 | -- params: if map is true then as a map where key is unitName |
||
763 | -- if map is false then as list |
||
764 | -- return list of possible assistable units |
||
765 | ------------------------------------------------------------- |
||
766 | |||
767 | local UNIQUE_WARNED = false; |
||
768 | function SA_GetCandidates(map) |
||
769 | local candidates = {}; |
||
770 | local mode, members = SA_GetIterInfo(); -- got stack overflow once here |
||
771 | local pullerAdded = false; |
||
772 | |||
773 | -- for soloing and party, add our pet |
||
774 | if (UnitExists("pet")) then |
||
775 | local op = SA_GetCandidate("pet", ""); |
||
776 | if (not op.invalid and not op.dead) then |
||
777 | -- fixes problem when there are multiple pets with same name |
||
778 | if (SA_OPTIONS["puller"] == op.unitName) then |
||
779 | pullerAdded = true; |
||
780 | end |
||
781 | |||
782 | if (map) then |
||
783 | candidates[op.unitName] = op; |
||
784 | else |
||
785 | table.insert(candidates, op); |
||
786 | end |
||
787 | end |
||
788 | end |
||
789 | |||
790 | -- if no party/raid, abort here |
||
791 | if (members==nil) then return candidates, 0; end; |
||
792 | |||
793 | local myname = UnitName("player"); |
||
794 | |||
795 | for i = 1, members do |
||
796 | local candidate = SA_GetCandidate(mode, i); |
||
797 | -- do not add invalid, dead or myself |
||
798 | if (not candidate.invalid and not candidate.dead and candidate["unitName"] ~= myname) then |
||
799 | if (map) then |
||
800 | candidates[candidate.unitName] = candidate; |
||
801 | else |
||
802 | table.insert(candidates, candidate); |
||
803 | end |
||
804 | |||
805 | -- add pet to list if has one |
||
806 | if (UnitExists(mode.."pet"..i)) then |
||
807 | local pcandidate = SA_GetCandidate(mode.."pet", i); |
||
808 | if (not pcandidate.invalid and not pcandidate.dead) then |
||
809 | -- hotfix for unique pet puller problem |
||
810 | -- players are unique by server so we don't have to worry about them |
||
811 | if (SA_OPTIONS["puller"] == pcandidate.unitName and pullerAdded) then |
||
812 | if (not UNIQUE_WARNED) then |
||
813 | printInfo("SMARTASSIST PROBLEM: Puller is not unique!"); |
||
814 | UNIQUE_WARNED = true; |
||
815 | end |
||
816 | else |
||
817 | if (map) then |
||
818 | candidates[pcandidate.unitName] = pcandidate; |
||
819 | else |
||
820 | table.insert(candidates, pcandidate); |
||
821 | end |
||
822 | |||
823 | -- if added puller unit, set flag that no other unit with same name can be added |
||
824 | if SA_OPTIONS["puller"] == pcandidate.unitName then |
||
825 | pullerAdded = true; |
||
826 | end |
||
827 | end |
||
828 | end |
||
829 | end |
||
830 | end |
||
831 | end |
||
832 | |||
833 | return candidates, members; |
||
834 | end |
||
835 | |||
836 | ------------------------------------------------------------------------------ |
||
837 | -- if we have enabled filtering targets, remove those candidates who have |
||
838 | -- something else. It was much more convient to implement this way rather |
||
839 | -- than to modify get candidates and gazillion other places |
||
840 | ------------------------------------------------------------------------------ |
||
841 | |||
842 | function SA_FilterCandidates(candidates, map) |
||
843 | local filtered = {}; |
||
844 | for key,candidate in candidates do |
||
845 | if (UnitExists(candidate.target) and string.find(string.lower(UnitName(candidate.target)), SA_OPTIONS.Filter)) then |
||
846 | if (map) then |
||
847 | filtered[key] = candidate; |
||
848 | else |
||
849 | table.insert(filtered, candidate); |
||
850 | end |
||
851 | end |
||
852 | end |
||
853 | return filtered; |
||
854 | end |
||
855 | |||
856 | function SA_FilterCandidatesByDistance(candidates, map) |
||
857 | --debugprofilestart(); |
||
858 | |||
859 | local filtered = {}; |
||
860 | for key,candidate in candidates do |
||
861 | if (CheckInteractDistance(candidate.unitId, 4)) then |
||
862 | if (map) then |
||
863 | filtered[key] = candidate; |
||
864 | else |
||
865 | table.insert(filtered, candidate); |
||
866 | end |
||
867 | end |
||
868 | end |
||
869 | |||
870 | --SA_Debug("filtering by distance took "..debugprofilestop().." ms", 1); |
||
871 | return filtered; |
||
872 | end |
||
873 | |||
874 | ------------------------------------------------------------------------------ |
||
875 | -- sort method for candidates |
||
876 | ------------------------------------------------------------------------------ |
||
877 | |||
878 | local detected_bug = false; |
||
879 | function SA_SortCandidate(a, b, members) |
||
880 | -- TODO: INVESTIGATE, but HOW? |
||
881 | -- this is added to debug odd behaviour in molten core where crash occured (b was nil, crashed at priorize health) |
||
882 | -- OKAY: found out the cause, if there are two units with same name (pet. ie. Cat) and your own is set as puller, |
||
883 | -- this should be fixed in getCandidates but is UNTESTED, remove this when tested |
||
884 | -- Update 20.6.2006 - still bugs on VERY rare occasions! |
||
885 | if (detected_bug==false and (a==nil or b==nil)) then |
||
886 | printInfo("?? BUG IN SMARTASSIST:"); |
||
887 | local cand,_ = SA_GetCandidates(false); |
||
888 | local old_state = SA_OPTIONS.Debug; |
||
889 | SA_OPTIONS.Debug = true; |
||
890 | SA_DebugCandidates(cand); |
||
891 | SA_OPTIONS.Debug = old_state; |
||
892 | detected_bug = true; |
||
893 | end |
||
894 | |||
895 | -- priority health always first |
||
896 | -- TODO: DOES NOT TAKE ACCOUNT THE MEMBERS > n DISABLING !!! <----------------xxxxxxxxxxxxxxxxxxxxx---------------xxxxxxxxxxxxxxxxxxxx------------xxxxxxxxxxxxxx |
||
897 | -- SHOULD DO NOW! TESTING!!! xxxxxxxxxxxxxxx |
||
898 | local priorize = SA_OPTIONS.PriorizeHealth; |
||
899 | if (members > SA_OPTIONS.DisableSliderValue and SA_OPTIONS.DisablePriorityHealth) then |
||
900 | priorize = false; |
||
901 | end |
||
902 | if (priorize) then |
||
903 | if (a.health < SA_OPTIONS.PriorizeHealthValue or b.health < SA_OPTIONS.PriorizeHealthValue) |
||
904 | then |
||
905 | return a.health < b.health; |
||
906 | end |
||
907 | end |
||
908 | |||
909 | -- depriorize players having passive target |
||
910 | if (SA_PASSIVECACHE[a.unitName] and not SA_PASSIVECACHE[b.unitName]) then return false; end; |
||
911 | if (SA_PASSIVECACHE[b.unitName] and not SA_PASSIVECACHE[a.unitName]) then return true; end; |
||
912 | |||
913 | -- our puller should be always top priority |
||
914 | if (a.unitName == SA_OPTIONS["puller"]) then return true; end |
||
915 | if (b.unitName == SA_OPTIONS["puller"]) then return false; end |
||
916 | |||
917 | -- priorize players whose target is marked |
||
918 | if (SA_MARKEDCACHE[a.unitName] and not SA_MARKEDCACHE[b.unitName]) then return true; end; |
||
919 | if (SA_MARKEDCACHE[b.unitName] and not SA_MARKEDCACHE[a.unitName]) then return false; end; |
||
920 | |||
921 | -- de-priorize pets |
||
922 | if (a.pet and not b.pet) then return false; end; |
||
923 | if (b.pet and not a.pet) then return true; end; |
||
924 | |||
925 | -- CT RaidAssist support, if we have main tanks set in CT RaidAssist prefer them |
||
926 | if (CT_RA_MainTanks ~= nil) then |
||
927 | local a_tank, b_tank = false; |
||
928 | for _,v in CT_RA_MainTanks do |
||
929 | if (a.unitName == v) then a_tank=true; end |
||
930 | if (b.unitName == v) then b_tank=true; end |
||
931 | end |
||
932 | -- if both are CTRA tanks, sort by priority (mt first!) -- TODO: perhaps put OT first ? |
||
933 | if (a_tank and b_tank) then |
||
934 | return SA_TableIndex(CT_RA_MainTanks, a.unitName) < SA_TableIndex(CT_RA_MainTanks, b.unitName); |
||
935 | end |
||
936 | |||
937 | if (a_tank) then return true; end |
||
938 | if (b_tank) then return false; end |
||
939 | end |
||
940 | |||
941 | -- if we have same class, sort secondary by health |
||
942 | -- TODO: add option to disable this and use alphabetic order instead (should keep the list more stable) |
||
943 | -- perhaps use alphabetic order when in groups larger than > n |
||
944 | -- 20.6.2006 - testing out disabling if larger than > n |
||
945 | if (a.class == b.class) then |
||
946 | if (priorize) then |
||
947 | return a.health < b.health; |
||
948 | else |
||
949 | return a.unitName < b.unitName; |
||
950 | end |
||
951 | end |
||
952 | |||
953 | -- and last, teh normal case where sorted by class |
||
954 | return SA_TableIndex(SA_OPTIONS.ClassOrder, a.class) < SA_TableIndex(SA_OPTIONS.ClassOrder, b.class); |
||
955 | end |
||
956 | |||
957 | ------------------------------------------------------------------------------ |
||
958 | -- Makes sure that the puller is in the party and if not clear / set to pet. |
||
959 | -- This gets called everytime assist is used |
||
960 | ------------------------------------------------------------------------------ |
||
961 | |||
962 | function SA_RefreshPuller(candidatelist) |
||
963 | -- check that current puller exists in party, if not clear |
||
964 | local candidates = SA_ConvertCandidateListToMap(candidatelist); |
||
965 | if (SA_OPTIONS["puller"]~=nil and candidates[SA_OPTIONS["puller"]]==nil) then |
||
966 | SA_Debug("Puller does not exist in candidates, clearing", 3); |
||
967 | SA_OPTIONS["puller"]=nil; |
||
968 | end |
||
969 | |||
970 | -- if there is no puller and we have pet, set it as one |
||
971 | if (SA_OPTIONS["puller"]==nil and UnitExists("pet") and not UnitIsDead("pet")) then |
||
972 | SA_Debug("no puller set, pet exists -> setting it as one", 3); |
||
973 | SA_OPTIONS["puller"] = UnitName("pet"); |
||
974 | end |
||
975 | end |
||
976 | |||
977 | ------------------------------------------------------------------------------------------ |
||
978 | -- params: |
||
979 | -- allowNearest = allow fallback to target nearest |
||
980 | -- recursive = is this recursive call, if it is we cannot add outside targets to skip list |
||
981 | -- Todo: better way for recursive? |
||
982 | ------------------------------------------------------------------------------------------ |
||
983 | |||
984 | function FindTarget(allowNearest, recursive) |
||
985 | -- reset PREVIOUS_FALLBACK, store real value in local variable. Easier this way than to handle all returns ... |
||
986 | local previous_fallback = PREVIOUS_FALLBACK; |
||
987 | PREVIOUS_FALLBACK = false; |
||
988 | |||
989 | local candidates, members = SA_GetCandidates(false); |
||
990 | |||
991 | -- reset order if target is kept over 3s |
||
992 | if (time() - PREVIOUS_ASSIST_TIME > 3 and SA_OPTIONS.PauseResetsOrder) then |
||
993 | SA_Debug("over 3s since last assist, reseting PREVIOUS_ASSISTS", 2); |
||
994 | PREVIOUS_ASSISTS = {}; |
||
995 | PREVIOUS_ASSIST_TIME = time(); |
||
996 | end |
||
997 | |||
998 | -- allow targeting nearest attacking again |
||
999 | if (time() - PREVIOUS_NEAREST_TIME > 5) then |
||
1000 | SA_Debug("over 5s since last assist, reseting PREVIOUS_NEAREST", 2); |
||
1001 | PREVIOUS_NEAREST = false; |
||
1002 | PREVIOUS_NEAREST_TIME = time(); |
||
1003 | end |
||
1004 | |||
1005 | if (SA_OPTIONS.Filter) then |
||
1006 | SA_Debug("filtering with "..tostring(SA_OPTIONS.Filter), 2); |
||
1007 | candidates = SA_FilterCandidates(candidates, false); |
||
1008 | end |
||
1009 | |||
1010 | -- filter out candidates out of range if assisting only members nearby |
||
1011 | if (SA_OPTIONS.AssistOnlyNearest) then |
||
1012 | candidates = SA_FilterCandidatesByDistance(candidates, false); |
||
1013 | end |
||
1014 | |||
1015 | SA_RefreshPuller(candidates); |
||
1016 | |||
1017 | -- if we should not assist anyone from raid without puller, clear the candidates list |
||
1018 | if (SA_OPTIONS.DisableAssistWithoutPuller and SA_OPTIONS["puller"]==nil) then |
||
1019 | SA_Debug("DisableAssistWithoutPuller and no puller -> clearing candidates list", 2); |
||
1020 | candidates = {}; |
||
1021 | end |
||
1022 | |||
1023 | -- try to target nearest before going to assist from raid, note that this is different from allowNearest |
||
1024 | if (SA_OPTIONS.CheckNearest and not PREVIOUS_NEAREST and not recursive) then |
||
1025 | --local pre_valid = isValidTarget("target"); |
||
1026 | -- okei, eli jos target nearest ei vaiha targettia niin se tuo myöhempi else palauttaa edellisen assistauksen targetin -> bug bug! |
||
1027 | TARGET_CHANGED = false; |
||
1028 | TargetNearestEnemy(); |
||
1029 | SA_Debug("check nearest got = "..tostring(UnitName("target")),1); |
||
1030 | local valid = isValidTarget("target") and UnitAffectingCombat("target"); |
||
1031 | if (SA_OPTIONS.NearestMustBePvP) then |
||
1032 | if (not UnitPlayerControlled("target")) then |
||
1033 | valid = false; |
||
1034 | end |
||
1035 | end |
||
1036 | if (SA_OPTIONS.NearestMustBeTargetting) then |
||
1037 | --if (UnitIsUnit("targettarget", "player")) then |
||
1038 | if (UnitName("targettarget") ~= UnitName("player")) then |
||
1039 | valid = false; |
||
1040 | end |
||
1041 | end |
||
1042 | if (valid) then |
||
1043 | SA_Debug("*** found good target from check nearest target="..tostring(UnitName("target")),1); |
||
1044 | if (SA_OPTIONS.VerboseNearest) then |
||
1045 | SA_Verbose("Targeted nearest", ALERT_COLOR); |
||
1046 | end |
||
1047 | PREVIOUS_NEAREST = true; |
||
1048 | return; |
||
1049 | else |
||
1050 | if (TARGET_CHANGED) then |
||
1051 | SA_Debug("invalid check nearest, restored target = "..tostring(UnitName("target")),1); |
||
1052 | TargetLastTarget(); |
||
1053 | end |
||
1054 | end |
||
1055 | end |
||
1056 | PREVIOUS_NEAREST = false; |
||
1057 | |||
1058 | -- store table size to variable because iterating it multiple times is no good |
||
1059 | table.sort(candidates, function(a,b) return SA_SortCandidate(a,b,members) end); |
||
1060 | |||
1061 | for _,candidate in candidates do |
||
1062 | -- this is ingenious loop which determines if current candidate target has been targetted on previous assists |
||
1063 | -- not a idiot proof check since previous party members might have changed target since then, but works amazingly well |
||
1064 | -- atleast preventing situation where multiple members have same target and you press assist key multiple times |
||
1065 | |||
1066 | -- 18.1.2006 - changed to show already targetted msg to test if its futile |
||
1067 | -- 21.1.2006 - this is triggered _multiple_ times per assist on some occasions, seems to be when there is only one target.. |
||
1068 | |||
1069 | local previously_targetted = false; |
||
1070 | for assisted,_ in PREVIOUS_ASSISTS do |
||
1071 | if (UnitExists(assisted)) then |
||
1072 | if (UnitIsUnit(candidate.target, assisted.."target")) then |
||
1073 | --SA_Debug("** already targetted once "..tostring(UnitName(assisted))); |
||
1074 | previously_targetted = true; |
||
1075 | break; |
||
1076 | end |
||
1077 | end |
||
1078 | end |
||
1079 | |||
1080 | -- if current target is same as our candidate, consider it "assisted" (in other words: skip it) |
||
1081 | -- added 24.1.2006 - this should make cycling trough enemies work more smoothly |
||
1082 | if (UnitExists("target") and UnitIsUnit(candidate.target, "target")) then |
||
1083 | SA_Debug(tostring(candidate.unitId).." has same target as we ("..tostring(UnitName("target")).."), consider this unit as assisted", 3); |
||
1084 | PREVIOUS_ASSISTS[candidate.unitId] = true; |
||
1085 | end |
||
1086 | |||
1087 | -- test each candidate, skips previously assisted UNLESS it has health below critical value |
||
1088 | local priority_health = candidate.health < SA_OPTIONS.PriorizeHealthValue and SA_OPTIONS.PriorizeHealth; |
||
1089 | |||
1090 | if (members > SA_OPTIONS.DisableSliderValue and SA_OPTIONS.DisablePriorityHealth) then |
||
1091 | priority_health = false; |
||
1092 | end |
||
1093 | |||
1094 | if ( (PREVIOUS_ASSISTS[candidate.unitId]==nil or priority_health) and (not previously_targetted) ) |
||
1095 | then |
||
1096 | -- test if candidate (partyN, pet, raidN etc) has valid target |
||
1097 | if (UnitCanAssist("player", candidate.unitId) and isValidTarget(candidate.target)) |
||
1098 | then |
||
1099 | if (SA_OPTIONS.VerboseAssist) then |
||
1100 | if (priority_health) then |
||
1101 | SA_Verbose("Priority assisting "..candidate.unitName.." ("..candidate.health.."%)", COLOR_ALERT); |
||
1102 | else |
||
1103 | SA_Verbose("Assisting "..candidate.unitName); |
||
1104 | end |
||
1105 | end |
||
1106 | SA_Debug("Found a good target from "..candidate.unitName.." ("..candidate.unitId..")", 3); |
||
1107 | AssistUnit(candidate["unitId"]); |
||
1108 | PREVIOUS_ASSISTS[candidate.unitId] = true; |
||
1109 | return; |
||
1110 | end |
||
1111 | else |
||
1112 | SA_Debug("** Skipping "..candidate.unitName.." ("..candidate.unitId..")", 4); |
||
1113 | end |
||
1114 | end |
||
1115 | |||
1116 | -- if we have skiplist, now is good time to clear it and try again with all members, recursive call is only made once |
||
1117 | if (SA_TableSize(PREVIOUS_ASSISTS)>0 and not recursive) then |
||
1118 | SA_Debug("** Unable to assist anyone but we have skiplist ("..SA_TableSize(PREVIOUS_ASSISTS)..") -> clearing it and trying again..", 3); |
||
1119 | PREVIOUS_ASSISTS = {}; |
||
1120 | return FindTarget(allowNearest, true); |
||
1121 | end |
||
1122 | |||
1123 | -- we might had good target already when assist was used, if there are no other targets available we must exit now |
||
1124 | -- falling back to target nearest in that case would be idiotic |
||
1125 | -- 29.7.2006 - do not abort on good target if previously acquired target using fallback to nearest, |
||
1126 | -- this allows toggling targets using smartassist key like TAB key. |
||
1127 | if (isValidTarget("target") and not previous_fallback) then |
||
1128 | SA_Debug("had already good target", 3); |
||
1129 | return; |
||
1130 | end |
||
1131 | |||
1132 | if (SA_OPTIONS.FallbackTargetNearest and allowNearest) then |
||
1133 | if (SA_OPTIONS.DisableTargetNearest and members > SA_OPTIONS.DisableSliderValue) then |
||
1134 | if (SA_OPTIONS.VerboseUnableToAssist) then |
||
1135 | printInfo("Unable to assist anyone. Targetting nearest is suspended in groups this large."); |
||
1136 | end |
||
1137 | return; |
||
1138 | end |
||
1139 | if (SA_OPTIONS.VerboseUnableToAssist) then |
||
1140 | printInfo("Unable to assist anyone. Trying to target nearest enemy."); |
||
1141 | end |
||
1142 | TargetNearestEnemy(); |
||
1143 | if (not isValidTarget("target")) then |
||
1144 | ClearTarget(); |
||
1145 | else |
||
1146 | PREVIOUS_FALLBACK = true; |
||
1147 | SA_Debug("fallback to target nearest found good target", 2); |
||
1148 | end |
||
1149 | end |
||
1150 | end |