vanilla-wow-addons – Rev 1
?pathlinks?
--[[
TargetOfTarget: Keeps Target of Target shown below TargetFrame
Copyright (c) 2005 Itchyban of Veeshan
Version 2.1 Beta 1
$Header: /usr/local/cvsroot/WoW/Interface/AddOns/TargetOfTarget/TargetOfTarget.lua,v 1.122 2005/05/30 17:43:33 jeff Exp $
]]
---
--- Where should the dead/ghost/corpse be done?
---
-------------------------
--- Versioning params ---
-------------------------
---
--- WARNING
---
--- Checking this into CVS or RCS without -ko can screw this up for now
---
--- (If you don't know what CVS or RCS are, you can ignore that)
---
local version_table_data = {};
version_table_data.object = "1.0";
version_table_data.apps = {};
version_table_data.apps.TargetOfTarget = {};
version_table_data.apps.TargetOfTarget.params = "2.0";
version_table_data.apps.TargetOfTarget.lua_CVS = "$Revision: 1.122 $";
version_table_data.apps.TargetOfTarget.XML_CVS = "";
-------------------------------------------------------
--- Check for existing globals before declaring any ---
-------------------------------------------------------
local globals_to_check = {
"HoTT_Aggro_Callback",
"HoTT_Debug",
"HoTT_Display",
"HoTT_Display_Bar",
"HoTT_Display_Bar_OnEvent",
"HoTT_Display_Bar_OnUpdate",
"HoTT_Display_Bar_OnValueChanged",
"HoTT_Display_NameArea",
"HoTT_Display_NameArea_Text",
"HoTT_Display_OnClick",
"HoTT_Display_OnEvent",
"HoTT_Display_OnUpdate",
"HoTT_InputBoxTemplate",
"HoTT_InputBoxTemplateLeft",
"HoTT_InputBoxTemplateMiddle",
"HoTT_InputBoxTemplateRight",
"HoTT_MainFrame",
"HoTT_MainFrame_OnEvent",
"HoTT_MainFrame_OnLoad",
"HoTT_Params",
"HoTT_Params_FourDigitInputBox",
"HoTT_Params_Frame",
"HoTT_Params_Frame_AggroMessage_Changed",
"HoTT_Params_Frame_AggroMessage_EditBox",
"HoTT_Params_Frame_AggroText",
"HoTT_Params_Frame_AlertsText",
"HoTT_Params_Frame_Alignment_Changed",
"HoTT_Params_Frame_BuffsText",
"HoTT_Params_Frame_BuffsText0",
"HoTT_Params_Frame_BuffsText1",
"HoTT_Params_Frame_BuffsText2",
"HoTT_Params_Frame_ButtonCenter",
"HoTT_Params_Frame_ButtonCenterText",
"HoTT_Params_Frame_ButtonLeft",
"HoTT_Params_Frame_ButtonLeftText",
"HoTT_Params_Frame_ButtonRight",
"HoTT_Params_Frame_ButtonRightText",
"HoTT_Params_Frame_Cancel",
"HoTT_Params_Frame_CheckRefocus",
"HoTT_Params_Frame_ClearFocus",
"HoTT_Params_Frame_Defaults",
"HoTT_Params_Frame_DisplayInsetsText",
"HoTT_Params_Frame_DisplayLeft_Changed",
"HoTT_Params_Frame_DisplayLeft_EditBox",
"HoTT_Params_Frame_DisplayLeft_EditBoxLeft",
"HoTT_Params_Frame_DisplayLeft_EditBoxMiddle",
"HoTT_Params_Frame_DisplayLeft_EditBoxRight",
"HoTT_Params_Frame_DisplayRight_Changed",
"HoTT_Params_Frame_DisplayRight_EditBox",
"HoTT_Params_Frame_DisplayRight_EditBoxLeft",
"HoTT_Params_Frame_DisplayRight_EditBoxMiddle",
"HoTT_Params_Frame_DisplayRight_EditBoxRight",
"HoTT_Params_Frame_Drop0_Changed",
"HoTT_Params_Frame_Drop0_EditBox",
"HoTT_Params_Frame_Drop0_EditBoxLeft",
"HoTT_Params_Frame_Drop0_EditBoxMiddle",
"HoTT_Params_Frame_Drop0_EditBoxRight",
"HoTT_Params_Frame_Drop1_Changed",
"HoTT_Params_Frame_Drop1_EditBox",
"HoTT_Params_Frame_Drop1_EditBoxLeft",
"HoTT_Params_Frame_Drop1_EditBoxMiddle",
"HoTT_Params_Frame_Drop1_EditBoxRight",
"HoTT_Params_Frame_Drop2_Changed",
"HoTT_Params_Frame_Drop2_EditBox",
"HoTT_Params_Frame_Drop2_EditBoxLeft",
"HoTT_Params_Frame_Drop2_EditBoxMiddle",
"HoTT_Params_Frame_Drop2_EditBoxRight",
"HoTT_Params_Frame_DropsText",
"HoTT_Params_Frame_LoadValues",
"HoTT_Params_Frame_ObjectName_Changed",
"HoTT_Params_Frame_ObjectName_EditBox",
"HoTT_Params_Frame_ObjectName_EditBoxLeft",
"HoTT_Params_Frame_ObjectName_EditBoxMiddle",
"HoTT_Params_Frame_ObjectName_EditBoxRight",
"HoTT_Params_Frame_ObjectText",
"HoTT_Params_Frame_Okay",
"HoTT_Params_Frame_OnCancel",
"HoTT_Params_Frame_OnDefaults",
"HoTT_Params_Frame_OnOkay",
"HoTT_Params_Frame_PositioningText",
"HoTT_Params_Frame_Scale_Changed",
"HoTT_Params_Frame_Scale_EditBox",
"HoTT_Params_Frame_Scale_EditBoxLeft",
"HoTT_Params_Frame_Scale_EditBoxMiddle",
"HoTT_Params_Frame_Scale_EditBoxRight",
"HoTT_Params_Frame_ShowAlignmentFrame",
"HoTT_Params_Frame_Title",
"HoTT_Params_Frame_Title_Text",
"HoTT_Params_Frame_TextInsetsText",
"HoTT_Params_Frame_TextLeft_Changed",
"HoTT_Params_Frame_TextLeft_EditBox",
"HoTT_Params_Frame_TextLeft_EditBoxLeft",
"HoTT_Params_Frame_TextLeft_EditBoxMiddle",
"HoTT_Params_Frame_TextLeft_EditBoxRight",
"HoTT_Params_Frame_TextRight_Changed",
"HoTT_Params_Frame_TextRight_EditBox",
"HoTT_Params_Frame_TextRight_EditBoxLeft",
"HoTT_Params_Frame_TextRight_EditBoxMiddle",
"HoTT_Params_Frame_TextRight_EditBoxRight",
"HoTT_Set_XML_CVS",
"HoTT_Settings",
"HoTT_Settings_Unparsed",
"SLASH_TARGETOFTARGET_TOT1",
"SLASH_TARGETOFTARGET_TOT2",
"SLASH_TARGETOFTARGET_TOTOFF1",
"SLASH_TARGETOFTARGET_TOTON1",
"SlashCmdList[\"TARGETOFTARGET_TOT\"]",
"SlashCmdList[\"TARGETOFTARGET_TOTON\"]",
"SlashCmdList[\"TARGETOFTARGET_TOTOFF\"]",
}
local slash_commands_to_check = {
"/tot",
"/toton",
"/totoff",
"/hott"
}
local all_checked_globals = {};
local k,v
for k,v in pairs (globals_to_check) do
table.insert(all_checked_globals, v);
end
for k,v in pairs (slash_commands_to_check) do
table.insert(all_checked_globals, v);
end
local global_check_failures = nil;
for k,v in pairs(globals_to_check) do
if ( type(getglobal(v)) ~= "nil" ) then
if ( type(global_check_failures) == "nil" ) then
global_check_failures = {};
end
table.insert(global_check_failures, v);
end
end
---
--- NOTE: This is not quite right yet, as slash_commands_to_check
--- somehow needs to be localized in the process
---
---
--- check_localized_slash_commands
---
local function check_localized_slash_commands ()
for k,v in pairs(slash_commands_to_check) do
local scl_key, scl_value;
for scl_key, scl_value in pairs(SlashCmdList) do
local idx = 1;
local slash_var = "SLASH_"..scl_key..idx;
local slash_cmd_string = getglobal(slash_var);
repeat
if ( slash_cmd_string == v ) then
if ( type(global_check_failures) == "nil" ) then
global_check_failures = {};
end
table.insert(global_check_failures,
v .. "( == " .. slash_cmd_string .. " )");
end
idx = idx+1;
slash_var = "SLASH_"..scl_key..idx;
slash_cmd_string = getglobal(slash_var);
until ( not slash_cmd_string )
end
end
end
---
--- "Safe" to declare globals now
--- at least we know what we are going to clobber!
---
---
--- Stray Global, here for clarity
---
--- HoTT_Set_XML_CVS
---
--- Called <OnLoad> in the XML file
--- Should probably make sure that it "flows through" properly
--- before I start relying on it for anything
function HoTT_Set_XML_CVS (version_string)
version_table_data.apps.TargetOfTarget.XML_CVS = version_string;
return version_table_data.apps.TargetOfTarget.XML_CVS;
end
---
--- HoTT_Patch_Warning
---
local function HoTT_Patch_Warning ()
local patch_not_ok = (type(UnitIsVisible) ~= "function");
if ( patch_not_ok ) then
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.");
end
return patch_not_ok;
end
---------------------------
--- Localizable Strings ---
---------------------------
local no_target_string = "";
local stunned_string = "(stunned)";
local undetermined_string = "(undetermined)";
local aggro_message = "--- AGGRO --- AGGRO ---";
local ras_format = "Duplicate UnitID for %s; %s, %s";
local toton_string = "/toton";
local totoff_string = "/totoff";
local loaded_string = "Itchy's TargetOfTarget AddOn loaded";
local loaded_variables_string = "TargetOfTarget SavedVariables settings restored"
local shift_at_load_string = "TargetOfTarget [Shift] at load";
local invalid_settings_string = "TargetOfTarget invalid saved settings. Copied to HoTT_Settings_Unparsed";
local on_string = "TargetOfTarget is now " .. GREEN_FONT_COLOR_CODE .. "ON";
local off_string = "TargetOfTarget is now OFF";
local saved_format = "TargetOfTarget saved settings: %s";
local save_failed_format = "TargetOfTarget no settings to save for: %s";
local restored_defaults = "TargetOfTarget restored default settings";
local restored_format = "TargetOfTarget restored settings: %s";
local restore_failed_format = "TargetOfTarget no settings to restore for: %s";
local help_message = [[
Itchy's ToT
/tot on -- enable ToT (or /toton)
/tot off -- disable ToT (or /totoff)
/tot params -- brings up dialog to edit aggro message and position display.
/tot version -- displays file versions. Please report these with any bugs or suggestions.]];
local help_ui = "/tot ui is obsolete. Use /tot params";
local string_global_check_failed = "TargetOfTarget found possible " ..
RED_FONT_COLOR_CODE .. "global variable conflict" ..
FONT_COLOR_CODE_CLOSE .. " on loading. For details, /tot globals";
local string_global_list = "TargetOfTarget global variable check list is in TargetOfTarget.lua, if you are curious."
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:";
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."
local string_assert_set_exists_fmt = "Change settings to %s, no set returned";
local string_assert_no_player = "Player name unexpectedly missing";
local string_assert_no_realm = "Realm name unexpectedly missing";
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";
local string_no_ui_description = "(no description given)"
local string_cleared_ui = "Cleared ToT UI parameters";
local string_default_ui_copied = "Copied default ToT UI parameters to your active set";
local fmt_copied_ui = "Copied ToT UI parameters to your active set: %s -- %s";
local fmt_no_such_ui = "No such ToT UI parameters, %s, available. For help, /tot ui";
local string_help_cmd = "help";
local string_on_cmd = "on";
local string_off_cmd = "off";
local string_ui_cmd = "ui";
local string_version_cmd = "version";
local string_globals_cmd = "globals";
local string_params_cmd = "params"
local string_unrecognized_cmd_help = "Unrecognized command -- try /tot help";
local string_slash_tot = "/tot"
local string_slash_hott = "/hott"
local ellipsis = "..."; -- Used as suffix in shortened names
local fmt_ro_access = "No write access to %s permitted.";
local fmt_not_a_table = "%s not a table, passed as %s";
local fmt_too_narrow = "Too narrow a window. %s set to previous value.";
local fmt_no_obj_edit = "%s does not exist in this UI. Previous value restored.";
-----------------------------------------
--- Parameter list and default values ---
-----------------------------------------
--- Application defaults
---
--- DO NOT CHANGE THESE HERE
---
--- Use HoTT_Params handle to keep overrides in SavedVariables
---
local default_params = {};
default_params.bar_unreliable_time = 0.500; -- time after which bar
-- is marked "unreliable"
default_params.poll_timer_period = 0.400; -- check ToT/health this often
-- (Only if not getting events)
default_params.aggro_message = aggro_message; -- or false or blank to disable
default_params.ui = {};
default_params.ui.display = {};
default_params.ui.display.relative_to = "TargetFrameHealthBar";
default_params.ui.display.scale = 1.0;
default_params.ui.display.inset = {};
default_params.ui.display.inset.left = -5;
default_params.ui.display.inset.right = -3;
default_params.ui.display.drops = {};
default_params.ui.display.drops[0] = 12;
default_params.ui.display.drops[1] = 36;
default_params.ui.display.drops[2] = 58;
default_params.ui.display.tot_name = {};
default_params.ui.display.tot_name.min_width = 20; -- prevent dynamic
-- adjustment too small
default_params.ui.display.tot_name.alignment = "CENTER";
default_params.ui.display.tot_name.inset = {};
default_params.ui.display.tot_name.inset.left = 6; -- padding for trimming
default_params.ui.display.tot_name.inset.right = 6; -- of strings to fit
--------------------------------
--- Locking tables read-only ---
--------------------------------
---
--- format_table_key
---
local function format_table_key (table_string, k)
local table_key;
if ( type(k) == "string" ) then
table_key = string.format("%s.%s", table_string, k);
else
table_key = string.format("%s[%s]", table_string, tostring(k));
end
return table_key;
end
---
--- wrap_readonly
---
local function wrap_readonly (table1, table_name)
if ( type(table_name) ~= "string" ) then
table_name = "";
end
local wrapper = {};
local ro_mt = {};
ro_mt.__index = table1;
ro_mt.__newindex = function (t,k,v)
error(string.format(fmt_ro_access,
format_table_key(table_name, k)),
2);
end;
setmetatable(wrapper, ro_mt);
return wrapper;
end
---
--- wrap_readonly_recursively
---
local function wrap_readonly_recursively (table1, table_name, visited)
local k,v;
local next_table_name;
local wrapper;
if ( type(table_name) ~= "string" ) then
table_name = "";
end
if ( not visited ) then
visited = {};
visited[table1] = true;
end
for k,v in pairs(table1) do
if ( ( type(v) == "table" ) and
( not visited[v] ) ) then
next_table_name = format_table_key(table_name, k);
table1[k] = wrap_readonly_recursively (v, next_table_name, visited)
end
end
wrapper = wrap_readonly(table1, table_name);
return wrapper;
end
---
--- Lock default_params
---
default_params = wrap_readonly_recursively(default_params, "default_params");
-----------------------------
--- Local state variables ---
-----------------------------
local hott_is_on; -- master
local t_target_info = {}; -- the "target" for which ToT applies
local tot_target_info = {}; -- the ToT itself
local player_name;
local realm_name;
local last_tot; -- name of the last ToT for "AGGRO" messaging
local this_tot;
local roster = {}; -- reverse lookup for UnitIDs
local roster_assertion_seen; -- so as to only show an error once
local time_since_bar_update = 0; -- internal for bar coloring
local bar_set_unreliable = nil; -- internal for bar coloring
local variables_loaded = false; -- Event has fired
local saved_variables_loaded = false; -- Event responded to completely
local this_frame_tot; -- internals for detecting most target changes
local last_frame_tot;
local poll_timer = 0; -- handles the cases events don't cover
local poll_timer_fired = false;
local placed_names = {}; -- cache for name strings that fit
local refocus_object = nil; -- Params frame object that needs focus
------------------------
--- Local parameters ---
------------------------
--- UnitIDs that generate UNIT_HEALTH events (health bar reliability check)
local units_getting_events = {};
units_getting_events.player = true;
units_getting_events.pet = true;
for idx = 1, 4 do
units_getting_events[format("party%i",idx)] = true;
units_getting_events[format("partypet%i",idx)] = true;
end
for idx = 1, 40 do
units_getting_events[format("raid%i",idx)] = true;
units_getting_events[format("raidpet%i",idx)] = true;
end
--- Colors for bars and for text
local colors = {};
colors.bar = {};
colors.bar.unreliable = { r = 1.00, g = 1.00, b = 1.00 };
colors.name = {};
colors.name.dead = { r = 0.50, g = 0.50, b = 0.50 };
colors.name.normal = { r = NORMAL_FONT_COLOR.r,
g = NORMAL_FONT_COLOR.g,
b = NORMAL_FONT_COLOR.b };
colors.name.unreliable = { r = 1.00, g = 1.00, b = 1.00 };
colors.name.unreliable_no_unit = { r = 0.83, g = 0.83, b = 0.83 };
colors.name.undetermined = { r = NORMAL_FONT_COLOR.r,
g = NORMAL_FONT_COLOR.g,
b = NORMAL_FONT_COLOR.b };
----------------------------------------------------------------
--- Globals for pre-release testing/tuning without reloading ---
----------------------------------------------------------------
HoTT_Debug = {};
HoTT_Debug.enabled = false;
HoTT_Debug.pn = placed_names;
HoTT_Debug.pnt = {};
---
--- dprint
---
local function dprint (message)
if ( HoTT_Debug.enabled ) then
if ( type(print) == "function" ) then
print(message);
else
DEFAULT_CHAT_FRAME:AddMessage(message)
end
end
end
---
--- tprint
---
local function tprint (message, relative_time)
if ( HoTT_Debug.enabled ) then
if ( not relative_time ) then
relative_time = 0;
end
dprint(format("%7.3f %s",
GetTime() - relative_time,
tostring(message)));
end
end
-------------------------------
--- Stubbed local functions ---
-------------------------------
local refresh_ui_positions;
local position_display;
local update_tot;
local update_tot_health;
local reset_poll_timer;
local launch_params_browser;
--------------------
--- Other locals ---
--------------------
--local current_params;
---------------------
--- Table utility ---
---------------------
---
--- clear_table
---
local function clear_table (table1)
local k,v;
for k,v in pairs(table1) do
table1[k] = nil;
end
return table;
end
---
--- copy_table_recursively
---
--- only used by add_settings_object_versioning()
--- will not copy non-iteratable tables
local function copy_table_recursively (table1, table2, missing_keys_only)
---
--- NOTE: Is this kind of potential self-copy "safe" in Lua?
--- Would be if iterator is static -- CHECK THIS!
--- Also applies to copy_table()
---
---
--- NOTE: Lua logic means that a nil entry in the destination
--- is always overwritten, even with missing_keys_only true
---
---
--- NOTE: Should really not copy field if dest == source
--- This should prevent infinite recursion as well
---
local k,v, copy_this
for k,v in pairs (table1) do
copy_this = ( table2[k] ~= v ) and
( ( not missing_keys_only ) or
( ( table2[k] == nil ) and ( v ~= nil ) ) );
if ( type(v) == "table" ) then
if ( copy_this ) then
table2[k] = {};
end
copy_table_recursively(v, table2[k], missing_keys_only);
else
if ( copy_this ) then
table2[k] = v;
end
end
end
return table2;
end
---
--- copy_table_structure
---
local function copy_table_structure (table1, table2, visited_nodes)
local k,v
if ( not visited_nodes ) then
visited_nodes = {};
visited_nodes[tostring(table1)] = true;
end
for k,v in pairs (table1) do
if ( ( type(v) == "table" ) and
( not ( visited_nodes[tostring(v)] ) ) ) then
if ( not ( type(table2[k]) == "table" ) ) then
table2[k] = {};
end
visited_nodes[tostring(v)] = true;
copy_table_structure(v, table2[k], visited_nodes);
end
end
return table2;
end
----------------------------
--- Handling params sets ---
----------------------------
local params = {}; -- will point to the current params object
HoTT_Params = {}; -- as well as a global handle to it
local scratch_params = {}; -- need something before HoTT_Settings is ready
---
--- set_deferral
---
local function set_deferral (child, parent, child_name, parent_name)
if ( type(child_name) ~= "string" ) then
child_name = "";
end
if ( type(parent_name) ~= "string" ) then
parent_name = "";
end
if ( type(child) ~= "table" ) then
error(string.format(fmt_not_a_table, child_name, "child"));
end
if ( type(parent) ~= "table" ) then
error(string.format(fmt_not_a_table, parent_name, "parent"));
end
local mt_defer = {};
mt_defer.__index = function (t, k)
local retval;
local pv = parent[k];
if ( type(pv) == "table" ) then
t[k] = {};
set_deferral(t[k], parent[k],
child_name, parent_name);
retval = t[k];
else
retval = pv;
end
return retval;
end;
-- mt_defer.__newindex = function (t, k, v)
--
-- local pv = parent[k];
--
-- if ( pv ) then
-- rawset(t, k, v); -- need rawset!
--
-- else
-- error(string.format(fmt_no_such_field,
-- format_table_key(parent_name, k)),
-- 2);
-- end
--
-- end;
setmetatable(child, mt_defer);
return child;
end
---
--- set_deferral_recursively
---
local function set_deferral_recursively (child, parent,
child_name, parent_name, visited)
local k,v;
local next_child_name;
local next_parent_name;
if ( type(child_name) ~= "string" ) then
child_name = "";
end
if ( type(parent_name) ~= "string" ) then
parent_name = "";
end
if ( type(child) ~= "table" ) then
error(string.format(fmt_not_a_table, child_name, "child"));
end
if ( type(parent) ~= "table" ) then
error(string.format(fmt_not_a_table, parent_name, "parent"));
end
if ( not visited ) then
visited = {};
visited[child] = true;
end
for k,v in pairs(child) do
if ( ( type(v) == "table" ) and
( not visited[v] ) ) then
if ( type(parent[k]) ~= "table" ) then
---
--- WARNING: This wipes masking data
---
child[k] = nil;
else
next_child_name = format_table_key(child_name, k);
next_parent_name = format_table_key(parent_name, k);
set_deferral_recursively (v, parent[k],
next_child_name,
next_parent_name,
visited);
end
end
end
set_deferral(child, parent, child_name, parent_name);
return child;
end
---
--- init_preload_params
---
local function init_preload_params ()
set_deferral_recursively(scratch_params, default_params,
"scratch_params", "default_params");
params = scratch_params;
HoTT_Params = scratch_params;
return params;
end
--- and init_preload_params for safety!
init_preload_params();
---
--- get_settings_set
---
local function get_settings_set (set_name)
return HoTT_Settings._sets[set_name];
end
---
--- change_settings
---
local function change_settings (set_name)
-- Assumes set_name exists and is valid
local new_settings_set = get_settings_set(set_name);
assert(new_settings_set, format(string_assert_set_exists_fmt,
tostring(set_name)));
set_deferral_recursively(new_settings_set, default_params,
"HoTT_Params", "default_params");
-- params.hott_was_on = hott_is_on; -- this borks on-load behavior?
params = new_settings_set;
HoTT_Params = new_settings_set;
refresh_ui_positions();
return params;
end
---
--- restore_defaults
---
local function restore_defaults ()
clear_table(params);
refresh_ui_positions();
-- DEFAULT_CHAT_FRAME:AddMessage(restored_defaults);
return params;
end
-----------------------------------------------
--- Parameters sets and persistent settings ---
-----------------------------------------------
---
--- NOTE: I should split these out some day
---
--- outside variables refered to:
---
--- player_name
--- realm_name
--- HoTT_Settings
---
--- various strings
---
---
--- The following methods assume that the object is initalized
--- and is self-consistent
---
---
--- get_default_settings_set_name
---
local function get_default_settings_set_name ()
--- DO NOT LOCALIZE THIS FORMAT STRING
--- Doing so will invalidate settings if the player changes locale
return format("%s of %s", player_name, realm_name);
end
---
--- settings_set_exists
---
local function settings_set_exists (set_name)
return HoTT_Settings._sets[set_name];
end
---
--- init_settings_set
---
local function init_settings_set (set_name)
HoTT_Settings._sets[set_name] = {};
return HoTT_Settings._sets[set_name];
end
---
--- get_active_settings_set_name
---
local function get_active_settings_set_name ()
return HoTT_Settings._players[realm_name][player_name].active_set;
end
---
--- change_active_settings_set
---
local function change_active_settings_set (set_name)
HoTT_Settings._players[realm_name][player_name].active_set = set_name;
return HoTT_Settings._sets[set_name];
end
---
--- get_active_settings_set
---
local function get_active_settings_set ()
return get_settings_set(get_active_settings_set_name());
end
---
--- add_settings_object_versioning
---
local function add_settings_object_versioning (settings_object)
if ( type(settings_object._version) == "table" ) then
clear_table(settings_object._version)
else
settings_object._version = {};
end
copy_table_recursively(version_table_data, settings_object._version);
return settings_object;
end
---
--- NOTE: These will need serious re-working
---
---
--- valid_settings_object
---
local function valid_settings_object (settings_object)
return ( type(settings_object) == "table") and
( type(settings_object._version) == "table" ) and
( string.sub(settings_object._version.object, 1, 2) == "1." )
;
end
---
--- repopulate_settings_object
---
local function repopulate_settings_object (settings_object)
-- nothing to do right now
return settings_object;
end
--- ########################################
--- update_settings_object ##### THIS ONE NEEDS TO BE CHANGED #####
--- ########################################
local function update_settings_object (settings_object)
-- nothing to do right now, only one valid version
return settings_object;
end
---
--- load_saved_settings
---
local function load_saved_settings ()
---
--- A mish-mosh of validation, object creation, initialization
--- error reporting, and some application-specific tie-ins
---
--- Will need to be cleaned up a lot to split out
---
---
--- Should really use getglobal(), but can't find "setglobal()"
---
local player_ptr;
local set_ptr;
local default_set_name;
local active_set_name;
if ( not valid_settings_object(HoTT_Settings) ) then
if (HoTT_Settings) then
HoTT_Settings_Unparsed = HoTT_Settings;
DEFAULT_CHAT_FRAME:AddMessage(invalid_settings_string);
end
-- Initialize from scratch
dprint("initializing object");
HoTT_Settings = {};
add_settings_object_versioning(HoTT_Settings);
HoTT_Settings._players = {};
HoTT_Settings._sets = {};
end
-- Add anything that is lost in the write/read cycle
-- Ok, we have a valid, but possibly outdated settings object
dprint("repopulate and update");
repopulate_settings_object(HoTT_Settings);
update_settings_object(HoTT_Settings);
-- Now its good to go, though possibly empty
assert(player_name, string_assert_no_player);
assert(realm_name, string_assert_no_realm);
-- Make sure this player's default set exists first
default_set_name = get_default_settings_set_name();
dprint("default_set_name: "..tostring(default_set_name));
if ( not settings_set_exists(default_set_name) ) then
dprint("initializing default set for "..tostring(default_set_name));
init_settings_set(default_set_name);
end
-- Then make sure the player exists
if ( not HoTT_Settings._players[realm_name] ) then
dprint("._players");
HoTT_Settings._players[realm_name] = {};
end
if ( not HoTT_Settings._players[realm_name][player_name] ) then
dprint("._players."..tostring(realm_name));
HoTT_Settings._players[realm_name][player_name] = {};
end
if ( not HoTT_Settings._players[realm_name][player_name] ) then
dprint("._players."..tostring(realm_name)..tostring(player_name));
HoTT_Settings._players[realm_name][player_name] = {};
end
-- and tie them to their default set if they don't have one already
active_set_name = get_active_settings_set_name();
if ( not active_set_name ) then
dprint("change to default");
change_active_settings_set(default_set_name);
end
-- Realm, player, and default set should be present now
-- Check that their active set is a good one
active_set_name = get_active_settings_set_name();
if ( not settings_set_exists(active_set_name) ) then
-- Go to their default set
DEFAULT_CHAT_FRAME:AddMessage(format(restore_failed_format,
active_set_name));
change_active_settings_set(default_set_name);
DEFAULT_CHAT_FRAME:AddMessage(format(restored_format,
active_set_name));
end
-- which really exists
dprint("Change to: "..tostring(active_set_name));
return change_settings(active_set_name);
end
-----------------------
-----------------------
--- LOCAL FUNCTIONS ---
-----------------------
-----------------------
--------------------------
--- target_info tables ---
--------------------------
---
--- clear_target_info
---
local function clear_target_info (target_info)
clear_table(target_info);
return target_info;
end
---
--- copy_target_info
---
local function copy_target_info (target_info1, target_info2)
local k,v;
clear_target_info(target_info2);
for k,v in pairs(target_info1) do
target_info2[k] = v;
end
return target_info2;
end
---
--- exact_target_info
---
local function exact_target_info (target_info1, target_info2)
return ( target_info1.name == target_info2.name ) and
( target_info1.level == target_info2.level ) and
( target_info1.sex == target_info2.sex ) and
( target_info1.player == target_info2.player ) and
( target_info1.enemy == target_info2.enemy ) and
( target_info1.canattack == target_info2.canattack ) and
( target_info1.canattackme == target_info2.canattackme ) and
( target_info1.friend == target_info2.friend ) and
( target_info1.shapeshifted == target_info2.shapeshifted );
end
---
--- clear_t
---
local function clear_t ()
clear_target_info(t_target_info);
return t_target_info;
end
---
--- clear_tot
---
local function clear_tot ()
last_tot = tot_target_info.name;
clear_target_info(tot_target_info);
this_tot = nil;
return tot_target_info;
end
---
--- target_to_t
---
local function target_to_t ()
if ( UnitExists("target") ) then
t_target_info.name = UnitName("target");
t_target_info.level = UnitLevel("target");
t_target_info.sex = UnitSex("target");
t_target_info.player = UnitIsPlayer("target");
t_target_info.enemy = UnitIsEnemy("target", "player");
t_target_info.canattack = UnitCanAttack("player", "target");
t_target_info.canattackme = UnitCanAttack("target", "player");
t_target_info.friend = UnitIsFriend("target", "player");
t_target_info.shapeshifted = nil; -- for later expansion
else
clear_t();
end
return t_target_info;
end
---
--- targettarget_to_tot
---
local function targettarget_to_tot ()
last_tot = tot_target_info.name;
if ( UnitExists("targettarget") ) then
tot_target_info.name = UnitName("targettarget");
tot_target_info.level = UnitLevel("targettarget");
tot_target_info.sex = UnitSex("targettarget");
tot_target_info.player = UnitIsPlayer("targettarget");
tot_target_info.enemy = UnitIsEnemy("targettarget", "player");
tot_target_info.canattack = UnitCanAttack("player", "targettarget");
tot_target_info.canattackme = UnitCanAttack("targettarget", "player");
tot_target_info.friend = UnitIsFriend("targettarget", "player");
tot_target_info.shapeshifted = nil; -- for later expansion
else
clear_tot();
end
this_tot = tot_target_info.name;
return tot_target_info;
end
---
--- self_to_tot
---
local function self_to_tot ()
-- Yep, this could be simpler, but leave it be for clarity
-- It shouldn't be called except on target change to self
-- and then when the idle check times out
last_tot = tot_target_info.name;
if ( UnitExists("player") ) then
tot_target_info.name = UnitName("player");
tot_target_info.level = UnitLevel("player");
tot_target_info.sex = UnitSex("player");
tot_target_info.player = UnitIsPlayer("player");
tot_target_info.enemy = UnitIsEnemy("player", "player");
tot_target_info.canattack = UnitCanAttack("player", "player");
tot_target_info.canattackme = UnitCanAttack("player", "player");
tot_target_info.friend = UnitIsFriend("player", "player");
tot_target_info.shapeshifted = nil; -- for later expansion
else
clear_tot();
end
this_tot = tot_target_info.name;
return tot_target_info;
end
---
--- new_target_appears_same_as_before
---
local function new_target_appears_same_as_before ()
local retval = UnitExists("target");
if ( retval ) then
local target_sex = UnitSex("target");
if ( ( UnitName("target") ~= t_target_info.name ) or
( UnitLevel("target") ~= t_target_info.level ) ) then
retval = false;
elseif ( target_sex == t_target_info.sex ) then
retval = true;
-- well, except for shapeshifted mobs,
-- which we very poorly handle with
elseif ( (target_sex == 2) or (t_target_info.sex == 2) ) then
retval = true;
else
retval = false;
end
end
return retval;
end
------------------------------------
--- Maintain a roster of UnitIDs ---
------------------------------------
---
--- insert_possible_unit
---
local function insert_possible_unit (unit, type)
local retval = true;
local name = UnitName(unit);
local existing_unit;
if ( name and (name ~= UNKNOWNOBJECT) and (name ~= UKNOWNBEING) ) then
existing_unit = roster[name];
if ( existing_unit and
( string.sub(existing_unit, 1, string.len(type)) == type ) ) then
assert(roster_assertion_seen,
format(ras_format,
name, existing_unit, unit));
roster_assertion_seen = true;
retval = nil;
else
roster[name] = unit;
retval = name;
end
end
return retval;
end
---
--- update_roster
---
local function update_roster ()
local retval = true;
local this_ok;
local unit, name, idx;
local nraid = GetNumRaidMembers();
local nparty = GetNumPartyMembers();
clear_table(roster);
if ( nraid > 1 ) then
for idx = 1, 40 do -- as they are by slot, and may not be sequential
unit = format("raid%i",idx);
this_ok = insert_possible_unit(unit, "raid");
retval = retval and this_ok;
unit = format("raidpet%i",idx);
this_ok = insert_possible_unit(unit, "raidpet");
retval = retval and this_ok;
end
end
if ( nparty > 1 ) then
for idx = 1, 4 do -- its only 4 and nparty-1 was seemingly one short
unit = format("party%i",idx);
this_ok =insert_possible_unit(unit, "party");
retval = retval and this_ok;
unit = format("partypet%i",idx);
this_ok =insert_possible_unit(unit, "partypet");
retval = retval and this_ok;
end
end
this_ok = insert_possible_unit("player", "player");
retval = retval and this_ok;
this_ok = insert_possible_unit("pet", "pet");
retval = retval and this_ok;
return retval;
end
---
--- best_tot_unit
---
local function best_tot_unit()
local retval = nil;
if ( ( UnitExists("targettarget") ) and
( UnitName("targettarget") == tot_target_info.name ) ) then
retval = "targettarget";
else
retval = roster[tot_target_info.name];
end
return retval;
end
---
--- update_bar_unit
---
local function update_bar_unit()
-- Returns true unless "real" unit is known not to have changed
local try_unit = roster[tot_target_info.name];
local old_unit = HoTT_Display_Bar.unit;
if ( not units_getting_events[try_unit] ) then
try_unit = nil;
end
HoTT_Display_Bar.unit = try_unit;
if ( HoTT_Debug.UBU and ( HoTT_Display_Bar.unit ~= old_unit) ) then
dprint("Bar now using "..tostring(HoTT_Display_Bar.unit));
end
return not ( HoTT_Display_Bar.unit and
( HoTT_Display_Bar.unit == old_unit )
);
end
---
--- clear_bar
---
local function clear_bar ()
HoTT_Display_Bar.unit = nil;
HoTT_Display_Bar:SetValue(0);
HoTT_Display_Dead:Hide();
end
---
--- available_name_width
---
local function available_name_width ()
local right = HoTT_Display:GetRight();
local left = HoTT_Display:GetLeft();
local anw = 115; -- Covers start-up nil problem
if ( right and left ) then
anw = (right - left)
- params.ui.display.tot_name.inset.right
- params.ui.display.tot_name.inset.left;
end
return anw;
end
---
--- set_display_name_color
---
--- Typcially ~ 5 ms for a new name and a reasonably-sized window
--- About 10-15 ms to truncate down a long name to nothing
--- regexp is not the culprit
local function set_display_name_color(name, color)
if ( name ) then
if ( placed_names.available_name_width ~= available_name_width() ) then
clear_table(placed_names);
placed_names.available_name_width = available_name_width();
end
local fits = placed_names[name];
if ( fits ) then
HoTT_Display_NameArea_Text:SetText(fits);
else
local now;
if ( HoTT_Debug.name_time ) then
now = GetTime();
end
HoTT_Display_NameArea_Text:SetText(name);
if ( ( HoTT_Display_NameArea_Text:GetStringWidth() <=
placed_names.available_name_width ) or
( string.len(name) <= 2 )
) then
fits = name;
else
local try = string.sub(name, 1, -2);
local ellipsis_chars = string.len(ellipsis);
while ( not fits ) do
HoTT_Display_NameArea_Text:SetText(try);
if ( ( HoTT_Display_NameArea_Text:GetStringWidth() <=
placed_names.available_name_width ) or
( string.len(try) <= 2 )
) then
fits = try;
else
try = string.sub(try, 1, -2);
end
end
try = string.sub(try, 1, -2) .. ellipsis;
fits = false;
while ( not fits ) do
HoTT_Display_NameArea_Text:SetText(try);
if ( ( HoTT_Display_NameArea_Text:GetStringWidth() <=
placed_names.available_name_width ) or
( string.len(try) <= ( 1 + ellipsis_chars ) )
) then
fits = try;
else
try = string.sub(try, 1, -2-ellipsis_chars) .. ellipsis;
end
end
fits = string.gsub(fits, "%s"..ellipsis.."$", ellipsis);
end -- did name fit first time
placed_names[name] = fits;
if ( HoTT_Debug.name_time ) then
local dt = GetTime() - now;
if ( dt > 0.0015 ) then
HoTT_Debug.pnt[name] = format("%5.3f",GetTime() - now);
end
tprint("Name time", now);
end
HoTT_Display_NameArea_Text:SetText(fits);
end
end
if ( color ) then
HoTT_Display_NameArea_Text:SetTextColor(color.r,
color.g,
color.b);
end
return true;
end
---
--- position_display
---
-- local
function position_display ()
local buff_rows_shown;
local anw = available_name_width();
local puid = params.ui.display;
local relative_to = puid.relative_to;
local relative_to_object = getglobal(relative_to);
if ( not relative_to_object ) then
message(format(fmt_no_such_object, puid.relative_to));
return false;
end
if ( HoTT_Debug.PD ) then
dprint("Positioning for " .. ( UnitName("target") or "<nil>"));
end
local has_buffs = UnitBuff("target",1);
local has_debuffs = UnitDebuff("target",1);
local is_friend = UnitIsFriend("player", "target");
if ( not ( has_buffs or has_debuffs ) ) then
buff_rows_shown = 0;
elseif ( ( has_debuffs and is_friend ) or
( has_buffs and not is_friend ) ) then
buff_rows_shown = 2;
else
buff_rows_shown = 1;
end
-- Check for "too narrow" problems and silently rectify
if ( anw < puid.tot_name.min_width ) then
puid.inset.right = puid.inset.right + ( puid.tot_name.min_width - anw );
end
--- Need to adjust relative offsets to UIParent scale
local s = 1/params.ui.display.scale;
if ( ( relative_to_object == UIParent ) or
( relative_to_object == WorldFrame ) ) then
HoTT_Display:SetPoint("TOPLEFT",
puid.relative_to,
"TOPLEFT",
s*puid.inset.left,
-s*puid.drops[buff_rows_shown]);
HoTT_Display:SetPoint("TOPRIGHT",
puid.relative_to,
"TOPRIGHT",
-s*puid.inset.right,
-s*puid.drops[buff_rows_shown]);
else
HoTT_Display:SetPoint("TOPLEFT",
puid.relative_to,
"BOTTOMLEFT",
s*puid.inset.left,
-s*puid.drops[buff_rows_shown]);
HoTT_Display:SetPoint("TOPRIGHT",
puid.relative_to,
"BOTTOMRIGHT",
-s*puid.inset.right,
-s*puid.drops[buff_rows_shown]);
end
return true;
end
---
--- refresh_ui_positions
---
---
--- NOTE: Call on /tot ui, and any time settings change
---
-- stubbed local earlier:
--
-- local
function refresh_ui_positions ()
local name_offset;
local puid = params.ui.display;
HoTT_Display:SetScale(puid.scale * UIParent:GetScale());
HoTT_Display_NameArea_Text:SetPoint("LEFT",
"HoTT_Display_NameArea",
"LEFT",
puid.tot_name.inset.left, 0);
HoTT_Display_NameArea_Text:SetPoint("RIGHT",
"HoTT_Display_NameArea",
"RIGHT",
-puid.tot_name.inset.right, 0);
HoTT_Display_NameArea_Text:SetJustifyH(puid.tot_name.alignment);
position_display();
return true;
end
---
--- global_check_warning
---
local function global_check_warning ()
local global_check_failed = ( type(global_check_failures) ~= "nil" );
if ( global_check_failed ) then
DEFAULT_CHAT_FRAME:AddMessage(string_global_check_failed);
end
return global_check_failed;
end
---
--- hott_init
---
local function hott_init ()
clear_t();
clear_tot();
hott_is_on = false;
last_tot = nil;
last_frame_tot = nil;
clear_table(roster);
clear_bar();
reset_poll_timer();
set_display_name_color(undetermined_string, colors.name.undetermined);
return true;
end
---
--- hott_off
---
local function hott_off ()
global_check_warning();
hott_init();
HoTT_Display:Hide();
params.hott_was_on = false;
-- DEFAULT_CHAT_FRAME:AddMessage(off_string);
return true;
end
---
--- hott_on
---
local function hott_on ()
global_check_warning();
if ( HoTT_Patch_Warning() ) then
return;
end
if ( not hott_is_on ) then
hott_init();
update_roster();
end
hott_is_on = true;
update_tot();
refresh_ui_positions();
if UnitExists("target") then
HoTT_Display:Show();
end
params.hott_was_on = true;
-- DEFAULT_CHAT_FRAME:AddMessage(on_string);
return true;
end
---
--- better_target_by_name
---
local function better_target_by_name (desiredTarget, second_pass)
local oldTarget = UnitName("target");
local newTarget;
local retval;
local reverse_unit = roster[desiredTarget];
if ( UnitName("targettarget") == desiredTarget ) then
TargetUnit("targettarget");
elseif (reverse_unit) then
TargetUnit(reverse_unit);
else
TargetByName(desiredTarget);
end
newTarget = UnitName("target");
if ( newTarget == desiredTarget ) then
retval = newTarget;
else
-- restore target to the "best" option, return nil
if ( not second_pass ) then
better_target_by_name(oldTarget, true);
else
ClearTarget();
end
retval = nil;
end
return retval;
end
---
--- bar_set_unreliable_callback
---
local function bar_set_unreliable_callback ()
-- placeholder
end
---
--- load_saved_variables
---
local function load_saved_variables ()
---
--- On entering this, VARIABLES_LOADED should have fired
--- and the flag indicating this has run should not be set
---
--- Return a true value to set that flag
---
load_saved_settings();
if ( IsShiftKeyDown() ) then
DEFAULT_CHAT_FRAME:AddMessage(shift_at_load_string);
hott_off();
else
if ( params.hott_was_on ) then
hott_on();
else
hott_off();
end
end
return true;
end
------------------------------------
--- Command parsing and dispatch ---
------------------------------------
---
--- HoTT_Command_Dispatch
---
local function command_dispatch (command_string)
local i1, i2;
local command, command_arguments
command_string = string.gsub(string.lower(command_string), "^%s*", "");
command_string = string.gsub(command_string, "%s*$", "");
i1, i2, command, command_arguments =
string.find(command_string, "^(%w+)%s*(.*)$");
if ( ( command == string_help_cmd ) or ( command == nil ) ) then
DEFAULT_CHAT_FRAME:AddMessage(help_message);
elseif ( command == string_on_cmd ) then
hott_on();
elseif ( command == string_off_cmd ) then
hott_off();
elseif ( command == string_ui_cmd ) then
DEFAULT_CHAT_FRAME:AddMessage(help_ui);
elseif ( command == string_version_cmd ) then
DEFAULT_CHAT_FRAME:AddMessage(
format("ToT CVS versions:\nLua: %s\nXML: %s",
version_table_data.apps.TargetOfTarget.lua_CVS,
version_table_data.apps.TargetOfTarget.XML_CVS));
elseif ( command == string_globals_cmd ) then
if ( not global_check_failures ) then
DEFAULT_CHAT_FRAME:AddMessage(string_global_list);
else
DEFAULT_CHAT_FRAME:AddMessage(string_global_check_explanation);
table_to_list = global_check_failures;
for k,v in pairs(global_check_failures) do
DEFAULT_CHAT_FRAME:AddMessage(v);
end
DEFAULT_CHAT_FRAME:AddMessage(string_global_variable_check_trailer);
end
elseif ( command == string_params_cmd ) then
launch_params_browser();
else
DEFAULT_CHAT_FRAME:AddMessage(string_unrecognized_cmd_help);
end
end
------------------------
------------------------
--- GLOBAL FUNCTIONS ---
------------------------
------------------------
----------------------
--- Event Handlers ---
----------------------
-------------------
--- HoTT Itself ---
-------------------
---
--- HoTT_MainFrame_OnLoad
---
function HoTT_MainFrame_OnLoad ()
check_localized_slash_commands();
init_preload_params();
restore_defaults();
hott_init();
-- Register slash commands
SLASH_TARGETOFTARGET_TOTON1 = toton_string;
SlashCmdList["TARGETOFTARGET_TOTON"] = function ()
hott_on();
end
SLASH_TARGETOFTARGET_TOTOFF1 = totoff_string;
SlashCmdList["TARGETOFTARGET_TOTOFF"] = function ()
hott_off();
end
SLASH_TARGETOFTARGET_TOT1 = string_slash_tot;
SLASH_TARGETOFTARGET_TOT2 = string_slash_hott;
SlashCmdList["TARGETOFTARGET_TOT"] = function (command_string)
command_dispatch(command_string);
end
player_name = UnitName("player");
realm_name = GetCVar("realmName");
-- Register for events
this:RegisterEvent("VARIABLES_LOADED");
this:RegisterEvent("PLAYER_ENTERING_WORLD");
this:RegisterEvent("UNIT_NAME_UPDATE");
this:RegisterEvent("PARTY_MEMBERS_CHANGED");
this:RegisterEvent("RAID_ROSTER_UPDATE");
this:RegisterEvent("PLAYER_TARGET_CHANGED");
this:RegisterEvent("UNIT_AURA");
if(UltimateUI_RegisterButton) then
UltimateUI_RegisterButton (
"TargetofTarget",
"Params",
"|cFF00CC00TargetOfTarget|r\nShows the target of the target! To use, type\n /tot on",
"Interface\\Icons\\Spell_Holy_SealOfSalvation",
launch_params_browser
);
end
--DEFAULT_CHAT_FRAME:AddMessage(loaded_string);
end
---
--- HoTT_MainFrame_OnEvent
---
function HoTT_MainFrame_OnEvent()
if ( event == "VARIABLES_LOADED" ) then
load_saved_variables();
end
if ( not hott_is_on ) then
-- we shouldn't be doing anything else...
else
if ( ( event == "PLAYER_ENTERING_WORLD" ) or
( event == "UNIT_NAME_UPDATE" ) or
( event == "PARTY_MEMBERS_CHANGED" ) or
( event == "RAID_ROSTER_UPDATE" ) ) then
update_roster();
if ( update_bar_unit() ) then update_tot_health() end;
end
if ( ( event == "PLAYER_TARGET_CHANGED" ) or
( event == "PLAYER_ENTERING_WORLD" ) ) then
dprint("PTC or PEW event");
update_tot();
position_display();
end
if ( ( event == "UNIT_AURA" ) and ( arg1 == "target" ) ) then
position_display();
end
end -- hott_is_on
end
---------------
--- Display ---
---------------
---
--- HoTT_Display_OnClick
---
function HoTT_Display_OnClick(button)
local name = tot_target_info.name;
local unit = best_tot_unit();
if ( button == "RightButton") then
return;
end
if ( button == "LeftButton" ) then
if ( SpellIsTargeting() and unit ) then
SpellTargetUnit(unit);
elseif ( unit ) then
TargetUnit(unit);
else
better_target_by_name(name); -- think about glowy hand impact...
end
end -- LeftButton
end
---
--- reset_poll_timer
---
-- local
function reset_poll_timer()
poll_timer = 0;
poll_timer_fired = false;
return true;
end
---
--- HoTT_Display_OnUpdate
---
function HoTT_Display_OnUpdate (interval)
if ( not UnitExists("target") ) then
last_frame_tot = nil;
reset_poll_timer();
this:Hide();
else
poll_timer = poll_timer + interval; -- can be moved inside .unit test
-- left here for debugging output
this_frame_tot = UnitName("targettarget");
if ( this_frame_tot ~= last_frame_tot ) then -- fast, but...
dprint("tot change");
update_tot();
elseif ( ( not HoTT_Display_Bar.unit ) and
( UnitIsPlayer("target") or
UnitAffectingCombat("target") ) ) then
-- Bar.unit take care of their own health and should have unique names
-- Only players or in-combat NPCs can change health or targets
if ( ( poll_timer > params.poll_timer_period ) and
( not poll_timer_fired ) ) then
elseif ( tot_target_info.player ) then
dprint("player timeout");
update_tot_health();
else
dprint("NPC timeout")
update_tot();
end
poll_timer_fired = true;
end -- tot changed or Bar.unit exists
last_frame_tot = this_frame_tot;
end -- target exists
end
---
--- HoTT_Display_OnEvent
---
function HoTT_Display_OnEvent ()
if ( UnitExists("target") and hott_is_on ) then
this:Show();
else
this:Hide();
end
end
------------------
--- Health Bar ---
------------------
---
--- HoTT_Display_Bar_OnEvent
---
function HoTT_Display_Bar_OnEvent (event_arg1)
if (this.unit and ( event_arg1 == this.unit ) and hott_is_on ) then
UnitFrameHealthBar_Update(this, this.unit)
end
return true;
end
---
--- HoTT_Display_Bar_OnValueChanged
---
function HoTT_Display_Bar_OnValueChanged (new_value)
if ( HoTT_Debug.BOVC and
units_getting_events[this.unit] and
( time_since_bar_update > 0.001 ) ) then
dprint(format("Bar update interval: %7.3f", time_since_bar_update))
end
HealthBar_OnValueChanged(new_value, true);
time_since_bar_update = 0;
bar_set_unreliable = false;
return true;
end
---
--- HoTT_Display_Bar_OnUpdate
---
function HoTT_Display_Bar_OnUpdate (interval)
time_since_bar_update = time_since_bar_update + interval;
if ( ( not this.unit ) and
( time_since_bar_update > params.bar_unreliable_time ) and
( not bar_set_unreliable )
) then
this:SetStatusBarColor(colors.bar.unreliable.r,
colors.bar.unreliable.g,
colors.bar.unreliable.b);
bar_set_unreliable = true;
if ( type(bar_set_unreliable_callback) == "function" ) then
bar_set_unreliable_callback(this);
end
end
return true;
end
--------------------------
--- Core functionality ---
--------------------------
--- NOTE: These need to be moved and made local, once confirmed working
---
--- update_tot_health
---
function update_tot_health ()
local now = GetTime();
local retval = UnitExists("targettarget");
if ( retval ) then
HoTT_Display_Bar:SetMinMaxValues(0, UnitHealthMax("targettarget"));
HoTT_Display_Bar:SetValue(UnitHealth("targettarget"));
if ( UnitIsCorpse("targettarget") or
UnitIsDeadOrGhost("targettarget") ) then
HoTT_Display_Dead:Show();
else
HoTT_Display_Dead:Hide();
end
end
reset_poll_timer();
tprint("update_tot_health", now);
return retval;
end
---
--- update_tot
---
function update_tot ()
local now = GetTime();
local retval;
local may_be_stunned_mob;
if ( not UnitExists("target") ) then
dprint("No target to check");
clear_t();
clear_tot();
clear_bar();
set_display_name_color(no_target_string, colors.name.normal);
HoTT_Display_Bar:Hide();
elseif ( ( not UnitIsPlayer("target") ) and
( not UnitAffectingCombat("target") ) ) then
dprint("Non-aggro NPC");
clear_t();
clear_tot();
clear_bar();
set_display_name_color(no_target_string, colors.name.normal);
HoTT_Display_Bar:Hide();
elseif ( UnitIsUnit("target", "player") ) then
dprint("Self-target case");
target_to_t();
self_to_tot();
if ( update_bar_unit() ) then update_tot_health() end;
set_display_name_color(tot_target_info.name, colors.name.normal);
HoTT_Display_Bar:Show();
else
-- Check stunned mob while we have "target" reliably
may_be_stunned_mob = ( ( not UnitIsPlayer("target") ) and
( UnitAffectingCombat("target") ) );
-- then go ahead and do the check
if ( UnitExists("targettarget") ) then
target_to_t();
targettarget_to_tot();
if ( update_bar_unit() ) then update_tot_health() end;
set_display_name_color(tot_target_info.name, colors.name.normal);
HoTT_Display_Bar:Show();
elseif ( may_be_stunned_mob ) then
if ( new_target_appears_same_as_before() ) then
dprint("Assuming stunned - same mob");
-- and keep the old info
-- NOTE THAT THIS DOES NOT UPDATE HEALTH BAR!
if ( roster[tot_target_info.name] ) then
set_display_name_color(nil, colors.name.unreliable);
else
set_display_name_color(nil, colors.name.unreliable_no_unit);
end
else
dprint("Assuming stunned - new mob");
set_display_name_color(stunned_string,
colors.name.unreliable);
end
else
--
-- Target probably doesn't have a target (or is stunned PC)
--
dprint("No target of target found");
target_to_t();
clear_tot();
clear_bar();
set_display_name_color(no_target_string, colors.name.normal);
HoTT_Display_Bar:Hide();
end
end
if ( ( this_tot ) and
( this_tot == player_name ) and
( this_tot ~= last_tot ) and
( t_target_info.canattackme ) ) then
if ( params.aggro_message and
string.find(params.aggro_message, "%S" ) ) then
UIErrorsFrame:AddMessage(params.aggro_message,
1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME);
end
if ( type(HoTT_Aggro_Callback) == "function" ) then
HoTT_Aggro_Callback();
end
end
reset_poll_timer();
tprint("update_tot", now);
return tot_target_info;
end
---------------------------------
--- Params Frame here for now ---
---------------------------------
--[[
window_offset[ => ui.display.drops[
window_offset.right => ui.display.inset.right
window_offset.left => ui.display.inset.left
window_offset.object => ui.display.relative_to
window_offset.scale => ui.display.scale
name_alignment => ui.display.tot_name.alignment
name_inset_left => ui.display.tot_name.inset.left
name_inset_right => ui.display.tot_name.inset.right
sign changes needed:
-window_offset[0] => ui.display.drops[0]
-window_offset[1] => ui.display.drops[1]
-window_offset[2] => ui.display.drops[2]
-window_offset.right => ui.display.inset.right
specials:
window_offset.text_offset:
"CENTER" => (ignore)
"LEFT" => ui.display.tot_name.inset.left
"RIGHT" => -ui.display.tot_name.inset.right
]]
---
--- params_refresh
---
local function params_refresh ()
refresh_ui_positions();
end
---
--- params_frame_int_check
---
local function params_frame_int_check (str, err_str)
local retval;
if ( string.find(str, "^-?%d+$") ) then
retval = tonumber(str);
else
message(string.format("%s must be an integer. Previous value restored.",
err_str));
HoTT_Params_Frame_LoadValues();
retval = false;
end
return retval;
end
---
--- anw_check
---
local function anw_check (obj, dl, dr, tl, tr, field_name_string)
obj = getglobal(obj or params.ui.display.relative_to);
dl = dl or params.ui.display.inset.left;
dr = dr or params.ui.display.inset.right;
tl = tl or params.ui.display.tot_name.inset.left;
tr = tr or params.ui.display.tot_name.inset.right;
local retval;
if ( ( obj:GetRight() - obj:GetLeft() - dl - dr - tl - tr ) <
params.ui.display.tot_name.min_width ) then
message(string.format(fmt_too_narrow, field_name_string));
HoTT_Params_Frame_LoadValues();
retval = false;
else
retval = true;
end
return retval;
end
---
--- HoTT_Params_Frame_LoadValues
---
function HoTT_Params_Frame_LoadValues ()
local puid = params.ui.display;
HoTT_Params_Frame_ObjectName_EditBox:SetText(tostring(puid.relative_to));
HoTT_Params_Frame_DisplayLeft_EditBox:SetText(tostring(puid.inset.left));
HoTT_Params_Frame_DisplayRight_EditBox:SetText(tostring(puid.inset.right));
HoTT_Params_Frame_TextLeft_EditBox:SetText(
tostring(puid.tot_name.inset.left));
HoTT_Params_Frame_TextRight_EditBox:SetText(
tostring(puid.tot_name.inset.right));
HoTT_Params_Frame_ButtonLeft:SetChecked(false);
HoTT_Params_Frame_ButtonCenter:SetChecked(false);
HoTT_Params_Frame_ButtonRight:SetChecked(false);
if ( puid.tot_name.alignment == "CENTER" ) then
HoTT_Params_Frame_ButtonCenter:SetChecked(true);
elseif ( puid.tot_name.alignment == "LEFT" ) then
HoTT_Params_Frame_ButtonLeft:SetChecked(true);
elseif ( puid.tot_name.alignment == "RIGHT" ) then
HoTT_Params_Frame_ButtonRight:SetChecked(true);
end
HoTT_Params_Frame_Scale_EditBox:SetText(format("%5.3f", puid.scale));
HoTT_Params_Frame_Drop0_EditBox:SetText(tostring(puid.drops[0]));
HoTT_Params_Frame_Drop1_EditBox:SetText(tostring(puid.drops[1]));
HoTT_Params_Frame_Drop2_EditBox:SetText(tostring(puid.drops[2]));
HoTT_Params_Frame_AggroMessage_EditBox:SetText(params.aggro_message);
end
---
--- HoTT_Params_Frame_ObjectName_Changed
---
function HoTT_Params_Frame_ObjectName_Changed (new_text)
local obj = getglobal(new_text);
if ( not ( obj and
( type(obj.GetRight == "function") ) and
( type(obj.GetLeft == "function") )
)
) then
message(string.format(fmt_no_obj_edit, new_text));
HoTT_Params_Frame_LoadValues();
refocus_object = HoTT_Params_Frame_ObjectName_EditBox;
elseif ( anw_check(new_text, nil, nil, nil, nil, "Relative-to object")
) then
params.ui.display.relative_to = new_text;
params_refresh();
else
refocus_object = HoTT_Params_Frame_ObjectName_EditBox;
end
end
---
--- HoTT_Params_Frame_DisplayLeft_Changed
---
function HoTT_Params_Frame_DisplayLeft_Changed (new_string)
local new_number = params_frame_int_check(new_string, "Left display inset");
if ( new_number and
anw_check(nil, new_number, nil, nil, nil, "Left display inset")
) then
params.ui.display.inset.left = new_number;
params_refresh();
else
refocus_object = HoTT_Params_Frame_DisplayLeft_EditBox;
end
end
---
--- HoTT_Params_Frame_DisplayRight_Changed
---
function HoTT_Params_Frame_DisplayRight_Changed (new_string)
local new_number = params_frame_int_check(new_string,"Right display inset");
if ( new_number and
anw_check(nil, nil, new_number, nil, nil, "Right display inset" )
) then
params.ui.display.inset.right = new_number;
params_refresh();
else
refocus_object = HoTT_Params_Frame_DisplayRight_EditBox;
end
end
---
--- HoTT_Params_Frame_TextLeft_Changed
---
function HoTT_Params_Frame_TextLeft_Changed (new_string)
local new_number = params_frame_int_check(new_string, "Left text inset");
if ( new_number and
anw_check(nil, nil, nil, new_number, nil, "Left text inset")
) then
params.ui.display.tot_name.inset.left = new_number;
params_refresh();
else
refocus_object = HoTT_Params_Frame_TextLeft_EditBox;
end
end
---
--- HoTT_Params_Frame_TextRight_Changed
---
function HoTT_Params_Frame_TextRight_Changed (new_string)
local new_number = params_frame_int_check(new_string, "Right text inset");
if ( new_number and
anw_check(nil, nil, nil, nil, new_number, "Right text inset" )
) then
params.ui.display.tot_name.inset.right = new_number;
params_refresh();
else
refocus_object = HoTT_Params_Frame_TextRight_EditBox;
end
end
---
--- HoTT_Params_Frame_Alignment_Changed
---
function HoTT_Params_Frame_Alignment_Changed (new_alignment)
params.ui.display.tot_name.alignment = new_alignment;
params_refresh();
end
---
--- HoTT_Params_Frame_Scale_Changed
---
function HoTT_Params_Frame_Scale_Changed (new_string)
local new_number = tonumber(new_string);
if ( new_number and ( new_number > 0 ) ) then
params.ui.display.scale = new_number;
params_refresh();
else
message("Scale must be a positive value. Previous value restored.");
HoTT_Params_Frame_LoadValues();
refocus_object = HoTT_Params_Frame_Scale_EditBox;
retval = false;
end
end
---
--- HoTT_Params_Frame_Drop0_Changed
---
function HoTT_Params_Frame_Drop0_Changed (new_string)
local new_number = params_frame_int_check(new_string, "No-buff drop");
if ( new_number ) then
params.ui.display.drops[0] = new_number;
params_refresh();
else
refocus_object = HoTT_Params_Frame_Drop0_EditBox;
end
end
---
--- HoTT_Params_Frame_Drop1_Changed
---
function HoTT_Params_Frame_Drop1_Changed (new_string)
local new_number = params_frame_int_check(new_string, "First-row drop");
if ( new_number ) then
params.ui.display.drops[1] = new_number;
params_refresh();
else
refocus_object = HoTT_Params_Frame_Drop1_EditBox;
end
end
---
--- HoTT_Params_Frame_Drop2_Changed
---
function HoTT_Params_Frame_Drop2_Changed (new_string)
local new_number = params_frame_int_check(new_string, "Second-row drop");
if ( new_number ) then
params.ui.display.drops[2] = new_number;
params_refresh();
else
refocus_object = HoTT_Params_Frame_Drop2_EditBox;
end
end
---
--- HoTT_Params_Frame_AggroMessage_Changed
---
function HoTT_Params_Frame_AggroMessage_Changed (new_text)
params.aggro_message = new_text;
params_refresh();
end
---
--- launch_params_browser
---
-- local
function launch_params_browser ()
scratch_params = {};
current_params = params;
set_deferral_recursively(scratch_params, params,
"scratch_params", "current_params");
params = scratch_params;
HoTT_Params = scratch_params;
HoTT_Params_Frame:Show();
end
---
--- HoTT_Params_Frame_OnCancel
---
function HoTT_Params_Frame_OnCancel ()
HoTT_Params_Frame_LoadValues(); -- to prevent error messaging
PlaySound("gsTitleOptionExit");
HideUIPanel(HoTT_Params_Frame);
params = current_params;
HoTT_Params = current_params;
params_refresh();
end
---
--- HoTT_Params_Frame_OnDefaults
---
function HoTT_Params_Frame_OnDefaults ()
params.aggro_message = default_params.aggro_message;
-- copy_table_recursively(default_params.ui, params.ui);
-- fails as default_params is write-locked and can't be iterated over
local dpuid = default_params.ui.display;
local puid = params.ui.display;
puid.relative_to = dpuid.relative_to;
puid.scale = dpuid.scale;
puid.inset.left = dpuid.inset.left;
puid.inset.right = dpuid.inset.right;
puid.drops[0] = dpuid.drops[0];
puid.drops[1] = dpuid.drops[1];
puid.drops[2] = dpuid.drops[2];
puid.tot_name.alignment = dpuid.tot_name.alignment;
puid.tot_name.inset.left = dpuid.tot_name.inset.left;
puid.tot_name.inset.right = dpuid.tot_name.inset.right;
HoTT_Params_Frame_LoadValues();
params_refresh();
end
---
--- HoTT_Params_Frame_OnOkay
---
function HoTT_Params_Frame_OnOkay ()
---
--- Can't blindly accept and close!
---
HoTT_Params_Frame_ClearFocus();
if ( refocus_object ) then
refocus_object:SetFocus();
refocus_object = nil;
else
if ( current_params.aggro_message ~= params.aggro_message ) then
current_params.aggro_message = params.aggro_message;
else
current_params.aggro_message = nil; -- defer to defaults
end
local puid = params.ui.display;
local cpuid = current_params.ui.display;
local dpuid = default_params.ui.display;
if ( ( puid.relative_to ~= dpuid.relative_to ) or
( puid.scale ~= dpuid.scale ) or
( puid.inset.left ~= dpuid.inset.left ) or
( puid.inset.right ~= dpuid.inset.right ) or
( puid.drops[0] ~= dpuid.drops[0] ) or
( puid.drops[1] ~= dpuid.drops[1] ) or
( puid.drops[2] ~= dpuid.drops[2] ) or
( puid.tot_name.alignment ~= dpuid.tot_name.alignment ) or
( puid.tot_name.inset.left ~= dpuid.tot_name.inset.left ) or
( puid.tot_name.inset.right ~= dpuid.tot_name.inset.right )
) then
cpuid.relative_to = puid.relative_to;
cpuid.scale = puid.scale;
cpuid.inset.left = puid.inset.left;
cpuid.inset.right = puid.inset.right;
cpuid.drops[0] = puid.drops[0];
cpuid.drops[1] = puid.drops[1];
cpuid.drops[2] = puid.drops[2];
cpuid.tot_name.alignment = puid.tot_name.alignment;
cpuid.tot_name.inset.left = puid.tot_name.inset.left;
cpuid.tot_name.inset.right = puid.tot_name.inset.right;
else
current_params.ui = nil; -- defer to defaults
end
PlaySound("gsTitleOptionOK");
HideUIPanel(HoTT_Params_Frame);
params = current_params;
HoTT_Params = current_params;
end
params_refresh();
end
---
--- HoTT_Params_Frame_ClearFocus
---
function HoTT_Params_Frame_ClearFocus ()
HoTT_Params_Frame_ObjectName_EditBox:ClearFocus();
HoTT_Params_Frame_DisplayLeft_EditBox:ClearFocus();
HoTT_Params_Frame_DisplayRight_EditBox:ClearFocus();
HoTT_Params_Frame_TextLeft_EditBox:ClearFocus();
HoTT_Params_Frame_TextRight_EditBox:ClearFocus();
HoTT_Params_Frame_Scale_EditBox:ClearFocus();
HoTT_Params_Frame_Drop0_EditBox:ClearFocus();
HoTT_Params_Frame_Drop1_EditBox:ClearFocus();
HoTT_Params_Frame_Drop2_EditBox:ClearFocus();
HoTT_Params_Frame_AggroMessage_EditBox:ClearFocus();
end
---
--- HoTT_Params_Frame_CheckRefocus
---
function HoTT_Params_Frame_CheckRefocus ()
if ( refocus_object ) then
refocus_object:SetFocus();
refocus_object = nil;
end
end