vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2  
3 TargetOfTarget: Keeps Target of Target shown below TargetFrame
4  
5 Copyright (c) 2005 Itchyban of Veeshan
6  
7 Version 2.1 Beta 1
8  
9 $Header: /usr/local/cvsroot/WoW/Interface/AddOns/TargetOfTarget/TargetOfTarget.lua,v 1.122 2005/05/30 17:43:33 jeff Exp $
10  
11  
12 ]]
13  
14  
15 ---
16 --- Where should the dead/ghost/corpse be done?
17 ---
18  
19 -------------------------
20 --- Versioning params ---
21 -------------------------
22  
23 ---
24 --- WARNING
25 ---
26 --- Checking this into CVS or RCS without -ko can screw this up for now
27 ---
28 --- (If you don't know what CVS or RCS are, you can ignore that)
29 ---
30  
31 local version_table_data = {};
32  
33 version_table_data.object = "1.0";
34 version_table_data.apps = {};
35 version_table_data.apps.TargetOfTarget = {};
36 version_table_data.apps.TargetOfTarget.params = "2.0";
37 version_table_data.apps.TargetOfTarget.lua_CVS = "$Revision: 1.122 $";
38 version_table_data.apps.TargetOfTarget.XML_CVS = "";
39  
40  
41  
42 -------------------------------------------------------
43 --- Check for existing globals before declaring any ---
44 -------------------------------------------------------
45  
46 local globals_to_check = {
47 "HoTT_Aggro_Callback",
48 "HoTT_Debug",
49 "HoTT_Display",
50 "HoTT_Display_Bar",
51 "HoTT_Display_Bar_OnEvent",
52 "HoTT_Display_Bar_OnUpdate",
53 "HoTT_Display_Bar_OnValueChanged",
54 "HoTT_Display_NameArea",
55 "HoTT_Display_NameArea_Text",
56 "HoTT_Display_OnClick",
57 "HoTT_Display_OnEvent",
58 "HoTT_Display_OnUpdate",
59 "HoTT_InputBoxTemplate",
60 "HoTT_InputBoxTemplateLeft",
61 "HoTT_InputBoxTemplateMiddle",
62 "HoTT_InputBoxTemplateRight",
63 "HoTT_MainFrame",
64 "HoTT_MainFrame_OnEvent",
65 "HoTT_MainFrame_OnLoad",
66 "HoTT_Params",
67 "HoTT_Params_FourDigitInputBox",
68 "HoTT_Params_Frame",
69 "HoTT_Params_Frame_AggroMessage_Changed",
70 "HoTT_Params_Frame_AggroMessage_EditBox",
71 "HoTT_Params_Frame_AggroText",
72 "HoTT_Params_Frame_AlertsText",
73 "HoTT_Params_Frame_Alignment_Changed",
74 "HoTT_Params_Frame_BuffsText",
75 "HoTT_Params_Frame_BuffsText0",
76 "HoTT_Params_Frame_BuffsText1",
77 "HoTT_Params_Frame_BuffsText2",
78 "HoTT_Params_Frame_ButtonCenter",
79 "HoTT_Params_Frame_ButtonCenterText",
80 "HoTT_Params_Frame_ButtonLeft",
81 "HoTT_Params_Frame_ButtonLeftText",
82 "HoTT_Params_Frame_ButtonRight",
83 "HoTT_Params_Frame_ButtonRightText",
84 "HoTT_Params_Frame_Cancel",
85 "HoTT_Params_Frame_CheckRefocus",
86 "HoTT_Params_Frame_ClearFocus",
87 "HoTT_Params_Frame_Defaults",
88 "HoTT_Params_Frame_DisplayInsetsText",
89 "HoTT_Params_Frame_DisplayLeft_Changed",
90 "HoTT_Params_Frame_DisplayLeft_EditBox",
91 "HoTT_Params_Frame_DisplayLeft_EditBoxLeft",
92 "HoTT_Params_Frame_DisplayLeft_EditBoxMiddle",
93 "HoTT_Params_Frame_DisplayLeft_EditBoxRight",
94 "HoTT_Params_Frame_DisplayRight_Changed",
95 "HoTT_Params_Frame_DisplayRight_EditBox",
96 "HoTT_Params_Frame_DisplayRight_EditBoxLeft",
97 "HoTT_Params_Frame_DisplayRight_EditBoxMiddle",
98 "HoTT_Params_Frame_DisplayRight_EditBoxRight",
99 "HoTT_Params_Frame_Drop0_Changed",
100 "HoTT_Params_Frame_Drop0_EditBox",
101 "HoTT_Params_Frame_Drop0_EditBoxLeft",
102 "HoTT_Params_Frame_Drop0_EditBoxMiddle",
103 "HoTT_Params_Frame_Drop0_EditBoxRight",
104 "HoTT_Params_Frame_Drop1_Changed",
105 "HoTT_Params_Frame_Drop1_EditBox",
106 "HoTT_Params_Frame_Drop1_EditBoxLeft",
107 "HoTT_Params_Frame_Drop1_EditBoxMiddle",
108 "HoTT_Params_Frame_Drop1_EditBoxRight",
109 "HoTT_Params_Frame_Drop2_Changed",
110 "HoTT_Params_Frame_Drop2_EditBox",
111 "HoTT_Params_Frame_Drop2_EditBoxLeft",
112 "HoTT_Params_Frame_Drop2_EditBoxMiddle",
113 "HoTT_Params_Frame_Drop2_EditBoxRight",
114 "HoTT_Params_Frame_DropsText",
115 "HoTT_Params_Frame_LoadValues",
116 "HoTT_Params_Frame_ObjectName_Changed",
117 "HoTT_Params_Frame_ObjectName_EditBox",
118 "HoTT_Params_Frame_ObjectName_EditBoxLeft",
119 "HoTT_Params_Frame_ObjectName_EditBoxMiddle",
120 "HoTT_Params_Frame_ObjectName_EditBoxRight",
121 "HoTT_Params_Frame_ObjectText",
122 "HoTT_Params_Frame_Okay",
123 "HoTT_Params_Frame_OnCancel",
124 "HoTT_Params_Frame_OnDefaults",
125 "HoTT_Params_Frame_OnOkay",
126 "HoTT_Params_Frame_PositioningText",
127 "HoTT_Params_Frame_Scale_Changed",
128 "HoTT_Params_Frame_Scale_EditBox",
129 "HoTT_Params_Frame_Scale_EditBoxLeft",
130 "HoTT_Params_Frame_Scale_EditBoxMiddle",
131 "HoTT_Params_Frame_Scale_EditBoxRight",
132 "HoTT_Params_Frame_ShowAlignmentFrame",
133 "HoTT_Params_Frame_Title",
134 "HoTT_Params_Frame_Title_Text",
135 "HoTT_Params_Frame_TextInsetsText",
136 "HoTT_Params_Frame_TextLeft_Changed",
137 "HoTT_Params_Frame_TextLeft_EditBox",
138 "HoTT_Params_Frame_TextLeft_EditBoxLeft",
139 "HoTT_Params_Frame_TextLeft_EditBoxMiddle",
140 "HoTT_Params_Frame_TextLeft_EditBoxRight",
141 "HoTT_Params_Frame_TextRight_Changed",
142 "HoTT_Params_Frame_TextRight_EditBox",
143 "HoTT_Params_Frame_TextRight_EditBoxLeft",
144 "HoTT_Params_Frame_TextRight_EditBoxMiddle",
145 "HoTT_Params_Frame_TextRight_EditBoxRight",
146 "HoTT_Set_XML_CVS",
147 "HoTT_Settings",
148 "HoTT_Settings_Unparsed",
149 "SLASH_TARGETOFTARGET_TOT1",
150 "SLASH_TARGETOFTARGET_TOT2",
151 "SLASH_TARGETOFTARGET_TOTOFF1",
152 "SLASH_TARGETOFTARGET_TOTON1",
153 "SlashCmdList[\"TARGETOFTARGET_TOT\"]",
154 "SlashCmdList[\"TARGETOFTARGET_TOTON\"]",
155 "SlashCmdList[\"TARGETOFTARGET_TOTOFF\"]",
156 }
157  
158 local slash_commands_to_check = {
159 "/tot",
160 "/toton",
161 "/totoff",
162 "/hott"
163 }
164  
165 local all_checked_globals = {};
166  
167 local k,v
168  
169 for k,v in pairs (globals_to_check) do
170 table.insert(all_checked_globals, v);
171 end
172  
173 for k,v in pairs (slash_commands_to_check) do
174 table.insert(all_checked_globals, v);
175 end
176  
177  
178  
179  
180  
181 local global_check_failures = nil;
182  
183 for k,v in pairs(globals_to_check) do
184  
185 if ( type(getglobal(v)) ~= "nil" ) then
186 if ( type(global_check_failures) == "nil" ) then
187 global_check_failures = {};
188 end
189 table.insert(global_check_failures, v);
190 end
191  
192 end
193  
194  
195  
196 ---
197 --- NOTE: This is not quite right yet, as slash_commands_to_check
198 --- somehow needs to be localized in the process
199 ---
200  
201  
202 ---
203 --- check_localized_slash_commands
204 ---
205  
206  
207 local function check_localized_slash_commands ()
208  
209 for k,v in pairs(slash_commands_to_check) do
210  
211 local scl_key, scl_value;
212  
213 for scl_key, scl_value in pairs(SlashCmdList) do
214  
215  
216 local idx = 1;
217 local slash_var = "SLASH_"..scl_key..idx;
218 local slash_cmd_string = getglobal(slash_var);
219  
220 repeat
221 if ( slash_cmd_string == v ) then
222 if ( type(global_check_failures) == "nil" ) then
223 global_check_failures = {};
224 end
225 table.insert(global_check_failures,
226 v .. "( == " .. slash_cmd_string .. " )");
227 end
228 idx = idx+1;
229 slash_var = "SLASH_"..scl_key..idx;
230 slash_cmd_string = getglobal(slash_var);
231  
232 until ( not slash_cmd_string )
233  
234 end
235  
236 end
237  
238 end
239  
240  
241 ---
242 --- "Safe" to declare globals now
243 --- at least we know what we are going to clobber!
244 ---
245  
246  
247  
248  
249 ---
250 --- Stray Global, here for clarity
251 ---
252 --- HoTT_Set_XML_CVS
253 ---
254  
255 --- Called <OnLoad> in the XML file
256 --- Should probably make sure that it "flows through" properly
257 --- before I start relying on it for anything
258  
259 function HoTT_Set_XML_CVS (version_string)
260 version_table_data.apps.TargetOfTarget.XML_CVS = version_string;
261  
262 return version_table_data.apps.TargetOfTarget.XML_CVS;
263 end
264  
265  
266 ---
267 --- HoTT_Patch_Warning
268 ---
269  
270 local function HoTT_Patch_Warning ()
271  
272 local patch_not_ok = (type(UnitIsVisible) ~= "function");
273  
274 if ( patch_not_ok ) then
275 message("This version requires patch 1.5.0, not present on this server. Enjoy this version on Test, but run the latest 1.x version here for now.");
276 end
277  
278 return patch_not_ok;
279 end
280  
281  
282 ---------------------------
283 --- Localizable Strings ---
284 ---------------------------
285  
286 local no_target_string = "";
287 local stunned_string = "(stunned)";
288 local undetermined_string = "(undetermined)";
289  
290 local aggro_message = "--- AGGRO --- AGGRO ---";
291  
292 local ras_format = "Duplicate UnitID for %s; %s, %s";
293  
294 local toton_string = "/toton";
295 local totoff_string = "/totoff";
296  
297 local loaded_string = "Itchy's TargetOfTarget AddOn loaded";
298 local loaded_variables_string = "TargetOfTarget SavedVariables settings restored"
299  
300 local shift_at_load_string = "TargetOfTarget [Shift] at load";
301 local invalid_settings_string = "TargetOfTarget invalid saved settings. Copied to HoTT_Settings_Unparsed";
302  
303 local on_string = "TargetOfTarget is now " .. GREEN_FONT_COLOR_CODE .. "ON";
304 local off_string = "TargetOfTarget is now OFF";
305  
306 local saved_format = "TargetOfTarget saved settings: %s";
307 local save_failed_format = "TargetOfTarget no settings to save for: %s";
308  
309 local restored_defaults = "TargetOfTarget restored default settings";
310 local restored_format = "TargetOfTarget restored settings: %s";
311 local restore_failed_format = "TargetOfTarget no settings to restore for: %s";
312  
313 local help_message = [[
314 Itchy's ToT
315  
316 /tot on -- enable ToT (or /toton)
317 /tot off -- disable ToT (or /totoff)
318  
319 /tot params -- brings up dialog to edit aggro message and position display.
320 /tot version -- displays file versions. Please report these with any bugs or suggestions.]];
321  
322 local help_ui = "/tot ui is obsolete. Use /tot params";
323  
324 local string_global_check_failed = "TargetOfTarget found possible " ..
325 RED_FONT_COLOR_CODE .. "global variable conflict" ..
326 FONT_COLOR_CODE_CLOSE .. " on loading. For details, /tot globals";
327  
328 local string_global_list = "TargetOfTarget global variable check list is in TargetOfTarget.lua, if you are curious."
329  
330 local string_global_check_explanation = "TargetOfTarget checks that the global variables it uses are not already in use when it loads. If they are already present, it is likely that another AddOn will conflict with TargetOfTarget, or you have two versions loading. The following variables were found in conflict:";
331 local string_global_variable_check_trailer = "If there are a lot of variables listed, it is likely that you have another copy of TargetOfTarget loading. Please check your AddOns."
332  
333 local string_assert_set_exists_fmt = "Change settings to %s, no set returned";
334 local string_assert_no_player = "Player name unexpectedly missing";
335 local string_assert_no_realm = "Realm name unexpectedly missing";
336  
337 local fmt_no_such_object = "%s does not exist in this UI. Use '/tot ui default' to restore defaults, or '/tot ui' to see other options";
338  
339 local string_no_ui_description = "(no description given)"
340  
341 local string_cleared_ui = "Cleared ToT UI parameters";
342 local string_default_ui_copied = "Copied default ToT UI parameters to your active set";
343  
344 local fmt_copied_ui = "Copied ToT UI parameters to your active set: %s -- %s";
345  
346 local fmt_no_such_ui = "No such ToT UI parameters, %s, available. For help, /tot ui";
347  
348 local string_help_cmd = "help";
349 local string_on_cmd = "on";
350 local string_off_cmd = "off";
351 local string_ui_cmd = "ui";
352 local string_version_cmd = "version";
353 local string_globals_cmd = "globals";
354 local string_params_cmd = "params"
355  
356 local string_unrecognized_cmd_help = "Unrecognized command -- try /tot help";
357  
358 local string_slash_tot = "/tot"
359 local string_slash_hott = "/hott"
360  
361 local ellipsis = "..."; -- Used as suffix in shortened names
362  
363 local fmt_ro_access = "No write access to %s permitted.";
364 local fmt_not_a_table = "%s not a table, passed as %s";
365  
366 local fmt_too_narrow = "Too narrow a window. %s set to previous value.";
367 local fmt_no_obj_edit = "%s does not exist in this UI. Previous value restored.";
368  
369  
370  
371 -----------------------------------------
372 --- Parameter list and default values ---
373 -----------------------------------------
374  
375  
376 --- Application defaults
377 ---
378 --- DO NOT CHANGE THESE HERE
379 ---
380 --- Use HoTT_Params handle to keep overrides in SavedVariables
381 ---
382  
383 local default_params = {};
384  
385 default_params.bar_unreliable_time = 0.500; -- time after which bar
386 -- is marked "unreliable"
387  
388 default_params.poll_timer_period = 0.400; -- check ToT/health this often
389 -- (Only if not getting events)
390  
391 default_params.aggro_message = aggro_message; -- or false or blank to disable
392  
393 default_params.ui = {};
394  
395 default_params.ui.display = {};
396  
397 default_params.ui.display.relative_to = "TargetFrameHealthBar";
398  
399 default_params.ui.display.scale = 1.0;
400  
401 default_params.ui.display.inset = {};
402  
403 default_params.ui.display.inset.left = -5;
404 default_params.ui.display.inset.right = -3;
405  
406  
407 default_params.ui.display.drops = {};
408  
409 default_params.ui.display.drops[0] = 12;
410 default_params.ui.display.drops[1] = 36;
411 default_params.ui.display.drops[2] = 58;
412  
413 default_params.ui.display.tot_name = {};
414  
415 default_params.ui.display.tot_name.min_width = 20; -- prevent dynamic
416 -- adjustment too small
417  
418 default_params.ui.display.tot_name.alignment = "CENTER";
419  
420 default_params.ui.display.tot_name.inset = {};
421  
422 default_params.ui.display.tot_name.inset.left = 6; -- padding for trimming
423 default_params.ui.display.tot_name.inset.right = 6; -- of strings to fit
424  
425  
426  
427 --------------------------------
428 --- Locking tables read-only ---
429 --------------------------------
430  
431 ---
432 --- format_table_key
433 ---
434  
435 local function format_table_key (table_string, k)
436  
437 local table_key;
438  
439 if ( type(k) == "string" ) then
440 table_key = string.format("%s.%s", table_string, k);
441 else
442 table_key = string.format("%s[%s]", table_string, tostring(k));
443 end
444  
445 return table_key;
446 end
447  
448  
449  
450  
451 ---
452 --- wrap_readonly
453 ---
454  
455 local function wrap_readonly (table1, table_name)
456  
457 if ( type(table_name) ~= "string" ) then
458 table_name = "";
459 end
460  
461 local wrapper = {};
462 local ro_mt = {};
463 ro_mt.__index = table1;
464 ro_mt.__newindex = function (t,k,v)
465 error(string.format(fmt_ro_access,
466 format_table_key(table_name, k)),
467 2);
468 end;
469  
470 setmetatable(wrapper, ro_mt);
471  
472 return wrapper;
473 end
474  
475  
476  
477 ---
478 --- wrap_readonly_recursively
479 ---
480  
481 local function wrap_readonly_recursively (table1, table_name, visited)
482  
483 local k,v;
484 local next_table_name;
485 local wrapper;
486  
487 if ( type(table_name) ~= "string" ) then
488 table_name = "";
489 end
490  
491 if ( not visited ) then
492 visited = {};
493 visited[table1] = true;
494 end
495  
496 for k,v in pairs(table1) do
497 if ( ( type(v) == "table" ) and
498 ( not visited[v] ) ) then
499 next_table_name = format_table_key(table_name, k);
500 table1[k] = wrap_readonly_recursively (v, next_table_name, visited)
501 end
502 end
503  
504 wrapper = wrap_readonly(table1, table_name);
505  
506 return wrapper;
507 end
508  
509  
510  
511 ---
512 --- Lock default_params
513 ---
514  
515 default_params = wrap_readonly_recursively(default_params, "default_params");
516  
517  
518  
519  
520  
521 -----------------------------
522 --- Local state variables ---
523 -----------------------------
524  
525 local hott_is_on; -- master
526  
527 local t_target_info = {}; -- the "target" for which ToT applies
528 local tot_target_info = {}; -- the ToT itself
529  
530 local player_name;
531 local realm_name;
532  
533 local last_tot; -- name of the last ToT for "AGGRO" messaging
534 local this_tot;
535  
536 local roster = {}; -- reverse lookup for UnitIDs
537 local roster_assertion_seen; -- so as to only show an error once
538  
539 local time_since_bar_update = 0; -- internal for bar coloring
540 local bar_set_unreliable = nil; -- internal for bar coloring
541  
542 local variables_loaded = false; -- Event has fired
543 local saved_variables_loaded = false; -- Event responded to completely
544  
545 local this_frame_tot; -- internals for detecting most target changes
546 local last_frame_tot;
547  
548 local poll_timer = 0; -- handles the cases events don't cover
549 local poll_timer_fired = false;
550  
551 local placed_names = {}; -- cache for name strings that fit
552  
553 local refocus_object = nil; -- Params frame object that needs focus
554  
555  
556  
557  
558  
559 ------------------------
560 --- Local parameters ---
561 ------------------------
562  
563  
564  
565 --- UnitIDs that generate UNIT_HEALTH events (health bar reliability check)
566  
567 local units_getting_events = {};
568  
569 units_getting_events.player = true;
570 units_getting_events.pet = true;
571  
572 for idx = 1, 4 do
573 units_getting_events[format("party%i",idx)] = true;
574 units_getting_events[format("partypet%i",idx)] = true;
575 end
576  
577 for idx = 1, 40 do
578 units_getting_events[format("raid%i",idx)] = true;
579 units_getting_events[format("raidpet%i",idx)] = true;
580 end
581  
582  
583  
584 --- Colors for bars and for text
585  
586 local colors = {};
587  
588 colors.bar = {};
589  
590 colors.bar.unreliable = { r = 1.00, g = 1.00, b = 1.00 };
591  
592 colors.name = {};
593  
594 colors.name.dead = { r = 0.50, g = 0.50, b = 0.50 };
595  
596 colors.name.normal = { r = NORMAL_FONT_COLOR.r,
597 g = NORMAL_FONT_COLOR.g,
598 b = NORMAL_FONT_COLOR.b };
599  
600 colors.name.unreliable = { r = 1.00, g = 1.00, b = 1.00 };
601  
602 colors.name.unreliable_no_unit = { r = 0.83, g = 0.83, b = 0.83 };
603  
604 colors.name.undetermined = { r = NORMAL_FONT_COLOR.r,
605 g = NORMAL_FONT_COLOR.g,
606 b = NORMAL_FONT_COLOR.b };
607  
608  
609  
610  
611 ----------------------------------------------------------------
612 --- Globals for pre-release testing/tuning without reloading ---
613 ----------------------------------------------------------------
614  
615 HoTT_Debug = {};
616  
617 HoTT_Debug.enabled = false;
618  
619 HoTT_Debug.pn = placed_names;
620  
621 HoTT_Debug.pnt = {};
622  
623  
624 ---
625 --- dprint
626 ---
627  
628 local function dprint (message)
629  
630 if ( HoTT_Debug.enabled ) then
631  
632 if ( type(print) == "function" ) then
633 print(message);
634 else
635 DEFAULT_CHAT_FRAME:AddMessage(message)
636 end
637  
638 end
639  
640 end
641  
642  
643  
644  
645 ---
646 --- tprint
647 ---
648  
649 local function tprint (message, relative_time)
650  
651 if ( HoTT_Debug.enabled ) then
652  
653 if ( not relative_time ) then
654 relative_time = 0;
655 end
656  
657 dprint(format("%7.3f %s",
658 GetTime() - relative_time,
659 tostring(message)));
660  
661 end
662  
663 end
664  
665  
666  
667  
668 -------------------------------
669 --- Stubbed local functions ---
670 -------------------------------
671  
672  
673 local refresh_ui_positions;
674 local position_display;
675  
676 local update_tot;
677 local update_tot_health;
678  
679 local reset_poll_timer;
680  
681 local launch_params_browser;
682  
683  
684  
685 --------------------
686 --- Other locals ---
687 --------------------
688  
689 --local current_params;
690  
691  
692  
693  
694 ---------------------
695 --- Table utility ---
696 ---------------------
697  
698 ---
699 --- clear_table
700 ---
701  
702 local function clear_table (table1)
703  
704 local k,v;
705  
706 for k,v in pairs(table1) do
707 table1[k] = nil;
708 end
709  
710 return table;
711 end
712  
713  
714  
715 ---
716 --- copy_table_recursively
717 ---
718  
719 --- only used by add_settings_object_versioning()
720 --- will not copy non-iteratable tables
721  
722 local function copy_table_recursively (table1, table2, missing_keys_only)
723  
724 ---
725 --- NOTE: Is this kind of potential self-copy "safe" in Lua?
726 --- Would be if iterator is static -- CHECK THIS!
727 --- Also applies to copy_table()
728 ---
729  
730 ---
731 --- NOTE: Lua logic means that a nil entry in the destination
732 --- is always overwritten, even with missing_keys_only true
733 ---
734  
735 ---
736 --- NOTE: Should really not copy field if dest == source
737 --- This should prevent infinite recursion as well
738 ---
739  
740 local k,v, copy_this
741  
742 for k,v in pairs (table1) do
743  
744 copy_this = ( table2[k] ~= v ) and
745 ( ( not missing_keys_only ) or
746 ( ( table2[k] == nil ) and ( v ~= nil ) ) );
747  
748 if ( type(v) == "table" ) then
749 if ( copy_this ) then
750 table2[k] = {};
751 end
752 copy_table_recursively(v, table2[k], missing_keys_only);
753  
754 else
755 if ( copy_this ) then
756 table2[k] = v;
757 end
758 end
759  
760 end
761  
762 return table2;
763 end
764  
765  
766  
767 ---
768 --- copy_table_structure
769 ---
770  
771 local function copy_table_structure (table1, table2, visited_nodes)
772  
773 local k,v
774  
775 if ( not visited_nodes ) then
776 visited_nodes = {};
777 visited_nodes[tostring(table1)] = true;
778 end
779  
780 for k,v in pairs (table1) do
781  
782 if ( ( type(v) == "table" ) and
783 ( not ( visited_nodes[tostring(v)] ) ) ) then
784  
785 if ( not ( type(table2[k]) == "table" ) ) then
786 table2[k] = {};
787 end
788  
789 visited_nodes[tostring(v)] = true;
790 copy_table_structure(v, table2[k], visited_nodes);
791  
792 end
793  
794 end
795  
796 return table2;
797 end
798  
799  
800  
801 ----------------------------
802 --- Handling params sets ---
803 ----------------------------
804  
805 local params = {}; -- will point to the current params object
806 HoTT_Params = {}; -- as well as a global handle to it
807  
808 local scratch_params = {}; -- need something before HoTT_Settings is ready
809  
810  
811  
812 ---
813 --- set_deferral
814 ---
815  
816 local function set_deferral (child, parent, child_name, parent_name)
817  
818 if ( type(child_name) ~= "string" ) then
819 child_name = "";
820 end
821  
822 if ( type(parent_name) ~= "string" ) then
823 parent_name = "";
824 end
825  
826 if ( type(child) ~= "table" ) then
827 error(string.format(fmt_not_a_table, child_name, "child"));
828 end
829  
830 if ( type(parent) ~= "table" ) then
831 error(string.format(fmt_not_a_table, parent_name, "parent"));
832 end
833  
834 local mt_defer = {};
835  
836 mt_defer.__index = function (t, k)
837  
838 local retval;
839 local pv = parent[k];
840  
841 if ( type(pv) == "table" ) then
842 t[k] = {};
843 set_deferral(t[k], parent[k],
844 child_name, parent_name);
845 retval = t[k];
846  
847 else
848 retval = pv;
849  
850 end
851  
852 return retval;
853 end;
854  
855 -- mt_defer.__newindex = function (t, k, v)
856 --
857 -- local pv = parent[k];
858 --
859 -- if ( pv ) then
860 -- rawset(t, k, v); -- need rawset!
861 --
862 -- else
863 -- error(string.format(fmt_no_such_field,
864 -- format_table_key(parent_name, k)),
865 -- 2);
866 -- end
867 --
868 -- end;
869  
870  
871 setmetatable(child, mt_defer);
872  
873 return child;
874 end
875  
876  
877  
878 ---
879 --- set_deferral_recursively
880 ---
881  
882 local function set_deferral_recursively (child, parent,
883 child_name, parent_name, visited)
884  
885  
886 local k,v;
887 local next_child_name;
888 local next_parent_name;
889  
890 if ( type(child_name) ~= "string" ) then
891 child_name = "";
892 end
893  
894 if ( type(parent_name) ~= "string" ) then
895 parent_name = "";
896 end
897  
898 if ( type(child) ~= "table" ) then
899 error(string.format(fmt_not_a_table, child_name, "child"));
900 end
901  
902 if ( type(parent) ~= "table" ) then
903 error(string.format(fmt_not_a_table, parent_name, "parent"));
904 end
905  
906 if ( not visited ) then
907 visited = {};
908 visited[child] = true;
909 end
910  
911  
912 for k,v in pairs(child) do
913 if ( ( type(v) == "table" ) and
914 ( not visited[v] ) ) then
915  
916 if ( type(parent[k]) ~= "table" ) then
917  
918 ---
919 --- WARNING: This wipes masking data
920 ---
921  
922 child[k] = nil;
923  
924 else
925 next_child_name = format_table_key(child_name, k);
926 next_parent_name = format_table_key(parent_name, k);
927  
928 set_deferral_recursively (v, parent[k],
929 next_child_name,
930 next_parent_name,
931 visited);
932  
933 end
934  
935 end
936  
937 end
938  
939 set_deferral(child, parent, child_name, parent_name);
940  
941 return child;
942 end
943  
944  
945  
946 ---
947 --- init_preload_params
948 ---
949  
950 local function init_preload_params ()
951  
952 set_deferral_recursively(scratch_params, default_params,
953 "scratch_params", "default_params");
954  
955 params = scratch_params;
956 HoTT_Params = scratch_params;
957  
958 return params;
959 end
960  
961 --- and init_preload_params for safety!
962  
963 init_preload_params();
964  
965  
966  
967  
968 ---
969 --- get_settings_set
970 ---
971  
972 local function get_settings_set (set_name)
973  
974 return HoTT_Settings._sets[set_name];
975 end
976  
977  
978  
979 ---
980 --- change_settings
981 ---
982  
983 local function change_settings (set_name)
984  
985 -- Assumes set_name exists and is valid
986  
987 local new_settings_set = get_settings_set(set_name);
988  
989 assert(new_settings_set, format(string_assert_set_exists_fmt,
990 tostring(set_name)));
991  
992 set_deferral_recursively(new_settings_set, default_params,
993 "HoTT_Params", "default_params");
994  
995 -- params.hott_was_on = hott_is_on; -- this borks on-load behavior?
996  
997 params = new_settings_set;
998 HoTT_Params = new_settings_set;
999  
1000 refresh_ui_positions();
1001  
1002 return params;
1003 end
1004  
1005  
1006  
1007 ---
1008 --- restore_defaults
1009 ---
1010  
1011 local function restore_defaults ()
1012  
1013 clear_table(params);
1014  
1015 refresh_ui_positions();
1016  
1017 -- DEFAULT_CHAT_FRAME:AddMessage(restored_defaults);
1018  
1019 return params;
1020 end
1021  
1022  
1023  
1024  
1025  
1026  
1027  
1028 -----------------------------------------------
1029 --- Parameters sets and persistent settings ---
1030 -----------------------------------------------
1031  
1032  
1033 ---
1034 --- NOTE: I should split these out some day
1035 ---
1036 --- outside variables refered to:
1037 ---
1038 --- player_name
1039 --- realm_name
1040 --- HoTT_Settings
1041 ---
1042 --- various strings
1043 ---
1044  
1045  
1046  
1047 ---
1048 --- The following methods assume that the object is initalized
1049 --- and is self-consistent
1050 ---
1051  
1052  
1053  
1054 ---
1055 --- get_default_settings_set_name
1056 ---
1057  
1058 local function get_default_settings_set_name ()
1059  
1060 --- DO NOT LOCALIZE THIS FORMAT STRING
1061 --- Doing so will invalidate settings if the player changes locale
1062  
1063 return format("%s of %s", player_name, realm_name);
1064 end
1065  
1066  
1067  
1068 ---
1069 --- settings_set_exists
1070 ---
1071  
1072 local function settings_set_exists (set_name)
1073  
1074 return HoTT_Settings._sets[set_name];
1075 end
1076  
1077  
1078  
1079 ---
1080 --- init_settings_set
1081 ---
1082  
1083 local function init_settings_set (set_name)
1084  
1085 HoTT_Settings._sets[set_name] = {};
1086  
1087 return HoTT_Settings._sets[set_name];
1088 end
1089  
1090  
1091  
1092 ---
1093 --- get_active_settings_set_name
1094 ---
1095  
1096 local function get_active_settings_set_name ()
1097  
1098 return HoTT_Settings._players[realm_name][player_name].active_set;
1099 end
1100  
1101  
1102  
1103 ---
1104 --- change_active_settings_set
1105 ---
1106  
1107 local function change_active_settings_set (set_name)
1108  
1109 HoTT_Settings._players[realm_name][player_name].active_set = set_name;
1110  
1111 return HoTT_Settings._sets[set_name];
1112 end
1113  
1114  
1115  
1116 ---
1117 --- get_active_settings_set
1118 ---
1119  
1120 local function get_active_settings_set ()
1121  
1122 return get_settings_set(get_active_settings_set_name());
1123 end
1124  
1125  
1126  
1127 ---
1128 --- add_settings_object_versioning
1129 ---
1130  
1131 local function add_settings_object_versioning (settings_object)
1132  
1133 if ( type(settings_object._version) == "table" ) then
1134 clear_table(settings_object._version)
1135 else
1136 settings_object._version = {};
1137 end
1138  
1139 copy_table_recursively(version_table_data, settings_object._version);
1140  
1141 return settings_object;
1142 end
1143  
1144  
1145  
1146 ---
1147 --- NOTE: These will need serious re-working
1148 ---
1149  
1150  
1151 ---
1152 --- valid_settings_object
1153 ---
1154  
1155 local function valid_settings_object (settings_object)
1156  
1157 return ( type(settings_object) == "table") and
1158 ( type(settings_object._version) == "table" ) and
1159 ( string.sub(settings_object._version.object, 1, 2) == "1." )
1160 ;
1161 end
1162  
1163  
1164  
1165 ---
1166 --- repopulate_settings_object
1167 ---
1168  
1169 local function repopulate_settings_object (settings_object)
1170  
1171 -- nothing to do right now
1172 return settings_object;
1173 end
1174  
1175  
1176  
1177 --- ########################################
1178 --- update_settings_object ##### THIS ONE NEEDS TO BE CHANGED #####
1179 --- ########################################
1180  
1181 local function update_settings_object (settings_object)
1182  
1183 -- nothing to do right now, only one valid version
1184 return settings_object;
1185 end
1186  
1187  
1188  
1189  
1190 ---
1191 --- load_saved_settings
1192 ---
1193  
1194 local function load_saved_settings ()
1195  
1196  
1197 ---
1198 --- A mish-mosh of validation, object creation, initialization
1199 --- error reporting, and some application-specific tie-ins
1200 ---
1201 --- Will need to be cleaned up a lot to split out
1202 ---
1203  
1204 ---
1205 --- Should really use getglobal(), but can't find "setglobal()"
1206 ---
1207  
1208  
1209 local player_ptr;
1210 local set_ptr;
1211  
1212 local default_set_name;
1213 local active_set_name;
1214  
1215  
1216 if ( not valid_settings_object(HoTT_Settings) ) then
1217  
1218 if (HoTT_Settings) then
1219  
1220 HoTT_Settings_Unparsed = HoTT_Settings;
1221  
1222 DEFAULT_CHAT_FRAME:AddMessage(invalid_settings_string);
1223  
1224 end
1225  
1226  
1227 -- Initialize from scratch
1228 dprint("initializing object");
1229  
1230 HoTT_Settings = {};
1231  
1232 add_settings_object_versioning(HoTT_Settings);
1233  
1234 HoTT_Settings._players = {};
1235 HoTT_Settings._sets = {};
1236  
1237 end
1238  
1239  
1240 -- Add anything that is lost in the write/read cycle
1241 -- Ok, we have a valid, but possibly outdated settings object
1242 dprint("repopulate and update");
1243  
1244 repopulate_settings_object(HoTT_Settings);
1245 update_settings_object(HoTT_Settings);
1246  
1247  
1248 -- Now its good to go, though possibly empty
1249  
1250  
1251 assert(player_name, string_assert_no_player);
1252 assert(realm_name, string_assert_no_realm);
1253  
1254  
1255 -- Make sure this player's default set exists first
1256  
1257  
1258 default_set_name = get_default_settings_set_name();
1259 dprint("default_set_name: "..tostring(default_set_name));
1260 if ( not settings_set_exists(default_set_name) ) then
1261 dprint("initializing default set for "..tostring(default_set_name));
1262 init_settings_set(default_set_name);
1263 end
1264  
1265  
1266 -- Then make sure the player exists
1267  
1268  
1269 if ( not HoTT_Settings._players[realm_name] ) then
1270 dprint("._players");
1271 HoTT_Settings._players[realm_name] = {};
1272 end
1273  
1274 if ( not HoTT_Settings._players[realm_name][player_name] ) then
1275 dprint("._players."..tostring(realm_name));
1276 HoTT_Settings._players[realm_name][player_name] = {};
1277 end
1278  
1279 if ( not HoTT_Settings._players[realm_name][player_name] ) then
1280 dprint("._players."..tostring(realm_name)..tostring(player_name));
1281 HoTT_Settings._players[realm_name][player_name] = {};
1282 end
1283  
1284  
1285 -- and tie them to their default set if they don't have one already
1286  
1287  
1288 active_set_name = get_active_settings_set_name();
1289  
1290 if ( not active_set_name ) then
1291 dprint("change to default");
1292 change_active_settings_set(default_set_name);
1293 end
1294  
1295  
1296 -- Realm, player, and default set should be present now
1297 -- Check that their active set is a good one
1298  
1299  
1300 active_set_name = get_active_settings_set_name();
1301  
1302  
1303  
1304 if ( not settings_set_exists(active_set_name) ) then
1305  
1306 -- Go to their default set
1307  
1308 DEFAULT_CHAT_FRAME:AddMessage(format(restore_failed_format,
1309 active_set_name));
1310  
1311 change_active_settings_set(default_set_name);
1312  
1313 DEFAULT_CHAT_FRAME:AddMessage(format(restored_format,
1314 active_set_name));
1315  
1316 end
1317  
1318 -- which really exists
1319 dprint("Change to: "..tostring(active_set_name));
1320 return change_settings(active_set_name);
1321 end
1322  
1323  
1324  
1325 -----------------------
1326 -----------------------
1327 --- LOCAL FUNCTIONS ---
1328 -----------------------
1329 -----------------------
1330  
1331  
1332 --------------------------
1333 --- target_info tables ---
1334 --------------------------
1335  
1336 ---
1337 --- clear_target_info
1338 ---
1339  
1340 local function clear_target_info (target_info)
1341  
1342 clear_table(target_info);
1343  
1344 return target_info;
1345 end
1346  
1347  
1348  
1349 ---
1350 --- copy_target_info
1351 ---
1352  
1353 local function copy_target_info (target_info1, target_info2)
1354  
1355 local k,v;
1356  
1357 clear_target_info(target_info2);
1358 for k,v in pairs(target_info1) do
1359 target_info2[k] = v;
1360 end
1361  
1362 return target_info2;
1363 end
1364  
1365  
1366  
1367 ---
1368 --- exact_target_info
1369 ---
1370  
1371 local function exact_target_info (target_info1, target_info2)
1372  
1373 return ( target_info1.name == target_info2.name ) and
1374 ( target_info1.level == target_info2.level ) and
1375 ( target_info1.sex == target_info2.sex ) and
1376 ( target_info1.player == target_info2.player ) and
1377 ( target_info1.enemy == target_info2.enemy ) and
1378 ( target_info1.canattack == target_info2.canattack ) and
1379 ( target_info1.canattackme == target_info2.canattackme ) and
1380 ( target_info1.friend == target_info2.friend ) and
1381 ( target_info1.shapeshifted == target_info2.shapeshifted );
1382 end
1383  
1384  
1385  
1386 ---
1387 --- clear_t
1388 ---
1389  
1390 local function clear_t ()
1391  
1392 clear_target_info(t_target_info);
1393  
1394 return t_target_info;
1395 end
1396  
1397  
1398  
1399 ---
1400 --- clear_tot
1401 ---
1402  
1403 local function clear_tot ()
1404  
1405 last_tot = tot_target_info.name;
1406  
1407 clear_target_info(tot_target_info);
1408  
1409 this_tot = nil;
1410  
1411 return tot_target_info;
1412 end
1413  
1414  
1415  
1416 ---
1417 --- target_to_t
1418 ---
1419  
1420 local function target_to_t ()
1421  
1422 if ( UnitExists("target") ) then
1423  
1424 t_target_info.name = UnitName("target");
1425 t_target_info.level = UnitLevel("target");
1426 t_target_info.sex = UnitSex("target");
1427 t_target_info.player = UnitIsPlayer("target");
1428 t_target_info.enemy = UnitIsEnemy("target", "player");
1429 t_target_info.canattack = UnitCanAttack("player", "target");
1430 t_target_info.canattackme = UnitCanAttack("target", "player");
1431 t_target_info.friend = UnitIsFriend("target", "player");
1432 t_target_info.shapeshifted = nil; -- for later expansion
1433  
1434 else
1435 clear_t();
1436  
1437 end
1438  
1439 return t_target_info;
1440 end
1441  
1442  
1443  
1444 ---
1445 --- targettarget_to_tot
1446 ---
1447  
1448 local function targettarget_to_tot ()
1449  
1450 last_tot = tot_target_info.name;
1451  
1452 if ( UnitExists("targettarget") ) then
1453  
1454 tot_target_info.name = UnitName("targettarget");
1455 tot_target_info.level = UnitLevel("targettarget");
1456 tot_target_info.sex = UnitSex("targettarget");
1457 tot_target_info.player = UnitIsPlayer("targettarget");
1458 tot_target_info.enemy = UnitIsEnemy("targettarget", "player");
1459 tot_target_info.canattack = UnitCanAttack("player", "targettarget");
1460 tot_target_info.canattackme = UnitCanAttack("targettarget", "player");
1461 tot_target_info.friend = UnitIsFriend("targettarget", "player");
1462 tot_target_info.shapeshifted = nil; -- for later expansion
1463  
1464 else
1465 clear_tot();
1466  
1467 end
1468  
1469 this_tot = tot_target_info.name;
1470  
1471 return tot_target_info;
1472 end
1473  
1474  
1475  
1476 ---
1477 --- self_to_tot
1478 ---
1479  
1480 local function self_to_tot ()
1481  
1482 -- Yep, this could be simpler, but leave it be for clarity
1483 -- It shouldn't be called except on target change to self
1484 -- and then when the idle check times out
1485  
1486 last_tot = tot_target_info.name;
1487  
1488 if ( UnitExists("player") ) then
1489  
1490 tot_target_info.name = UnitName("player");
1491 tot_target_info.level = UnitLevel("player");
1492 tot_target_info.sex = UnitSex("player");
1493 tot_target_info.player = UnitIsPlayer("player");
1494 tot_target_info.enemy = UnitIsEnemy("player", "player");
1495 tot_target_info.canattack = UnitCanAttack("player", "player");
1496 tot_target_info.canattackme = UnitCanAttack("player", "player");
1497 tot_target_info.friend = UnitIsFriend("player", "player");
1498 tot_target_info.shapeshifted = nil; -- for later expansion
1499  
1500 else
1501 clear_tot();
1502  
1503 end
1504  
1505 this_tot = tot_target_info.name;
1506  
1507 return tot_target_info;
1508 end
1509  
1510  
1511  
1512 ---
1513 --- new_target_appears_same_as_before
1514 ---
1515  
1516 local function new_target_appears_same_as_before ()
1517  
1518 local retval = UnitExists("target");
1519  
1520 if ( retval ) then
1521  
1522 local target_sex = UnitSex("target");
1523  
1524 if ( ( UnitName("target") ~= t_target_info.name ) or
1525 ( UnitLevel("target") ~= t_target_info.level ) ) then
1526 retval = false;
1527  
1528 elseif ( target_sex == t_target_info.sex ) then
1529 retval = true;
1530  
1531 -- well, except for shapeshifted mobs,
1532 -- which we very poorly handle with
1533  
1534 elseif ( (target_sex == 2) or (t_target_info.sex == 2) ) then
1535 retval = true;
1536  
1537 else
1538 retval = false;
1539  
1540 end
1541  
1542 end
1543  
1544 return retval;
1545 end
1546  
1547  
1548  
1549 ------------------------------------
1550 --- Maintain a roster of UnitIDs ---
1551 ------------------------------------
1552  
1553 ---
1554 --- insert_possible_unit
1555 ---
1556  
1557 local function insert_possible_unit (unit, type)
1558  
1559 local retval = true;
1560  
1561 local name = UnitName(unit);
1562 local existing_unit;
1563  
1564 if ( name and (name ~= UNKNOWNOBJECT) and (name ~= UKNOWNBEING) ) then
1565  
1566 existing_unit = roster[name];
1567  
1568 if ( existing_unit and
1569 ( string.sub(existing_unit, 1, string.len(type)) == type ) ) then
1570 assert(roster_assertion_seen,
1571 format(ras_format,
1572 name, existing_unit, unit));
1573 roster_assertion_seen = true;
1574 retval = nil;
1575 else
1576 roster[name] = unit;
1577 retval = name;
1578 end
1579  
1580 end
1581  
1582 return retval;
1583 end
1584  
1585  
1586  
1587 ---
1588 --- update_roster
1589 ---
1590  
1591 local function update_roster ()
1592  
1593 local retval = true;
1594 local this_ok;
1595  
1596 local unit, name, idx;
1597 local nraid = GetNumRaidMembers();
1598 local nparty = GetNumPartyMembers();
1599  
1600 clear_table(roster);
1601  
1602 if ( nraid > 1 ) then
1603  
1604 for idx = 1, 40 do -- as they are by slot, and may not be sequential
1605 unit = format("raid%i",idx);
1606 this_ok = insert_possible_unit(unit, "raid");
1607 retval = retval and this_ok;
1608 unit = format("raidpet%i",idx);
1609 this_ok = insert_possible_unit(unit, "raidpet");
1610 retval = retval and this_ok;
1611 end
1612  
1613 end
1614  
1615 if ( nparty > 1 ) then
1616  
1617 for idx = 1, 4 do -- its only 4 and nparty-1 was seemingly one short
1618 unit = format("party%i",idx);
1619 this_ok =insert_possible_unit(unit, "party");
1620 retval = retval and this_ok;
1621 unit = format("partypet%i",idx);
1622 this_ok =insert_possible_unit(unit, "partypet");
1623 retval = retval and this_ok;
1624 end
1625  
1626 end
1627  
1628 this_ok = insert_possible_unit("player", "player");
1629 retval = retval and this_ok;
1630 this_ok = insert_possible_unit("pet", "pet");
1631 retval = retval and this_ok;
1632  
1633 return retval;
1634 end
1635  
1636  
1637  
1638 ---
1639 --- best_tot_unit
1640 ---
1641  
1642 local function best_tot_unit()
1643  
1644 local retval = nil;
1645  
1646 if ( ( UnitExists("targettarget") ) and
1647 ( UnitName("targettarget") == tot_target_info.name ) ) then
1648 retval = "targettarget";
1649  
1650 else
1651 retval = roster[tot_target_info.name];
1652  
1653 end
1654  
1655 return retval;
1656 end
1657  
1658  
1659  
1660  
1661 ---
1662 --- update_bar_unit
1663 ---
1664  
1665 local function update_bar_unit()
1666  
1667 -- Returns true unless "real" unit is known not to have changed
1668  
1669 local try_unit = roster[tot_target_info.name];
1670 local old_unit = HoTT_Display_Bar.unit;
1671 if ( not units_getting_events[try_unit] ) then
1672 try_unit = nil;
1673 end
1674 HoTT_Display_Bar.unit = try_unit;
1675 if ( HoTT_Debug.UBU and ( HoTT_Display_Bar.unit ~= old_unit) ) then
1676 dprint("Bar now using "..tostring(HoTT_Display_Bar.unit));
1677 end
1678  
1679 return not ( HoTT_Display_Bar.unit and
1680 ( HoTT_Display_Bar.unit == old_unit )
1681 );
1682 end
1683  
1684  
1685  
1686 ---
1687 --- clear_bar
1688 ---
1689  
1690 local function clear_bar ()
1691  
1692 HoTT_Display_Bar.unit = nil;
1693 HoTT_Display_Bar:SetValue(0);
1694 HoTT_Display_Dead:Hide();
1695  
1696 end
1697  
1698  
1699  
1700 ---
1701 --- available_name_width
1702 ---
1703  
1704 local function available_name_width ()
1705  
1706 local right = HoTT_Display:GetRight();
1707 local left = HoTT_Display:GetLeft();
1708  
1709 local anw = 115; -- Covers start-up nil problem
1710  
1711 if ( right and left ) then
1712 anw = (right - left)
1713 - params.ui.display.tot_name.inset.right
1714 - params.ui.display.tot_name.inset.left;
1715 end
1716  
1717 return anw;
1718  
1719 end
1720  
1721  
1722  
1723 ---
1724 --- set_display_name_color
1725 ---
1726  
1727 --- Typcially ~ 5 ms for a new name and a reasonably-sized window
1728 --- About 10-15 ms to truncate down a long name to nothing
1729 --- regexp is not the culprit
1730  
1731 local function set_display_name_color(name, color)
1732  
1733  
1734 if ( name ) then
1735  
1736 if ( placed_names.available_name_width ~= available_name_width() ) then
1737 clear_table(placed_names);
1738 placed_names.available_name_width = available_name_width();
1739  
1740 end
1741  
1742 local fits = placed_names[name];
1743  
1744 if ( fits ) then
1745 HoTT_Display_NameArea_Text:SetText(fits);
1746  
1747 else
1748  
1749 local now;
1750  
1751 if ( HoTT_Debug.name_time ) then
1752 now = GetTime();
1753 end
1754  
1755 HoTT_Display_NameArea_Text:SetText(name);
1756 if ( ( HoTT_Display_NameArea_Text:GetStringWidth() <=
1757 placed_names.available_name_width ) or
1758 ( string.len(name) <= 2 )
1759 ) then
1760 fits = name;
1761  
1762 else
1763  
1764 local try = string.sub(name, 1, -2);
1765 local ellipsis_chars = string.len(ellipsis);
1766  
1767 while ( not fits ) do
1768 HoTT_Display_NameArea_Text:SetText(try);
1769 if ( ( HoTT_Display_NameArea_Text:GetStringWidth() <=
1770 placed_names.available_name_width ) or
1771 ( string.len(try) <= 2 )
1772 ) then
1773 fits = try;
1774 else
1775 try = string.sub(try, 1, -2);
1776 end
1777 end
1778  
1779 try = string.sub(try, 1, -2) .. ellipsis;
1780 fits = false;
1781  
1782 while ( not fits ) do
1783 HoTT_Display_NameArea_Text:SetText(try);
1784 if ( ( HoTT_Display_NameArea_Text:GetStringWidth() <=
1785 placed_names.available_name_width ) or
1786 ( string.len(try) <= ( 1 + ellipsis_chars ) )
1787 ) then
1788 fits = try;
1789 else
1790 try = string.sub(try, 1, -2-ellipsis_chars) .. ellipsis;
1791 end
1792 end
1793  
1794 fits = string.gsub(fits, "%s"..ellipsis.."$", ellipsis);
1795  
1796 end -- did name fit first time
1797  
1798 placed_names[name] = fits;
1799 if ( HoTT_Debug.name_time ) then
1800 local dt = GetTime() - now;
1801 if ( dt > 0.0015 ) then
1802 HoTT_Debug.pnt[name] = format("%5.3f",GetTime() - now);
1803 end
1804 tprint("Name time", now);
1805 end
1806 HoTT_Display_NameArea_Text:SetText(fits);
1807  
1808 end
1809  
1810 end
1811  
1812 if ( color ) then
1813 HoTT_Display_NameArea_Text:SetTextColor(color.r,
1814 color.g,
1815 color.b);
1816 end
1817  
1818 return true;
1819 end
1820  
1821  
1822  
1823 ---
1824 --- position_display
1825 ---
1826  
1827 -- local
1828 function position_display ()
1829  
1830 local buff_rows_shown;
1831 local anw = available_name_width();
1832  
1833 local puid = params.ui.display;
1834 local relative_to = puid.relative_to;
1835 local relative_to_object = getglobal(relative_to);
1836  
1837 if ( not relative_to_object ) then
1838 message(format(fmt_no_such_object, puid.relative_to));
1839  
1840 return false;
1841 end
1842  
1843  
1844 if ( HoTT_Debug.PD ) then
1845 dprint("Positioning for " .. ( UnitName("target") or "<nil>"));
1846 end
1847  
1848  
1849 local has_buffs = UnitBuff("target",1);
1850 local has_debuffs = UnitDebuff("target",1);
1851 local is_friend = UnitIsFriend("player", "target");
1852  
1853 if ( not ( has_buffs or has_debuffs ) ) then
1854 buff_rows_shown = 0;
1855  
1856 elseif ( ( has_debuffs and is_friend ) or
1857 ( has_buffs and not is_friend ) ) then
1858 buff_rows_shown = 2;
1859  
1860 else
1861 buff_rows_shown = 1;
1862  
1863 end
1864  
1865 -- Check for "too narrow" problems and silently rectify
1866  
1867 if ( anw < puid.tot_name.min_width ) then
1868 puid.inset.right = puid.inset.right + ( puid.tot_name.min_width - anw );
1869 end
1870  
1871  
1872 --- Need to adjust relative offsets to UIParent scale
1873  
1874 local s = 1/params.ui.display.scale;
1875  
1876  
1877 if ( ( relative_to_object == UIParent ) or
1878 ( relative_to_object == WorldFrame ) ) then
1879  
1880 HoTT_Display:SetPoint("TOPLEFT",
1881 puid.relative_to,
1882 "TOPLEFT",
1883 s*puid.inset.left,
1884 -s*puid.drops[buff_rows_shown]);
1885  
1886 HoTT_Display:SetPoint("TOPRIGHT",
1887 puid.relative_to,
1888 "TOPRIGHT",
1889 -s*puid.inset.right,
1890 -s*puid.drops[buff_rows_shown]);
1891  
1892 else
1893  
1894 HoTT_Display:SetPoint("TOPLEFT",
1895 puid.relative_to,
1896 "BOTTOMLEFT",
1897 s*puid.inset.left,
1898 -s*puid.drops[buff_rows_shown]);
1899  
1900 HoTT_Display:SetPoint("TOPRIGHT",
1901 puid.relative_to,
1902 "BOTTOMRIGHT",
1903 -s*puid.inset.right,
1904 -s*puid.drops[buff_rows_shown]);
1905  
1906 end
1907  
1908 return true;
1909 end
1910  
1911  
1912  
1913 ---
1914 --- refresh_ui_positions
1915 ---
1916  
1917 ---
1918 --- NOTE: Call on /tot ui, and any time settings change
1919 ---
1920  
1921 -- stubbed local earlier:
1922 --
1923 -- local
1924 function refresh_ui_positions ()
1925  
1926 local name_offset;
1927  
1928 local puid = params.ui.display;
1929  
1930 HoTT_Display:SetScale(puid.scale * UIParent:GetScale());
1931  
1932 HoTT_Display_NameArea_Text:SetPoint("LEFT",
1933 "HoTT_Display_NameArea",
1934 "LEFT",
1935 puid.tot_name.inset.left, 0);
1936  
1937 HoTT_Display_NameArea_Text:SetPoint("RIGHT",
1938 "HoTT_Display_NameArea",
1939 "RIGHT",
1940 -puid.tot_name.inset.right, 0);
1941  
1942 HoTT_Display_NameArea_Text:SetJustifyH(puid.tot_name.alignment);
1943  
1944 position_display();
1945  
1946 return true;
1947 end
1948  
1949  
1950  
1951 ---
1952 --- global_check_warning
1953 ---
1954  
1955 local function global_check_warning ()
1956  
1957 local global_check_failed = ( type(global_check_failures) ~= "nil" );
1958  
1959 if ( global_check_failed ) then
1960 DEFAULT_CHAT_FRAME:AddMessage(string_global_check_failed);
1961 end
1962  
1963 return global_check_failed;
1964 end
1965  
1966  
1967 ---
1968 --- hott_init
1969 ---
1970  
1971 local function hott_init ()
1972  
1973 clear_t();
1974 clear_tot();
1975 hott_is_on = false;
1976 last_tot = nil;
1977 last_frame_tot = nil;
1978  
1979 clear_table(roster);
1980  
1981 clear_bar();
1982  
1983 reset_poll_timer();
1984  
1985 set_display_name_color(undetermined_string, colors.name.undetermined);
1986  
1987 return true;
1988 end
1989  
1990  
1991  
1992 ---
1993 --- hott_off
1994 ---
1995  
1996 local function hott_off ()
1997  
1998 global_check_warning();
1999  
2000 hott_init();
2001  
2002 HoTT_Display:Hide();
2003  
2004 params.hott_was_on = false;
2005  
2006 -- DEFAULT_CHAT_FRAME:AddMessage(off_string);
2007  
2008 return true;
2009 end
2010  
2011  
2012  
2013 ---
2014 --- hott_on
2015 ---
2016  
2017 local function hott_on ()
2018  
2019 global_check_warning();
2020  
2021 if ( HoTT_Patch_Warning() ) then
2022 return;
2023 end
2024  
2025 if ( not hott_is_on ) then
2026 hott_init();
2027 update_roster();
2028 end
2029  
2030 hott_is_on = true;
2031  
2032 update_tot();
2033  
2034 refresh_ui_positions();
2035  
2036 if UnitExists("target") then
2037 HoTT_Display:Show();
2038 end
2039  
2040 params.hott_was_on = true;
2041  
2042 -- DEFAULT_CHAT_FRAME:AddMessage(on_string);
2043  
2044 return true;
2045 end
2046  
2047  
2048  
2049 ---
2050 --- better_target_by_name
2051 ---
2052  
2053 local function better_target_by_name (desiredTarget, second_pass)
2054  
2055 local oldTarget = UnitName("target");
2056 local newTarget;
2057 local retval;
2058  
2059 local reverse_unit = roster[desiredTarget];
2060 if ( UnitName("targettarget") == desiredTarget ) then
2061 TargetUnit("targettarget");
2062  
2063 elseif (reverse_unit) then
2064 TargetUnit(reverse_unit);
2065  
2066 else
2067 TargetByName(desiredTarget);
2068  
2069 end
2070  
2071 newTarget = UnitName("target");
2072  
2073 if ( newTarget == desiredTarget ) then
2074  
2075 retval = newTarget;
2076  
2077 else
2078 -- restore target to the "best" option, return nil
2079  
2080 if ( not second_pass ) then
2081 better_target_by_name(oldTarget, true);
2082  
2083 else
2084 ClearTarget();
2085  
2086 end
2087  
2088 retval = nil;
2089  
2090 end
2091  
2092 return retval;
2093  
2094 end
2095  
2096  
2097  
2098 ---
2099 --- bar_set_unreliable_callback
2100 ---
2101  
2102 local function bar_set_unreliable_callback ()
2103 -- placeholder
2104 end
2105  
2106  
2107  
2108 ---
2109 --- load_saved_variables
2110 ---
2111  
2112 local function load_saved_variables ()
2113  
2114 ---
2115 --- On entering this, VARIABLES_LOADED should have fired
2116 --- and the flag indicating this has run should not be set
2117 ---
2118 --- Return a true value to set that flag
2119 ---
2120  
2121 load_saved_settings();
2122  
2123 if ( IsShiftKeyDown() ) then
2124  
2125 DEFAULT_CHAT_FRAME:AddMessage(shift_at_load_string);
2126  
2127 hott_off();
2128  
2129 else
2130  
2131 if ( params.hott_was_on ) then
2132  
2133 hott_on();
2134  
2135 else
2136  
2137 hott_off();
2138  
2139 end
2140  
2141 end
2142  
2143 return true;
2144 end
2145  
2146  
2147  
2148 ------------------------------------
2149 --- Command parsing and dispatch ---
2150 ------------------------------------
2151  
2152  
2153 ---
2154 --- HoTT_Command_Dispatch
2155 ---
2156  
2157 local function command_dispatch (command_string)
2158  
2159 local i1, i2;
2160 local command, command_arguments
2161  
2162 command_string = string.gsub(string.lower(command_string), "^%s*", "");
2163 command_string = string.gsub(command_string, "%s*$", "");
2164  
2165 i1, i2, command, command_arguments =
2166 string.find(command_string, "^(%w+)%s*(.*)$");
2167  
2168  
2169 if ( ( command == string_help_cmd ) or ( command == nil ) ) then
2170 DEFAULT_CHAT_FRAME:AddMessage(help_message);
2171  
2172  
2173 elseif ( command == string_on_cmd ) then
2174 hott_on();
2175  
2176  
2177 elseif ( command == string_off_cmd ) then
2178 hott_off();
2179  
2180  
2181 elseif ( command == string_ui_cmd ) then
2182 DEFAULT_CHAT_FRAME:AddMessage(help_ui);
2183  
2184  
2185 elseif ( command == string_version_cmd ) then
2186 DEFAULT_CHAT_FRAME:AddMessage(
2187 format("ToT CVS versions:\nLua: %s\nXML: %s",
2188 version_table_data.apps.TargetOfTarget.lua_CVS,
2189 version_table_data.apps.TargetOfTarget.XML_CVS));
2190  
2191  
2192 elseif ( command == string_globals_cmd ) then
2193  
2194 if ( not global_check_failures ) then
2195 DEFAULT_CHAT_FRAME:AddMessage(string_global_list);
2196  
2197 else
2198 DEFAULT_CHAT_FRAME:AddMessage(string_global_check_explanation);
2199 table_to_list = global_check_failures;
2200 for k,v in pairs(global_check_failures) do
2201 DEFAULT_CHAT_FRAME:AddMessage(v);
2202 end
2203 DEFAULT_CHAT_FRAME:AddMessage(string_global_variable_check_trailer);
2204  
2205 end
2206  
2207  
2208 elseif ( command == string_params_cmd ) then
2209 launch_params_browser();
2210  
2211  
2212 else
2213 DEFAULT_CHAT_FRAME:AddMessage(string_unrecognized_cmd_help);
2214  
2215 end
2216  
2217 end
2218  
2219  
2220  
2221  
2222  
2223  
2224  
2225 ------------------------
2226 ------------------------
2227 --- GLOBAL FUNCTIONS ---
2228 ------------------------
2229 ------------------------
2230  
2231  
2232 ----------------------
2233 --- Event Handlers ---
2234 ----------------------
2235  
2236  
2237 -------------------
2238 --- HoTT Itself ---
2239 -------------------
2240  
2241  
2242 ---
2243 --- HoTT_MainFrame_OnLoad
2244 ---
2245  
2246 function HoTT_MainFrame_OnLoad ()
2247  
2248 check_localized_slash_commands();
2249  
2250 init_preload_params();
2251 restore_defaults();
2252 hott_init();
2253  
2254 -- Register slash commands
2255  
2256 SLASH_TARGETOFTARGET_TOTON1 = toton_string;
2257 SlashCmdList["TARGETOFTARGET_TOTON"] = function ()
2258 hott_on();
2259 end
2260  
2261 SLASH_TARGETOFTARGET_TOTOFF1 = totoff_string;
2262 SlashCmdList["TARGETOFTARGET_TOTOFF"] = function ()
2263 hott_off();
2264 end
2265  
2266 SLASH_TARGETOFTARGET_TOT1 = string_slash_tot;
2267 SLASH_TARGETOFTARGET_TOT2 = string_slash_hott;
2268 SlashCmdList["TARGETOFTARGET_TOT"] = function (command_string)
2269 command_dispatch(command_string);
2270 end
2271  
2272 player_name = UnitName("player");
2273 realm_name = GetCVar("realmName");
2274  
2275  
2276 -- Register for events
2277  
2278 this:RegisterEvent("VARIABLES_LOADED");
2279 this:RegisterEvent("PLAYER_ENTERING_WORLD");
2280 this:RegisterEvent("UNIT_NAME_UPDATE");
2281  
2282 this:RegisterEvent("PARTY_MEMBERS_CHANGED");
2283 this:RegisterEvent("RAID_ROSTER_UPDATE");
2284  
2285 this:RegisterEvent("PLAYER_TARGET_CHANGED");
2286  
2287 this:RegisterEvent("UNIT_AURA");
2288  
2289 if(UltimateUI_RegisterButton) then
2290 UltimateUI_RegisterButton (
2291 "TargetofTarget",
2292 "Params",
2293 "|cFF00CC00TargetOfTarget|r\nShows the target of the target! To use, type\n /tot on",
2294 "Interface\\Icons\\Spell_Holy_SealOfSalvation",
2295 launch_params_browser
2296 );
2297 end
2298  
2299  
2300 --DEFAULT_CHAT_FRAME:AddMessage(loaded_string);
2301  
2302 end
2303  
2304  
2305  
2306 ---
2307 --- HoTT_MainFrame_OnEvent
2308 ---
2309  
2310 function HoTT_MainFrame_OnEvent()
2311  
2312  
2313 if ( event == "VARIABLES_LOADED" ) then
2314 load_saved_variables();
2315 end
2316  
2317  
2318 if ( not hott_is_on ) then
2319  
2320 -- we shouldn't be doing anything else...
2321  
2322 else
2323  
2324 if ( ( event == "PLAYER_ENTERING_WORLD" ) or
2325 ( event == "UNIT_NAME_UPDATE" ) or
2326 ( event == "PARTY_MEMBERS_CHANGED" ) or
2327 ( event == "RAID_ROSTER_UPDATE" ) ) then
2328  
2329 update_roster();
2330 if ( update_bar_unit() ) then update_tot_health() end;
2331  
2332 end
2333  
2334 if ( ( event == "PLAYER_TARGET_CHANGED" ) or
2335 ( event == "PLAYER_ENTERING_WORLD" ) ) then
2336 dprint("PTC or PEW event");
2337 update_tot();
2338 position_display();
2339  
2340 end
2341  
2342 if ( ( event == "UNIT_AURA" ) and ( arg1 == "target" ) ) then
2343  
2344 position_display();
2345  
2346 end
2347  
2348 end -- hott_is_on
2349  
2350 end
2351  
2352  
2353  
2354 ---------------
2355 --- Display ---
2356 ---------------
2357  
2358 ---
2359 --- HoTT_Display_OnClick
2360 ---
2361  
2362 function HoTT_Display_OnClick(button)
2363  
2364 local name = tot_target_info.name;
2365 local unit = best_tot_unit();
2366  
2367 if ( button == "RightButton") then
2368 return;
2369 end
2370  
2371 if ( button == "LeftButton" ) then
2372  
2373 if ( SpellIsTargeting() and unit ) then
2374 SpellTargetUnit(unit);
2375  
2376 elseif ( unit ) then
2377 TargetUnit(unit);
2378  
2379 else
2380 better_target_by_name(name); -- think about glowy hand impact...
2381  
2382 end
2383  
2384 end -- LeftButton
2385  
2386 end
2387  
2388  
2389  
2390 ---
2391 --- reset_poll_timer
2392 ---
2393  
2394 -- local
2395 function reset_poll_timer()
2396  
2397 poll_timer = 0;
2398 poll_timer_fired = false;
2399  
2400 return true;
2401 end
2402  
2403  
2404  
2405 ---
2406 --- HoTT_Display_OnUpdate
2407 ---
2408  
2409 function HoTT_Display_OnUpdate (interval)
2410  
2411 if ( not UnitExists("target") ) then
2412 last_frame_tot = nil;
2413 reset_poll_timer();
2414 this:Hide();
2415  
2416 else
2417  
2418 poll_timer = poll_timer + interval; -- can be moved inside .unit test
2419 -- left here for debugging output
2420  
2421 this_frame_tot = UnitName("targettarget");
2422  
2423 if ( this_frame_tot ~= last_frame_tot ) then -- fast, but...
2424 dprint("tot change");
2425 update_tot();
2426  
2427 elseif ( ( not HoTT_Display_Bar.unit ) and
2428 ( UnitIsPlayer("target") or
2429 UnitAffectingCombat("target") ) ) then
2430  
2431 -- Bar.unit take care of their own health and should have unique names
2432 -- Only players or in-combat NPCs can change health or targets
2433  
2434 if ( ( poll_timer > params.poll_timer_period ) and
2435 ( not poll_timer_fired ) ) then
2436  
2437  
2438 elseif ( tot_target_info.player ) then
2439 dprint("player timeout");
2440 update_tot_health();
2441  
2442 else
2443 dprint("NPC timeout")
2444 update_tot();
2445  
2446 end
2447  
2448 poll_timer_fired = true;
2449  
2450 end -- tot changed or Bar.unit exists
2451  
2452 last_frame_tot = this_frame_tot;
2453  
2454 end -- target exists
2455  
2456 end
2457  
2458  
2459  
2460 ---
2461 --- HoTT_Display_OnEvent
2462 ---
2463  
2464 function HoTT_Display_OnEvent ()
2465  
2466 if ( UnitExists("target") and hott_is_on ) then
2467 this:Show();
2468 else
2469 this:Hide();
2470 end
2471  
2472 end
2473  
2474  
2475  
2476  
2477  
2478 ------------------
2479 --- Health Bar ---
2480 ------------------
2481  
2482  
2483 ---
2484 --- HoTT_Display_Bar_OnEvent
2485 ---
2486  
2487 function HoTT_Display_Bar_OnEvent (event_arg1)
2488  
2489 if (this.unit and ( event_arg1 == this.unit ) and hott_is_on ) then
2490 UnitFrameHealthBar_Update(this, this.unit)
2491 end
2492  
2493 return true;
2494 end
2495  
2496  
2497  
2498 ---
2499 --- HoTT_Display_Bar_OnValueChanged
2500 ---
2501  
2502 function HoTT_Display_Bar_OnValueChanged (new_value)
2503  
2504 if ( HoTT_Debug.BOVC and
2505 units_getting_events[this.unit] and
2506 ( time_since_bar_update > 0.001 ) ) then
2507 dprint(format("Bar update interval: %7.3f", time_since_bar_update))
2508 end
2509  
2510 HealthBar_OnValueChanged(new_value, true);
2511 time_since_bar_update = 0;
2512 bar_set_unreliable = false;
2513  
2514 return true;
2515 end
2516  
2517  
2518  
2519 ---
2520 --- HoTT_Display_Bar_OnUpdate
2521 ---
2522  
2523 function HoTT_Display_Bar_OnUpdate (interval)
2524  
2525 time_since_bar_update = time_since_bar_update + interval;
2526  
2527 if ( ( not this.unit ) and
2528 ( time_since_bar_update > params.bar_unreliable_time ) and
2529 ( not bar_set_unreliable )
2530 ) then
2531 this:SetStatusBarColor(colors.bar.unreliable.r,
2532 colors.bar.unreliable.g,
2533 colors.bar.unreliable.b);
2534 bar_set_unreliable = true;
2535 if ( type(bar_set_unreliable_callback) == "function" ) then
2536 bar_set_unreliable_callback(this);
2537 end
2538  
2539 end
2540  
2541 return true;
2542 end
2543  
2544  
2545  
2546 --------------------------
2547 --- Core functionality ---
2548 --------------------------
2549  
2550 --- NOTE: These need to be moved and made local, once confirmed working
2551  
2552 ---
2553 --- update_tot_health
2554 ---
2555  
2556 function update_tot_health ()
2557 local now = GetTime();
2558 local retval = UnitExists("targettarget");
2559  
2560 if ( retval ) then
2561 HoTT_Display_Bar:SetMinMaxValues(0, UnitHealthMax("targettarget"));
2562 HoTT_Display_Bar:SetValue(UnitHealth("targettarget"));
2563 if ( UnitIsCorpse("targettarget") or
2564 UnitIsDeadOrGhost("targettarget") ) then
2565 HoTT_Display_Dead:Show();
2566 else
2567 HoTT_Display_Dead:Hide();
2568 end
2569 end
2570  
2571 reset_poll_timer();
2572 tprint("update_tot_health", now);
2573 return retval;
2574 end
2575  
2576  
2577  
2578 ---
2579 --- update_tot
2580 ---
2581  
2582 function update_tot ()
2583 local now = GetTime();
2584 local retval;
2585 local may_be_stunned_mob;
2586  
2587 if ( not UnitExists("target") ) then
2588  
2589 dprint("No target to check");
2590  
2591 clear_t();
2592 clear_tot();
2593 clear_bar();
2594 set_display_name_color(no_target_string, colors.name.normal);
2595 HoTT_Display_Bar:Hide();
2596  
2597  
2598 elseif ( ( not UnitIsPlayer("target") ) and
2599 ( not UnitAffectingCombat("target") ) ) then
2600  
2601 dprint("Non-aggro NPC");
2602  
2603 clear_t();
2604 clear_tot();
2605 clear_bar();
2606 set_display_name_color(no_target_string, colors.name.normal);
2607 HoTT_Display_Bar:Hide();
2608  
2609  
2610 elseif ( UnitIsUnit("target", "player") ) then
2611  
2612 dprint("Self-target case");
2613  
2614 target_to_t();
2615 self_to_tot();
2616 if ( update_bar_unit() ) then update_tot_health() end;
2617 set_display_name_color(tot_target_info.name, colors.name.normal);
2618 HoTT_Display_Bar:Show();
2619  
2620  
2621 else
2622  
2623 -- Check stunned mob while we have "target" reliably
2624  
2625 may_be_stunned_mob = ( ( not UnitIsPlayer("target") ) and
2626 ( UnitAffectingCombat("target") ) );
2627  
2628 -- then go ahead and do the check
2629  
2630 if ( UnitExists("targettarget") ) then
2631  
2632 target_to_t();
2633 targettarget_to_tot();
2634 if ( update_bar_unit() ) then update_tot_health() end;
2635 set_display_name_color(tot_target_info.name, colors.name.normal);
2636 HoTT_Display_Bar:Show();
2637  
2638  
2639  
2640 elseif ( may_be_stunned_mob ) then
2641  
2642 if ( new_target_appears_same_as_before() ) then
2643  
2644 dprint("Assuming stunned - same mob");
2645  
2646 -- and keep the old info
2647 -- NOTE THAT THIS DOES NOT UPDATE HEALTH BAR!
2648  
2649 if ( roster[tot_target_info.name] ) then
2650 set_display_name_color(nil, colors.name.unreliable);
2651 else
2652 set_display_name_color(nil, colors.name.unreliable_no_unit);
2653 end
2654  
2655  
2656 else
2657  
2658 dprint("Assuming stunned - new mob");
2659  
2660 set_display_name_color(stunned_string,
2661 colors.name.unreliable);
2662  
2663 end
2664  
2665  
2666 else
2667 --
2668 -- Target probably doesn't have a target (or is stunned PC)
2669 --
2670  
2671 dprint("No target of target found");
2672  
2673 target_to_t();
2674 clear_tot();
2675 clear_bar();
2676 set_display_name_color(no_target_string, colors.name.normal);
2677 HoTT_Display_Bar:Hide();
2678  
2679 end
2680  
2681  
2682 end
2683  
2684 if ( ( this_tot ) and
2685 ( this_tot == player_name ) and
2686 ( this_tot ~= last_tot ) and
2687 ( t_target_info.canattackme ) ) then
2688  
2689 if ( params.aggro_message and
2690 string.find(params.aggro_message, "%S" ) ) then
2691 UIErrorsFrame:AddMessage(params.aggro_message,
2692 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME);
2693 end
2694  
2695 if ( type(HoTT_Aggro_Callback) == "function" ) then
2696 HoTT_Aggro_Callback();
2697 end
2698  
2699 end
2700  
2701 reset_poll_timer();
2702 tprint("update_tot", now);
2703 return tot_target_info;
2704  
2705 end
2706  
2707  
2708 ---------------------------------
2709 --- Params Frame here for now ---
2710 ---------------------------------
2711  
2712  
2713  
2714 --[[
2715  
2716 window_offset[ => ui.display.drops[
2717 window_offset.right => ui.display.inset.right
2718 window_offset.left => ui.display.inset.left
2719 window_offset.object => ui.display.relative_to
2720 window_offset.scale => ui.display.scale
2721 name_alignment => ui.display.tot_name.alignment
2722 name_inset_left => ui.display.tot_name.inset.left
2723 name_inset_right => ui.display.tot_name.inset.right
2724  
2725  
2726 sign changes needed:
2727  
2728 -window_offset[0] => ui.display.drops[0]
2729 -window_offset[1] => ui.display.drops[1]
2730 -window_offset[2] => ui.display.drops[2]
2731 -window_offset.right => ui.display.inset.right
2732  
2733  
2734 specials:
2735  
2736 window_offset.text_offset:
2737  
2738 "CENTER" => (ignore)
2739 "LEFT" => ui.display.tot_name.inset.left
2740 "RIGHT" => -ui.display.tot_name.inset.right
2741  
2742  
2743  
2744 ]]
2745  
2746  
2747  
2748 ---
2749 --- params_refresh
2750 ---
2751  
2752 local function params_refresh ()
2753 refresh_ui_positions();
2754  
2755 end
2756  
2757  
2758  
2759 ---
2760 --- params_frame_int_check
2761 ---
2762  
2763 local function params_frame_int_check (str, err_str)
2764  
2765 local retval;
2766  
2767 if ( string.find(str, "^-?%d+$") ) then
2768 retval = tonumber(str);
2769  
2770 else
2771 message(string.format("%s must be an integer. Previous value restored.",
2772 err_str));
2773 HoTT_Params_Frame_LoadValues();
2774 retval = false;
2775 end
2776  
2777 return retval;
2778 end
2779  
2780  
2781  
2782 ---
2783 --- anw_check
2784 ---
2785  
2786 local function anw_check (obj, dl, dr, tl, tr, field_name_string)
2787  
2788 obj = getglobal(obj or params.ui.display.relative_to);
2789 dl = dl or params.ui.display.inset.left;
2790 dr = dr or params.ui.display.inset.right;
2791 tl = tl or params.ui.display.tot_name.inset.left;
2792 tr = tr or params.ui.display.tot_name.inset.right;
2793  
2794 local retval;
2795  
2796 if ( ( obj:GetRight() - obj:GetLeft() - dl - dr - tl - tr ) <
2797 params.ui.display.tot_name.min_width ) then
2798  
2799 message(string.format(fmt_too_narrow, field_name_string));
2800 HoTT_Params_Frame_LoadValues();
2801 retval = false;
2802  
2803 else
2804 retval = true;
2805  
2806 end
2807  
2808 return retval;
2809 end
2810  
2811  
2812  
2813 ---
2814 --- HoTT_Params_Frame_LoadValues
2815 ---
2816  
2817 function HoTT_Params_Frame_LoadValues ()
2818  
2819 local puid = params.ui.display;
2820  
2821 HoTT_Params_Frame_ObjectName_EditBox:SetText(tostring(puid.relative_to));
2822  
2823 HoTT_Params_Frame_DisplayLeft_EditBox:SetText(tostring(puid.inset.left));
2824 HoTT_Params_Frame_DisplayRight_EditBox:SetText(tostring(puid.inset.right));
2825 HoTT_Params_Frame_TextLeft_EditBox:SetText(
2826 tostring(puid.tot_name.inset.left));
2827 HoTT_Params_Frame_TextRight_EditBox:SetText(
2828 tostring(puid.tot_name.inset.right));
2829  
2830 HoTT_Params_Frame_ButtonLeft:SetChecked(false);
2831 HoTT_Params_Frame_ButtonCenter:SetChecked(false);
2832 HoTT_Params_Frame_ButtonRight:SetChecked(false);
2833  
2834 if ( puid.tot_name.alignment == "CENTER" ) then
2835 HoTT_Params_Frame_ButtonCenter:SetChecked(true);
2836  
2837 elseif ( puid.tot_name.alignment == "LEFT" ) then
2838 HoTT_Params_Frame_ButtonLeft:SetChecked(true);
2839  
2840 elseif ( puid.tot_name.alignment == "RIGHT" ) then
2841 HoTT_Params_Frame_ButtonRight:SetChecked(true);
2842  
2843 end
2844  
2845 HoTT_Params_Frame_Scale_EditBox:SetText(format("%5.3f", puid.scale));
2846  
2847 HoTT_Params_Frame_Drop0_EditBox:SetText(tostring(puid.drops[0]));
2848 HoTT_Params_Frame_Drop1_EditBox:SetText(tostring(puid.drops[1]));
2849 HoTT_Params_Frame_Drop2_EditBox:SetText(tostring(puid.drops[2]));
2850  
2851  
2852 HoTT_Params_Frame_AggroMessage_EditBox:SetText(params.aggro_message);
2853  
2854 end
2855  
2856  
2857  
2858  
2859 ---
2860 --- HoTT_Params_Frame_ObjectName_Changed
2861 ---
2862  
2863 function HoTT_Params_Frame_ObjectName_Changed (new_text)
2864  
2865 local obj = getglobal(new_text);
2866  
2867 if ( not ( obj and
2868 ( type(obj.GetRight == "function") ) and
2869 ( type(obj.GetLeft == "function") )
2870 )
2871 ) then
2872  
2873 message(string.format(fmt_no_obj_edit, new_text));
2874 HoTT_Params_Frame_LoadValues();
2875 refocus_object = HoTT_Params_Frame_ObjectName_EditBox;
2876  
2877 elseif ( anw_check(new_text, nil, nil, nil, nil, "Relative-to object")
2878 ) then
2879  
2880 params.ui.display.relative_to = new_text;
2881 params_refresh();
2882  
2883 else
2884 refocus_object = HoTT_Params_Frame_ObjectName_EditBox;
2885  
2886 end
2887  
2888 end
2889  
2890  
2891  
2892 ---
2893 --- HoTT_Params_Frame_DisplayLeft_Changed
2894 ---
2895  
2896 function HoTT_Params_Frame_DisplayLeft_Changed (new_string)
2897  
2898 local new_number = params_frame_int_check(new_string, "Left display inset");
2899  
2900 if ( new_number and
2901 anw_check(nil, new_number, nil, nil, nil, "Left display inset")
2902 ) then
2903 params.ui.display.inset.left = new_number;
2904 params_refresh();
2905  
2906 else
2907 refocus_object = HoTT_Params_Frame_DisplayLeft_EditBox;
2908  
2909 end
2910  
2911 end
2912  
2913  
2914  
2915 ---
2916 --- HoTT_Params_Frame_DisplayRight_Changed
2917 ---
2918  
2919 function HoTT_Params_Frame_DisplayRight_Changed (new_string)
2920  
2921 local new_number = params_frame_int_check(new_string,"Right display inset");
2922  
2923 if ( new_number and
2924 anw_check(nil, nil, new_number, nil, nil, "Right display inset" )
2925 ) then
2926 params.ui.display.inset.right = new_number;
2927 params_refresh();
2928  
2929 else
2930 refocus_object = HoTT_Params_Frame_DisplayRight_EditBox;
2931  
2932 end
2933  
2934 end
2935  
2936  
2937  
2938 ---
2939 --- HoTT_Params_Frame_TextLeft_Changed
2940 ---
2941  
2942 function HoTT_Params_Frame_TextLeft_Changed (new_string)
2943  
2944 local new_number = params_frame_int_check(new_string, "Left text inset");
2945  
2946 if ( new_number and
2947 anw_check(nil, nil, nil, new_number, nil, "Left text inset")
2948 ) then
2949 params.ui.display.tot_name.inset.left = new_number;
2950 params_refresh();
2951  
2952 else
2953 refocus_object = HoTT_Params_Frame_TextLeft_EditBox;
2954  
2955 end
2956  
2957 end
2958  
2959  
2960  
2961 ---
2962 --- HoTT_Params_Frame_TextRight_Changed
2963 ---
2964  
2965 function HoTT_Params_Frame_TextRight_Changed (new_string)
2966  
2967 local new_number = params_frame_int_check(new_string, "Right text inset");
2968  
2969 if ( new_number and
2970 anw_check(nil, nil, nil, nil, new_number, "Right text inset" )
2971 ) then
2972 params.ui.display.tot_name.inset.right = new_number;
2973 params_refresh();
2974  
2975 else
2976 refocus_object = HoTT_Params_Frame_TextRight_EditBox;
2977  
2978 end
2979  
2980 end
2981  
2982  
2983  
2984 ---
2985 --- HoTT_Params_Frame_Alignment_Changed
2986 ---
2987  
2988 function HoTT_Params_Frame_Alignment_Changed (new_alignment)
2989  
2990 params.ui.display.tot_name.alignment = new_alignment;
2991 params_refresh();
2992  
2993 end
2994  
2995  
2996  
2997 ---
2998 --- HoTT_Params_Frame_Scale_Changed
2999 ---
3000  
3001 function HoTT_Params_Frame_Scale_Changed (new_string)
3002  
3003 local new_number = tonumber(new_string);
3004  
3005 if ( new_number and ( new_number > 0 ) ) then
3006 params.ui.display.scale = new_number;
3007 params_refresh();
3008  
3009 else
3010 message("Scale must be a positive value. Previous value restored.");
3011 HoTT_Params_Frame_LoadValues();
3012 refocus_object = HoTT_Params_Frame_Scale_EditBox;
3013 retval = false;
3014 end
3015  
3016 end
3017  
3018  
3019  
3020 ---
3021 --- HoTT_Params_Frame_Drop0_Changed
3022 ---
3023  
3024 function HoTT_Params_Frame_Drop0_Changed (new_string)
3025  
3026 local new_number = params_frame_int_check(new_string, "No-buff drop");
3027  
3028 if ( new_number ) then
3029 params.ui.display.drops[0] = new_number;
3030 params_refresh();
3031  
3032 else
3033 refocus_object = HoTT_Params_Frame_Drop0_EditBox;
3034  
3035 end
3036  
3037 end
3038  
3039  
3040  
3041 ---
3042 --- HoTT_Params_Frame_Drop1_Changed
3043 ---
3044  
3045 function HoTT_Params_Frame_Drop1_Changed (new_string)
3046  
3047 local new_number = params_frame_int_check(new_string, "First-row drop");
3048  
3049 if ( new_number ) then
3050 params.ui.display.drops[1] = new_number;
3051 params_refresh();
3052  
3053 else
3054 refocus_object = HoTT_Params_Frame_Drop1_EditBox;
3055  
3056 end
3057  
3058 end
3059  
3060  
3061  
3062 ---
3063 --- HoTT_Params_Frame_Drop2_Changed
3064 ---
3065  
3066 function HoTT_Params_Frame_Drop2_Changed (new_string)
3067  
3068 local new_number = params_frame_int_check(new_string, "Second-row drop");
3069  
3070 if ( new_number ) then
3071 params.ui.display.drops[2] = new_number;
3072 params_refresh();
3073  
3074 else
3075 refocus_object = HoTT_Params_Frame_Drop2_EditBox;
3076  
3077 end
3078  
3079 end
3080  
3081  
3082  
3083 ---
3084 --- HoTT_Params_Frame_AggroMessage_Changed
3085 ---
3086  
3087 function HoTT_Params_Frame_AggroMessage_Changed (new_text)
3088  
3089 params.aggro_message = new_text;
3090 params_refresh();
3091  
3092 end
3093  
3094  
3095  
3096 ---
3097 --- launch_params_browser
3098 ---
3099  
3100 -- local
3101 function launch_params_browser ()
3102  
3103 scratch_params = {};
3104  
3105 current_params = params;
3106  
3107 set_deferral_recursively(scratch_params, params,
3108 "scratch_params", "current_params");
3109  
3110 params = scratch_params;
3111 HoTT_Params = scratch_params;
3112  
3113 HoTT_Params_Frame:Show();
3114  
3115 end
3116  
3117  
3118  
3119 ---
3120 --- HoTT_Params_Frame_OnCancel
3121 ---
3122  
3123 function HoTT_Params_Frame_OnCancel ()
3124  
3125 HoTT_Params_Frame_LoadValues(); -- to prevent error messaging
3126 PlaySound("gsTitleOptionExit");
3127 HideUIPanel(HoTT_Params_Frame);
3128  
3129 params = current_params;
3130 HoTT_Params = current_params;
3131  
3132 params_refresh();
3133  
3134 end
3135  
3136  
3137  
3138 ---
3139 --- HoTT_Params_Frame_OnDefaults
3140 ---
3141  
3142 function HoTT_Params_Frame_OnDefaults ()
3143  
3144 params.aggro_message = default_params.aggro_message;
3145  
3146 -- copy_table_recursively(default_params.ui, params.ui);
3147 -- fails as default_params is write-locked and can't be iterated over
3148  
3149 local dpuid = default_params.ui.display;
3150 local puid = params.ui.display;
3151  
3152 puid.relative_to = dpuid.relative_to;
3153 puid.scale = dpuid.scale;
3154 puid.inset.left = dpuid.inset.left;
3155 puid.inset.right = dpuid.inset.right;
3156 puid.drops[0] = dpuid.drops[0];
3157 puid.drops[1] = dpuid.drops[1];
3158 puid.drops[2] = dpuid.drops[2];
3159 puid.tot_name.alignment = dpuid.tot_name.alignment;
3160 puid.tot_name.inset.left = dpuid.tot_name.inset.left;
3161 puid.tot_name.inset.right = dpuid.tot_name.inset.right;
3162  
3163  
3164 HoTT_Params_Frame_LoadValues();
3165  
3166 params_refresh();
3167  
3168 end
3169  
3170  
3171  
3172 ---
3173 --- HoTT_Params_Frame_OnOkay
3174 ---
3175  
3176 function HoTT_Params_Frame_OnOkay ()
3177  
3178 ---
3179 --- Can't blindly accept and close!
3180 ---
3181  
3182 HoTT_Params_Frame_ClearFocus();
3183  
3184 if ( refocus_object ) then
3185 refocus_object:SetFocus();
3186 refocus_object = nil;
3187  
3188 else
3189  
3190  
3191 if ( current_params.aggro_message ~= params.aggro_message ) then
3192 current_params.aggro_message = params.aggro_message;
3193 else
3194 current_params.aggro_message = nil; -- defer to defaults
3195 end
3196  
3197 local puid = params.ui.display;
3198 local cpuid = current_params.ui.display;
3199 local dpuid = default_params.ui.display;
3200  
3201  
3202 if ( ( puid.relative_to ~= dpuid.relative_to ) or
3203 ( puid.scale ~= dpuid.scale ) or
3204 ( puid.inset.left ~= dpuid.inset.left ) or
3205 ( puid.inset.right ~= dpuid.inset.right ) or
3206 ( puid.drops[0] ~= dpuid.drops[0] ) or
3207 ( puid.drops[1] ~= dpuid.drops[1] ) or
3208 ( puid.drops[2] ~= dpuid.drops[2] ) or
3209 ( puid.tot_name.alignment ~= dpuid.tot_name.alignment ) or
3210 ( puid.tot_name.inset.left ~= dpuid.tot_name.inset.left ) or
3211 ( puid.tot_name.inset.right ~= dpuid.tot_name.inset.right )
3212 ) then
3213  
3214 cpuid.relative_to = puid.relative_to;
3215 cpuid.scale = puid.scale;
3216 cpuid.inset.left = puid.inset.left;
3217 cpuid.inset.right = puid.inset.right;
3218 cpuid.drops[0] = puid.drops[0];
3219 cpuid.drops[1] = puid.drops[1];
3220 cpuid.drops[2] = puid.drops[2];
3221 cpuid.tot_name.alignment = puid.tot_name.alignment;
3222 cpuid.tot_name.inset.left = puid.tot_name.inset.left;
3223 cpuid.tot_name.inset.right = puid.tot_name.inset.right;
3224  
3225 else
3226  
3227 current_params.ui = nil; -- defer to defaults
3228  
3229 end
3230  
3231 PlaySound("gsTitleOptionOK");
3232 HideUIPanel(HoTT_Params_Frame);
3233  
3234  
3235 params = current_params;
3236 HoTT_Params = current_params;
3237  
3238 end
3239  
3240 params_refresh();
3241  
3242 end
3243  
3244  
3245  
3246 ---
3247 --- HoTT_Params_Frame_ClearFocus
3248 ---
3249  
3250 function HoTT_Params_Frame_ClearFocus ()
3251  
3252 HoTT_Params_Frame_ObjectName_EditBox:ClearFocus();
3253 HoTT_Params_Frame_DisplayLeft_EditBox:ClearFocus();
3254 HoTT_Params_Frame_DisplayRight_EditBox:ClearFocus();
3255 HoTT_Params_Frame_TextLeft_EditBox:ClearFocus();
3256 HoTT_Params_Frame_TextRight_EditBox:ClearFocus();
3257 HoTT_Params_Frame_Scale_EditBox:ClearFocus();
3258 HoTT_Params_Frame_Drop0_EditBox:ClearFocus();
3259 HoTT_Params_Frame_Drop1_EditBox:ClearFocus();
3260 HoTT_Params_Frame_Drop2_EditBox:ClearFocus();
3261 HoTT_Params_Frame_AggroMessage_EditBox:ClearFocus();
3262  
3263 end
3264  
3265  
3266  
3267 ---
3268 --- HoTT_Params_Frame_CheckRefocus
3269 ---
3270  
3271 function HoTT_Params_Frame_CheckRefocus ()
3272  
3273 if ( refocus_object ) then
3274 refocus_object:SetFocus();
3275 refocus_object = nil;
3276 end
3277  
3278 end