local ex = Examiner;
local cfg, cache;
local gtt = GameTooltip;
-- Module
local mod = ex:CreateModule("Stats","Gear Statistics");
mod.help = "Right Click for extended menu";
mod:CreatePage(false);
mod:HasButton(true);
mod.details = ex:CreateDetailObject();
-- Variables
local ITEM_HEIGHT = 12;
local displayList = {};
local resists = {};
local entries = {};
-- Stat Entry Order -- Item 0 is the header for the group
local StatEntryOrder = {
{ [0] = PLAYERSTAT_BASE_STATS, "STR", "AGI", "STA", "INT", "SPI", "ARMOR", "MASTERY" },
{ [0] = HEALTH.." & "..MANA, "HP", "MP", "HP5", "MP5" },
{ [0] = PLAYERSTAT_SPELL_COMBAT.." "..STATS_LABEL:gsub(":",""), "SPELLDMG", "ARCANEDMG", "FIREDMG", "NATUREDMG", "FROSTDMG", "SHADOWDMG", "HOLYDMG", "SPELLCRIT", "SPELLHIT", "SPELLHASTE", "SPELLPENETRATION" },
{ [0] = MELEE.." & "..RANGED, "AP", "RAP", "CRIT", "HIT", "HASTE", "ARMORPENETRATION", "EXPERTISE", "WPNDMG", "RANGEDDMG", "DAGGERSKILL", "ONEAXESKILL", "TWOAXESKILL", "ONESWORDSKILL", "TWOSWORDSKILL", "ONEMACESKILL", "TWOMACESKILL", "BOWSKILL", "GUNSSKILL", "CROSSBOWSKILL" },
{ [0] = PLAYERSTAT_DEFENSES, "DEFENSE", "DODGE", "PARRY", "BLOCK", "BLOCKVALUE", "RESILIENCE", "PVPPOWER" },
};
--------------------------------------------------------------------------------------------------------
-- Module Scripts --
--------------------------------------------------------------------------------------------------------
-- OnInitialize
function mod:OnInitialize()
cfg = ex.cfg;
cache = Examiner_Cache;
-- Defaults
cfg.statsViewType = (cfg.statsViewType or 1);
-- Add cache "iLvlAverage" sort method
local cacheMod = ex:GetModuleFromToken("Cache");
if (cacheMod) and (cacheMod.cacheSortMethods) then
cacheMod.cacheSortMethods[#cacheMod.cacheSortMethods + 1] = "iLvlAverage";
end
end
-- OnConfigChanged
function mod:OnConfigChanged(var,value)
if (cfg.statsViewType == 1) and (var == "combineAdditiveStats" or var == "percentRatings") then
self:BuildShownList();
end
end
-- OnButtonClick
function mod:OnButtonClick(frame,button)
-- left
if (button == "LeftButton") then
if (IsShiftKeyDown()) and (ex.itemsLoaded) then
ex:CacheStatsForCompare();
elseif (IsControlKeyDown()) then
cfg.statsViewType = (cfg.statsViewType == 1 and 2 or 1);
self:BuildShownList();
end
-- right
elseif (IsShiftKeyDown()) then
ex:CacheStatsForCompare(1);
end
end
-- OnInspectReady
function mod:OnInspectReady(unit,guid)
self:HasData(ex.itemsLoaded);
if (ex.itemsLoaded) then
self:InitDetails();
self:BuildShownList();
end
end
mod.OnInspect = mod.OnInspectReady;
-- OnCacheLoaded
function mod:OnCacheLoaded(entry,unit)
-- Az: Add "iLvlAverage" to old entries if none exist, maybe one day I will remove this
if (not entry.iLvlAverage) then
local numItems = (#LibGearExam.Slots - 2); -- Ignore Tabard + Shirt, hence minus 2
local iLvlTotal = 0;
for slotName, link in next, entry.Items do
if (slotName ~= "TabardSlot") and (slotName ~= "ShirtSlot") then
local itemLevel = GetDetailedItemLevelInfo(link);
if (itemLevel) then
if (slotName == "MainHandSlot") and (not entry.Items.SecondaryHandSlot) then
itemLevel = (itemLevel * 2);
end
iLvlTotal = (iLvlTotal + itemLevel);
end
end
end
entry.iLvlAverage = (iLvlTotal / numItems);
end
-- Show stuff
self:HasData(true);
self:InitDetails();
self:BuildShownList();
end
-- OnClearInspect
function mod:OnClearInspect()
self:HasData(nil);
self.details:Clear();
end
-- OnCompare
function mod:OnCompare(isCompare,compareEntry)
self:BuildShownList();
end
-- OnDetailsUpdate
function mod:OnDetailsUpdate()
if (cfg.statsViewType == 2) then
self:BuildShownList();
end
end
--------------------------------------------------------------------------------------------------------
-- Menu --
--------------------------------------------------------------------------------------------------------
-- Menu Init Items
function mod.MenuInit(parent,list)
-- stats
local tbl = list[#list + 1]; tbl.text = "Stats"; tbl.header = 1;
tbl = list[#list + 1]; tbl.text = "Cache Player"; tbl.value = 1; tbl.checked = (cache[ex:GetEntryName()] ~= nil);
-- view
tbl = list[#list + 1]; tbl.header = 1;
tbl = list[#list + 1]; tbl.text = "View"; tbl.header = 1;
tbl = list[#list + 1]; tbl.text = "Gear Stats"; tbl.value = 4; tbl.checked = (cfg.statsViewType == 1);
tbl = list[#list + 1]; tbl.text = "Details"; tbl.value = 5; tbl.checked = (cfg.statsViewType == 2);
-- compare
tbl = list[#list + 1]; tbl.header = 1;
tbl = list[#list + 1]; tbl.text = "Compare"; tbl.header = 1;
tbl = list[#list + 1]; tbl.text = "Mark for Compare"; tbl.value = 2; tbl.checked = (ex.isComparing and ex.compareStats.entry == ex:GetEntryName());
if (ex.isComparing) then
tbl = list[#list + 1]; tbl.text = "Clear Compare"; tbl.value = 3;
end
end
-- Menu Select Item
function mod.MenuSelect(parent,entry)
-- Cache
if (entry.value == 1) then
ex:CachePlayer(1);
-- Mark for Compare & Clear Compare
elseif (entry.value == 2 or entry.value == 3) then
ex:CacheStatsForCompare(entry.value == 3);
-- View Type
else
cfg.statsViewType = (entry.value - 3);
mod:BuildShownList();
end
end
--------------------------------------------------------------------------------------------------------
-- Details --
--------------------------------------------------------------------------------------------------------
-- Obtain Gem and Item Level Details
-- http://www.wowwiki.com/Item_level#Epic_Item_Level_Chart
-- http://elitistjerks.com/f15/t44718-item_level_mechanics/
local function GetGemAndItemInfo()
local iLvlTotal, iSlotValues, iLvlMin, iLvlMax = 0, 0;
local gemCount, gemRed, gemYellow, gemBlue = 0, 0, 0, 0;
for slotName, link in next, ex.info.Items do
-- Calculate Item Level Numbers
if (slotName ~= "TabardSlot") and (slotName ~= "ShirtSlot") then
local _, _, itemRarity, itemLevel = GetItemInfo(link);
itemLevel = GetDetailedItemLevelInfo(link);
if (itemLevel) then
iLvlMin = min(iLvlMin or itemLevel,itemLevel);
iLvlMax = max(iLvlMax or itemLevel,itemLevel);
-- Az: Since heirlooms scale, we should at least not count them as a level 1 item, that wouldn't be fair
-- if (itemRarity == 7) and (itemLevel == 1) then
-- itemLevel = ex.info.level;
-- end
if (slotName == "MainHandSlot") and (not ex.info.Items.SecondaryHandSlot) then
itemLevel = (itemLevel * 2);
end
iLvlTotal = (iLvlTotal + itemLevel);
end
end
end
-- Return
return iLvlTotal, iLvlMin, iLvlMax;
end
-- Initialise Details
function mod:InitDetails()
local details = self.details;
details:Clear();
-- Unit Details
if (ex.unit) then
details:Add("Unit");
details:Add("Token",ex.unit);
details:Add(HEALTH,UnitHealthMax(ex.unit));
if (UnitPowerType(ex.unit) == 0) then
details:Add(MANA,UnitPowerMax(ex.unit));
end
end
-- Item Level
local iLvlTotal, iLvlMin, iLvlMax = GetGemAndItemInfo();
local numItems = (#LibGearExam.Slots - 2); -- Ignore Tabard + Shirt, hence minus 2
details:Add("Item Levels");
details:Add("Combined Item Levels",iLvlTotal);
details:Add("Average Item Level",format("%.2f",iLvlTotal / numItems));
if (iLvlMin and iLvlMax) then
details:Add("Min / Max Item Levels",iLvlMin.." / "..iLvlMax);
end
ex.info.iLvlAverage = math.floor((iLvlTotal / numItems)*10)*0.1;
-- Gems
-- details:Add("Gems");
-- details:Add("Number of Gems",gemCount);
-- details:Add("Gem Color Matches",format("|cffff6060%d|r/|cffffff00%d|r/|cff008ef8%d",gemRed,gemYellow,gemBlue));
-- Cache
if (ex.isCacheEntry) then
details:Add("Cached Entry");
details:Add("Zone",ex.info.zone);
details:Add("Date",date("%a, %b %d, %Y",ex.info.time));
details:Add("Time",date("%H:%M:%S",ex.info.time));
details:Add("Time Ago",ex:FormatTime(time() - ex.info.time));
end
end
--------------------------------------------------------------------------------------------------------
-- Update Stat Lists --
--------------------------------------------------------------------------------------------------------
-- Show Resistances
local function UpdateResistances()
for i = 1, 5 do
local statToken = (LibGearExam.MagicSchools[i].."RESIST");
if (ex.unitStats[statToken]) or (ex.isComparing and ex.compareStats[statToken]) then
local statText = LibGearExam:GetStatValue(statToken,ex.unitStats,ex.isComparing and ex.compareStats,ex.info.level,cfg.combineAdditiveStats,cfg.percentRatings);
resists[i].value:SetText(statText);
else
resists[i].value:SetText("");
end
end
end
-- ScrollBar: Update Stat List
local function UpdateShownItems(self)
FauxScrollFrame_Update(self,displayList.count,#entries,ITEM_HEIGHT);
local index = self.offset;
for i = 1, #entries do
index = (index + 1);
local entry = entries[i];
if (index <= displayList.count) then
if (displayList[index].value) then
entry.left:SetTextColor(1,1,1);
entry.left:SetFormattedText(" %s",displayList[index].name);
entry.right:SetText(displayList[index].value);
elseif (displayList[index].name) then
entry.left:SetTextColor(0.5,0.75,1.0);
entry.left:SetFormattedText("%s:",displayList[index].name);
entry.right:SetText("");
else
entry.left:SetText("");
entry.right:SetText("");
end
-- this is where the tip is drawn
-- if (displayList[index].tip) then
-- entry.tip.tip = displayList[index].tip;
-- entry.tip:SetWidth(max(entry.right:GetWidth(),20));
-- entry.tip:Show();
-- else
-- entry.tip:Hide();
-- end
entry:Show();
else
entry:Hide();
end
end
entries[1]:SetWidth(displayList.count > #entries and 200 or 216);
end
-- Adds a List Entry
local function AddListEntry(name,value,tip)
displayList.count = (displayList.count + 1);
local tbl = displayList[displayList.count] or {};
displayList[displayList.count] = tbl;
tbl.name = name;
tbl.value = value;
tbl.tip = tip;
end
-- Build Stat List
local function BuildStatList()
displayList.count = 0;
local needHeader;
-- Build display table
for _, statCat in ipairs(StatEntryOrder) do
needHeader = 1;
for _, statToken in ipairs(statCat) do
if (ex.unitStats[statToken]) or (ex.isComparing and ex.compareStats[statToken]) then
if (needHeader) then
AddListEntry(statCat[0]);
needHeader = nil;
end
local value, tip = LibGearExam:GetStatValue(statToken,ex.unitStats,ex.isComparing and ex.compareStats,ex.info.level,cfg.combineAdditiveStats,cfg.percentRatings);
AddListEntry(LibGearExam:FormatStatName(statToken,cfg.percentRatings),value,tip);
end
end
end
-- Add Sets
if (next(ex.info.Sets)) then
AddListEntry();
AddListEntry("Sets");
end
for setName, setEntry in next, ex.info.Sets do
AddListEntry(setName,setEntry.count.."/"..setEntry.max);
end
-- Add Padding + Update Resistances + Shown Items
AddListEntry();
UpdateResistances();
UpdateShownItems(mod.scroll);
end
-- Build Detail List
local function BuildInfoList()
displayList.count = 0;
--- Show Details from Modules
for index, mod in ipairs(ex.modules) do
if (mod.details) and (#mod.details.entries > 0) then
for index, entry in ipairs(mod.details.entries) do
AddListEntry(entry.label,entry.value,entry.tip);
end
end
end
-- Add Padding + Update Resistances + Shown Items
AddListEntry();
UpdateResistances();
UpdateShownItems(mod.scroll);
end
-- Build the Shown List
function mod:BuildShownList()
if (cfg.statsViewType == 1) then
BuildStatList();
else
BuildInfoList();
end
end
--------------------------------------------------------------------------------------------------------
-- Widget Creation --
--------------------------------------------------------------------------------------------------------
-- Resistance Boxes
for i = 1, 5 do
local t = CreateFrame("Frame",nil,mod.page);
t:SetSize(32,29);
t.texture = t:CreateTexture(nil,"BACKGROUND");
t.texture:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ResistanceIcons");
local normHeight, offset = 0.11328125, 0.00;
t.texture:SetTexCoord(offset,1 - offset,(i - 1 + offset) * normHeight,(i - offset) * normHeight);
t.texture:SetAllPoints();
t.value = t:CreateFontString(nil,"ARTWORK","GameFontNormal");
t.value:SetFont(GameFontNormal:GetFont(),12,"OUTLINE");
t.value:SetPoint("BOTTOM",1,3);
t.value:SetTextColor(1,1,0);
if (i == 1) then
t:SetPoint("TOPLEFT",36,-9);
else
t:SetPoint("LEFT",resists[i - 1],"RIGHT");
end
resists[i] = t;
end
-- Stat Entries
local StatEntry_OnEnter = function(self,motion) gtt:SetOwner(self,"ANCHOR_RIGHT"); gtt:SetText(self.tip); end
for i = 1, 20 do
local t = CreateFrame("Frame",nil,mod.page);
t:SetSize(200,ITEM_HEIGHT);
t.id = i;
if (i == 1) then
t:SetPoint("TOPLEFT",8,-40);
else
t:SetPoint("TOPLEFT",entries[i - 1],"BOTTOMLEFT");
t:SetPoint("TOPRIGHT",entries[i - 1],"BOTTOMRIGHT");
end
t.left = t:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall");
t.left:SetPoint("LEFT");
t.right = t:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall");
t.right:SetPoint("RIGHT");
t.right:SetPoint("LEFT",t.left,"RIGHT");
t.right:SetTextColor(1,1,0);
t.right:SetJustifyH("RIGHT");
t.tip = CreateFrame("Frame",nil,t);
t.tip:SetPoint("TOPRIGHT");
t.tip:SetPoint("BOTTOMRIGHT");
t.tip:SetScript("OnEnter",StatEntry_OnEnter);
t.tip:SetScript("OnLeave",ex.HideGTT);
t.tip:EnableMouse(true);
entries[i] = t;
end
-- Scroll
mod.scroll = CreateFrame("ScrollFrame","Examiner"..mod.token.."Scroll",mod.page,"FauxScrollFrameTemplate");
mod.scroll:SetPoint("TOPLEFT",entries[1]);
mod.scroll:SetPoint("BOTTOMRIGHT",entries[#entries],-3,-1);
mod.scroll:SetScript("OnVerticalScroll",function(self,offset) FauxScrollFrame_OnVerticalScroll(self,offset,ITEM_HEIGHT,UpdateShownItems) end);