-- labels for key binding configuration -- standard location for these variables: -- GlobalStrings.lua BINDING_NAME_TOGGLEKILLLOG = "Toggle Kill Log"; BINDING_NAME_RELOADUI = "Reload User Interface"; -- tell UI how to handle this frame -- standard location for this variable: -- UIParent.lua UIPanelWindows["KillLogFrame"] = { area = "left", pushable = 0 }; KILL_LOG_VERSION = "1.1"; KILLS_DISPLAYED = 11; RECENT_KILLS_DISPLAYED = 10; KILLLOG_KILL_HEIGHT = 16; KILL_LOG = "Kill Log"; -- BEGIN added to allow enabling / disabling -- disabling Kill Log will lead to two things - the hiding of the Kill Log window, and Kill Log ignoring events -- I've followed (read: copied) the procedures of Quest Share to a very great extent, so who knows if it will work KillLog_ModEnabled = true; -- END added to allow enabling / disabling KillLogCVarMax = 0; KillLogCVarPrefix = nil; KillLogVars = { }; DeathCount = 0; DeathLog = { }; KillLogRecentVars = { }; KillLogByName = { }; KillLogDisplay = { }; KillDifficultyOrder = { verydifficult = 1, difficult = 2, standard = 3, trivial = 4, unknown = 5 }; --KillDifficultyColor = { }; --KillDifficultyColor["verydifficult"] = { r = 1.00, g = 0.50, b = 0.25 }; --KillDifficultyColor["difficult"] = { r = 1.00, g = 1.00, b = 0.00 }; --KillDifficultyColor["standard"] = { r = 0.25, g = 0.75, b = 0.25 }; --KillDifficultyColor["trivial"] = { r = 0.50, g = 0.50, b = 0.50 }; --KillDifficultyColor["unknown"] = { r = 0.10, g = 0.10, b = 1.00 }; --KillDifficultyColor["header"] = { r = 0.70, g = 0.70, b = 0.70 }; QuestDifficultyColor["unknown"] = { r = 0.25, g = 0.25, b = 0.75 }; KillLogSortValues = { }; table.setn(KillLogSortValues, 12); KillLogSortValues[1] = { label = 'Name', value = 'Name' }; KillLogSortValues[2] = { label = 'Level', value = 'Level' }; KillLogSortValues[3] = { label = 'Type', value = 'Type' }; KillLogSortValues[4] = { label = 'Kills', value = 'Kills' }; KillLogSortValues[5] = { label = 'Deaths', value = 'Deaths' }; KillLogSortValues[6] = { label = 'XP', value = 'XP' }; KillLogSortValues[7] = { label = 'Player Melee Hit %', value = 'P Hit' }; KillLogSortValues[8] = { label = 'Player Melee Crit %', value = 'P Crit' }; KillLogSortValues[9] = { label = 'Player Melee Avg Dmg', value = 'P Dmg' }; KillLogSortValues[10] = { label = 'Mob Melee Hit %', value = 'M Hit' }; KillLogSortValues[11] = { label = 'Mob Melee Crit %', value = 'M Crit' }; KillLogSortValues[12] = { label = 'Mob Melee Avg Dmg', value = 'M Dmg' }; KillLogDetailTypes = { }; table.setn(KillLogDetailTypes, 3); KillLogDetailTypes[1] = { label = 'Melee', value = 'Melee' }; KillLogDetailTypes[2] = { label = 'Spell, Ability & Ranged', value = 'Spell' }; KillLogDetailTypes[3] = { label = 'Pet', value = 'Pet' }; -- BEGIN added to allow enabling/disabling function KillLog_Enable(value,checked) if (checked == 1) then KillLog_DebugMessage("Enabling Quest Share"); KillLog_ModEnabled = true; KillLog_EnableMod(); else KillLog_ModEnabled = false; HideUIPanel(KillLogFrame); end end function KillLog_EnableMod() KillLog_Init(); end -- END added to allow enabling/disabling function ToggleKillLog() -- BEGIN added to allow enabling/disabling -- probably not necessary, but since Quest Share had it... :) if (not KillLog_ModEnabled) then HideUIPanel(KillLogFrame); else if ( KillLogFrame:IsVisible() ) then HideUIPanel(KillLogFrame); else ShowUIPanel(KillLogFrame); end end -- END added to allow enabling/disabling end function DefineKillLogDisplay() local offset = 0; local KillLogDisplayNew = { }; if ( KillLogRecentVars[1] ) then KillLogDisplay = { }; table.foreach(KillLogRecentVars, InsertKillLogDisplay); if ( table.getn(KillLogDisplay) > 0 ) then KillLogDisplayNew[1] = { name = "Recent", isHeader = 1, isCollapsed = nil }; if ( KillLogFrame.sortText ~= 'Type' ) then QuickSort(KillLogDisplay, KillLogFrame.sort); else QuickSort(KillLogDisplay, CompareName); end for i=1, table.getn(KillLogDisplay) do KillLogDisplayNew[i+1] = KillLogDisplay[i]; end offset = table.getn(KillLogDisplay) + 1; end end if ( KillLogVars[1] ) then KillLogDisplay = { }; table.foreach(KillLogVars, InsertKillLogDisplay); if table.getn(KillLogDisplay) > 0 then QuickSort(KillLogDisplay, KillLogFrame.sort); local header, lastHeader, selectedMobVisible; for i=1, table.getn(KillLogDisplay) do header = GetKillLogHeader(KillLogDisplay[i]); if ( not lastHeader or lastHeader ~= header ) then KillLogDisplayNew[i+offset] = { name = header, isHeader = 1, isCollapsed = nil }; offset = offset + 1; lastHeader = header; end KillLogDisplayNew[i+offset] = KillLogDisplay[i]; if ( not KillLogFrame.combatStats and (KillLogDisplay[i].meleeDamageToMob ~= 0 or KillLogDisplay[i].meleeDamageToPlayer ~= 0) ) then KillLogFrame.combatStats = 1; end --hide detail if the selected mob was filtered if ( KillLogFrame.selectedMob and KillLogFrame.selectedMob == KillLogDisplay[i].name ) then selectedMobVisible = 1; end end if ( not selectedMobVisible ) then KillLog_SetSelection(); end table.setn(KillLogDisplay, table.getn(KillLogDisplay) + offset); for k, v in KillLogDisplayNew do KillLogDisplay[k] = v; end end end end function InsertKillLogDisplay(index, value) if ( not KillLogFrame.filterEncountered and value.kills == 0 ) then return nil; end value.isHeader = nil; value.isCollapsed = nil; local playerLevel = UnitLevel("player"); if ( value.minLevel == 1000 and value.maxLevel == -1 ) then value.mobDifficulty = 'unknown'; elseif ( value.maxLevel < (playerLevel - 5) or value.creatureType == 'Ambient' ) then value.mobDifficulty = 'trivial'; elseif ( value.maxLevel <= playerLevel ) then value.mobDifficulty = 'standard'; elseif ( value.maxLevel <= (playerLevel + 5) ) then value.mobDifficulty = 'difficult'; else value.mobDifficulty = 'verydifficult'; end if ( not KillLogFrame.filterTrivial and (value.mobDifficulty == 'trivial' or value.mobDifficulty == 'unknown') ) then return nil; end --create aliases to stats used for sorting if ( KillLogFrame.sortDetail == 'Melee' ) then value.sortHitsToMob = value.meleeHitsToMob; value.sortCritsToMob = value.meleeCritsToMob; value.sortEvadesToMob = value.meleeEvadesToMob; value.sortMissesToMob = value.meleeMissesToMob; value.sortDamageToMob = value.meleeDamageToMob; value.sortHitsToPlayer = value.meleeHitsToPlayer; value.sortCritsToPlayer = value.meleeCritsToPlayer; value.sortEvadesToPlayer = value.meleeEvadesToPlayer; value.sortMissesToPlayer = value.meleeMissesToPlayer; value.sortDamageToPlayer = value.meleeDamageToPlayer; elseif ( KillLogFrame.sortDetail == 'Spell' ) then value.sortHitsToMob = value.spellHitsToMob; value.sortCritsToMob = value.spellCritsToMob; value.sortEvadesToMob = value.spellEvadesToMob; value.sortMissesToMob = value.spellMissesToMob; value.sortDamageToMob = value.spellDamageToMob; value.sortHitsToPlayer = value.spellHitsToPlayer; value.sortCritsToPlayer = value.spellCritsToPlayer; value.sortEvadesToPlayer = value.spellEvadesToPlayer; value.sortMissesToPlayer = value.spellMissesToPlayer; value.sortDamageToPlayer = value.spellDamageToPlayer; elseif ( KillLogFrame.sortDetail == 'Pet' ) then value.sortHitsToMob = value.petHitsToMob; value.sortCritsToMob = value.petCritsToMob; value.sortEvadesToMob = value.petEvadesToMob; value.sortMissesToMob = value.petMissesToMob; value.sortDamageToMob = value.petDamageToMob; value.sortHitsToPlayer = value.petHitsToPlayer; value.sortCritsToPlayer = value.petCritsToPlayer; value.sortEvadesToPlayer = value.petEvadesToPlayer; value.sortMissesToPlayer = value.petMissesToPlayer; value.sortDamageToPlayer = value.petDamageToPlayer; end --create aliases to stats used for detail information if ( KillLogFrame.detailFilterText == 'Melee' ) then value.detailHitsToMob = value.meleeHitsToMob; value.detailCritsToMob = value.meleeCritsToMob; value.detailEvadesToMob = value.meleeEvadesToMob; value.detailMissesToMob = value.meleeMissesToMob; value.detailDamageToMob = value.meleeDamageToMob; value.detailHitsToPlayer = value.meleeHitsToPlayer; value.detailCritsToPlayer = value.meleeCritsToPlayer; value.detailEvadesToPlayer = value.meleeEvadesToPlayer; value.detailMissesToPlayer = value.meleeMissesToPlayer; value.detailDamageToPlayer = value.meleeDamageToPlayer; elseif ( KillLogFrame.detailFilterText == 'Spell' ) then value.detailHitsToMob = value.spellHitsToMob; value.detailCritsToMob = value.spellCritsToMob; value.detailEvadesToMob = value.spellEvadesToMob; value.detailMissesToMob = value.spellMissesToMob; value.detailDamageToMob = value.spellDamageToMob; value.detailHitsToPlayer = value.spellHitsToPlayer; value.detailCritsToPlayer = value.spellCritsToPlayer; value.detailEvadesToPlayer = value.spellEvadesToPlayer; value.detailMissesToPlayer = value.spellMissesToPlayer; value.detailDamageToPlayer = value.spellDamageToPlayer; elseif ( KillLogFrame.detailFilterText == 'Pet' ) then value.detailHitsToMob = value.petHitsToMob; value.detailCritsToMob = value.petCritsToMob; value.detailEvadesToMob = value.petEvadesToMob; value.detailMissesToMob = value.petMissesToMob; value.detailDamageToMob = value.petDamageToMob; value.detailHitsToPlayer = value.petHitsToPlayer; value.detailCritsToPlayer = value.petCritsToPlayer; value.detailEvadesToPlayer = value.petEvadesToPlayer; value.detailMissesToPlayer = value.petMissesToPlayer; value.detailDamageToPlayer = value.petDamageToPlayer; end -- cache detail sort if sorting by detail if ( KillLogFrame.sortText == 'P Hit' ) then local totalSwings = value.sortHitsToMob + value.sortCritsToMob + value.sortEvadesToMob + value.sortMissesToMob; if ( totalSwings == 0 ) then value.detailSort = 0; else value.detailSort = (value.sortHitsToMob + value.sortCritsToMob) * 100 / totalSwings; end elseif ( KillLogFrame.sortText == 'P Crit' ) then local totalSwings = value.sortHitsToMob + value.sortCritsToMob + value.sortEvadesToMob + value.sortMissesToMob; if ( totalSwings == 0 ) then value.detailSort = 0; else value.detailSort = value.sortCritsToMob * 100 / totalSwings; end elseif ( KillLogFrame.sortText == 'P Dmg' ) then if ( (value.sortHitsToMob + value.sortCritsToMob) == 0 ) then value.detailSort = 0; else value.detailSort = value.sortDamageToMob / (value.sortHitsToMob + value.sortCritsToMob); end elseif ( KillLogFrame.sortText == 'M Hit' ) then local totalSwings = value.sortHitsToPlayer + value.sortCritsToPlayer + value.sortEvadesToPlayer + value.sortMissesToPlayer; if ( totalSwings == 0 ) then value.detailSort = 0; else value.detailSort = (value.sortHitsToPlayer + value.sortCritsToPlayer) * 100 / totalSwings; end elseif ( KillLogFrame.sortText == 'M Crit' ) then local totalSwings = value.sortHitsToPlayer + value.sortCritsToPlayer + value.sortEvadesToPlayer + value.sortMissesToPlayer; if ( totalSwings == 0 ) then value.detailSort = 0; else value.detailSort = value.sortCritsToPlayer * 100 / totalSwings; end elseif ( KillLogFrame.sortText == 'M Dmg' ) then if ( (value.sortHitsToPlayer + value.sortCritsToPlayer) == 0 ) then value.detailSort = 0; else value.detailSort = value.sortDamageToPlayer / (value.sortHitsToPlayer + value.sortCritsToPlayer); end end table.insert(KillLogDisplay, value); return nil; end function KillLog_Update() local numEntries = GetNumKillLogEntries(); local mobOffset = FauxScrollFrame_GetOffset(KillLogListScrollFrame); if ( numEntries == 0 ) then KillLogCollapseAllButton:Disable(); KillLogClearButton:Disable(); KillLogResetButton:Disable(); KillLog_SetSelection(); else KillLogCollapseAllButton:Enable(); KillLogClearButton:Enable(); if ( KillLogFrame.combatStats ) then KillLogResetButton:Enable(); end end FauxScrollFrame_Update(KillLogListScrollFrame, numEntries, KILLS_DISPLAYED, KILLLOG_KILL_HEIGHT, KillLogHighlightFrame, 293, 316 ) -- KillLogRecentHighlightFrame:Hide(); KillLogHighlightFrame:Hide(); -- local isRecent; local highlightApplied; for i=1, KILLS_DISPLAYED, 1 do local killIndex = i + mobOffset; local killButton = getglobal("KillLogTitle"..i); if ( killIndex > numEntries ) then killButton:Hide(); else local killData = GetKillLogDisplay(killIndex); local color; if ( killData.isHeader ) then if ( not killData.name ) then killButton:SetText(""); else killButton:SetText(killData.name); -- if ( killData.name == 'Recent' ) then -- isRecent = 1; -- elseif ( isRecent ) then -- isRecent = nil; -- end end if ( killData.isCollapsed ) then killButton:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up"); else killButton:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up"); end color = QuestDifficultyColor["header"]; getglobal("KillLogTitle"..i.."Highlight"):SetTexture("Interface\\Buttons\\UI-PlusButton-Hilight"); else killButton:SetText(" "..killData.name); killButton:SetNormalTexture(""); color = QuestDifficultyColor[killData.mobDifficulty]; getglobal("KillLogTitle"..i.."Highlight"):SetTexture(""); end killButton:SetTextColor(color.r, color.g, color.b); killButton:Show(); killButton:SetID(killIndex); -- if ( isRecent ) then -- if ( KillLogFrame.selectedName and killData.name == KillLogFrame.selectedName ) then -- KillLogRecentHighlightFrame:SetPoint("TOPLEFT", "KillLogTitle"..i, "TOPLEFT", 0, 0); -- KillLogRecentHighlightFrame:Show(); -- killButton:LockHighlight(); -- else -- killButton:UnlockHighlight(); -- end -- else if ( KillLogFrame.selectedID and killIndex == KillLogFrame.selectedID and KillLogFrame.selectedName and killData.name == KillLogFrame.selectedName ) then --highlightApplied = 1; KillLogHighlightFrame:SetPoint("TOPLEFT", "KillLogTitle"..i, "TOPLEFT", 0, 0); KillLogHighlightFrame:Show(); killButton:LockHighlight(); elseif ( not KillLogFrame.selectedID and KillLogFrame.selectedName and killData.name == KillLogFrame.selectedName ) then --highlightApplied = 1; KillLogFrame.selectedID = killIndex; KillLogHighlightFrame:SetPoint("TOPLEFT", "KillLogTitle"..i, "TOPLEFT", 0, 0); KillLogHighlightFrame:Show(); killButton:LockHighlight(); else killButton:UnlockHighlight(); end -- end end end if ( KillLogFrame.selectedID and (GetKillLogDisplay(KillLogFrame.selectedID) ~= nil)) then KillLog_ShowMobDetails(); else KillLog_HideMobDetails(); end local numHeaders = 0; local notExpanded = 0; for i=1, numEntries, 1 do local index = i; local killData = GetKillLogDisplay(i); if ( killData.name and killData.isHeader ) then numHeaders = numHeaders + 1; if ( killData.isCollapsed ) then notExpanded = notExpanded + 1; end end end if ( notExpanded ~= numHeaders ) then KillLogCollapseAllButton.collapsed = nil; KillLogCollapseAllButton:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up"); else KillLogCollapseAllButton.collapsed = 1; KillLogCollapseAllButton:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up"); end end function KillLog_GetFirstSelectableMob() if ( GetNumKillLogEntries() > 0 ) then KillLogFrame.showMobDetails = 1; if ( not KillLogRecentVars[1] ) then KillLog_SetSelection(2); else for i=1, KILLS_DISPLAYED do if ( not KillLogDisplay[i] ) then break; elseif ( KillLogDisplay[i].name == KillLogRecentVars[1].name ) then KillLog_SetSelection(i); break; end end end FauxScrollFrame_SetOffset(KillLogListScrollFrame, 0); else KillLogFrame.showMobDetails = nil; KillLog_SetSelection(); end KillLogListScrollFrameScrollBar:SetValue(0); end function KillLog_SetSelection(id) if ( not id ) then KillLog_HideMobDetails(); return; elseif ( KillLogFrame.showMobDetails ) then KillLog_ShowMobDetails(); else KillLog_HideMobDetails(); return; end local killID = id; local killData = GetKillLogDisplay(killID); if ( not killData ) then return; end KillLogHighlightFrame:Show(); if ( killData.isHeader ) then KillLogHighlightFrame:Hide(); --KillLogFrame.selectedID = nil; --KillLogFrame.selectedName = nil; if ( killData.isCollapsed ) then ExpandKillHeader(killID); return; else CollapseKillHeader(killID); return; end else KillLogFrame.selectedID = id; KillLogFrame.selectedName = killData.name; local color = QuestDifficultyColor[killData.mobDifficulty]; --KillLogRecentMobHighlight:SetVertexColor(color.r, color.g, color.b); KillLogMobHighlight:SetVertexColor(color.r, color.g, color.b); end if ( GetKillLogSelection() > GetNumKillLogEntries() ) then return; end KillLog_UpdateDetails(); end function KillLog_UpdateDetails() local killID = GetKillLogSelection(); local killData = GetKillLogDisplay(killID); if ( not killData or killData.isHeader ) then return; end -- not able to store picture to date, no need to change image from the default unknown one --if ( killData.portrait ) then -- KillLogMobPortrait:SetTexture(killData.portrait); --else -- KillLogMobPortrait:SetTexture("Interface\\CharacterFrame\\TempPortrait"); --end KillLogMobTitle:SetText(killData.name); local level; if ( killData.mobDifficulty == 'unknown') then level = '???'; elseif ( killData.minLevel == killData.maxLevel ) then if ( killData.isElite == 1 ) then level = killData.minLevel..'+'; else level = killData.minLevel; end else if ( killData.isElite == 1 ) then level = killData.minLevel..'+ to '..killData.maxLevel..'+'; else level = killData.minLevel..' to '..killData.maxLevel; end end if ( level ) then level = level.." "..killData.creatureType; KillLogMobLevelText:SetText("Level "..level); end KillLogMobKillsStat:SetText(killData.kills); KillLogMobDeathsStat:SetText(killData.deaths); KillLogMobXPStat:SetText(killData.xpEarned); local totalSwings = killData.detailHitsToMob + killData.detailCritsToMob + killData.detailEvadesToMob + killData.detailMissesToMob; if ( totalSwings ~= 0 ) then KillLogStatsPlayerHit:SetText(format('%.01f', (killData.detailHitsToMob + killData.detailCritsToMob) * 100 / totalSwings )); KillLogStatsPlayerCrit:SetText(format('%.01f', killData.detailCritsToMob * 100 / totalSwings )); else KillLogStatsPlayerHit:SetText('n/a'); KillLogStatsPlayerCrit:SetText('n/a'); end if ( (killData.detailHitsToMob + killData.detailCritsToMob) ~= 0 ) then KillLogStatsPlayerDamage:SetText(format('%.01f', killData.detailDamageToMob / (killData.detailHitsToMob + killData.detailCritsToMob))); else KillLogStatsPlayerDamage:SetText('n/a'); end totalSwings = killData.detailHitsToPlayer + killData.detailCritsToPlayer + killData.detailEvadesToPlayer + killData.detailMissesToPlayer; if ( totalSwings ~= 0 ) then KillLogStatsMobHit:SetText(format('%.01f', (killData.detailHitsToPlayer + killData.detailCritsToPlayer) * 100 / totalSwings )); KillLogStatsMobCrit:SetText(format('%.01f', killData.detailCritsToPlayer * 100 / totalSwings )); else KillLogStatsMobHit:SetText('n/a'); KillLogStatsMobCrit:SetText('n/a'); end if ( (killData.detailHitsToPlayer + killData.detailCritsToPlayer) ~= 0 ) then KillLogStatsMobDamage:SetText(format('%.01f', killData.detailDamageToPlayer / (killData.detailHitsToPlayer + killData.detailCritsToPlayer))); else KillLogStatsMobDamage:SetText('n/a'); end KillLogDetailScrollFrameScrollBar:SetValue(0); KillLogDetailScrollFrame:UpdateScrollChildRect(); if ( (KillLogDetailScrollFrameScrollBarScrollUpButton:IsEnabled() == 0) and (KillLogDetailScrollFrameScrollBarScrollDownButton:IsEnabled() == 0) ) then KillLogDetailScrollFrameScrollBar:Hide(); KillLogDetailScrollFrameTop:Hide(); KillLogDetailScrollFrameBottom:Hide(); else KillLogDetailScrollFrameScrollBar:Show(); KillLogDetailScrollFrameTop:Show(); KillLogDetailScrollFrameBottom:Show(); end end function KillLog_ShowMobDetails() KillLogMobIcon:Show(); KillLogMobTitle:Show(); KillLogMobLevelText:Show(); KillLogMobKillsLabel:Show(); KillLogMobKillsStat:Show(); KillLogMobDeathsLabel:Show(); KillLogMobDeathsStat:Show(); KillLogMobXPLabel:Show(); KillLogMobXPStat:Show(); KillLogDetailTypeDropDown:Show(); KillLogStatsHitLabel:Show(); KillLogStatsCritLabel:Show(); KillLogStatsDamageLabel:Show(); KillLogStatsPlayerLabel:Show(); KillLogStatsPlayerHit:Show(); KillLogStatsPlayerCrit:Show(); KillLogStatsPlayerDamage:Show(); KillLogStatsMobLabel:Show(); KillLogStatsMobHit:Show(); KillLogStatsMobCrit:Show(); KillLogStatsMobDamage:Show(); end function KillLog_HideMobDetails() KillLogMobIcon:Hide(); KillLogMobTitle:Hide(); KillLogMobLevelText:Hide(); KillLogMobKillsLabel:Hide(); KillLogMobKillsStat:Hide(); KillLogMobDeathsLabel:Hide(); KillLogMobDeathsStat:Hide(); KillLogMobXPLabel:Hide(); KillLogMobXPStat:Hide(); KillLogDetailTypeDropDown:Hide(); KillLogStatsHitLabel:Hide(); KillLogStatsCritLabel:Hide(); KillLogStatsDamageLabel:Hide(); KillLogStatsPlayerLabel:Hide(); KillLogStatsPlayerHit:Hide(); KillLogStatsPlayerCrit:Hide(); KillLogStatsPlayerDamage:Hide(); KillLogStatsMobLabel:Hide(); KillLogStatsMobHit:Hide(); KillLogStatsMobCrit:Hide(); KillLogStatsMobDamage:Hide(); end function GetNumKillLogEntries() local count = table.getn(KillLogDisplay); for i=1, table.getn(KillLogDisplay) do if ( KillLogDisplay[i].isHeader ) then if ( KillLogDisplay[i].isCollapsed ) then count = count - KillLogDisplay[i].isCollapsed; end end end return count; end function GetKillLogDisplay(id) local offset = 0; for i=1, table.getn(KillLogDisplay) do if ( i == id + offset ) then return KillLogDisplay[i]; end if ( KillLogDisplay[i].isHeader and KillLogDisplay[i].isCollapsed ) then offset = offset + KillLogDisplay[i].isCollapsed; i = i + KillLogDisplay[i].isCollapsed; end end return nil; end function GetKillLogSelection() if ( not KillLogFrame.selectedID ) then return 0; else return KillLogFrame.selectedID; end end function KillLogTitleButton_OnLoad() this:RegisterForClicks("LeftButtonUp", "RightButtonUp"); end -- --- ---- ----- Header Functions ---- --- -- function GetKillLogHeader(killData) if ( KillLogFrame.sortText == 'Level' ) then if ( killData.mobDifficulty == 'unknown' ) then return 'Unknown'; else local rangeMin = (math.floor((killData.maxLevel) / 5) * 5); local rangeMax = rangeMin + 4; return rangeMin..' to '..rangeMax; end elseif ( KillLogFrame.sortText == 'Type' ) then -- This should perform ucfirst on the header... the pattern matches but the replaced string is not converted to uppercase? --return string.gsub(killData.creatureType, "^(%l)", string.upper("%1")); -- creature type is now capitolized already --return string.upper(string.sub(killData.creatureType, 1, 1))..string.sub(killData.creatureType, 2); return killData.creatureType; elseif ( KillLogFrame.sortText == 'P Hit' or KillLogFrame.sortText == 'P Crit' or KillLogFrame.sortText == 'M Hit' or KillLogFrame.sortText == 'M Crit') then return format('%d%%', math.floor(killData.detailSort / 10) * 10); else return 'All'; end end function ExpandKillHeader(id) -- expand ALL headers if ( id == 0 ) then for i=1, table.getn(KillLogDisplay) do if ( KillLogDisplay[i].isHeader ) then KillLogDisplay[i].isCollapsed = nil; end end -- expand single headers else GetKillLogDisplay(id).isCollapsed = nil; end KillLogFrame.selectedID = nil; KillLog_Update(); end function CollapseKillHeader(id) local count = 0; -- collapse ALL headers -- within block, id is changed to the current header index if ( id == 0 ) then for i=1, table.getn(KillLogDisplay) do if ( KillLogDisplay[i].isHeader ) then if ( id ~= 0 ) then KillLogDisplay[id].isCollapsed = count; end count = 0; id = i; else count = count + 1; end end KillLogDisplay[id].isCollapsed = count; -- collapse single header else local killData = GetKillLogDisplay(id); if ( not killData.isCollapsed ) then for i=id+1, table.getn(KillLogDisplay) do local tempData = GetKillLogDisplay(i); if ( not tempData or tempData.isHeader ) then break; end count = count + 1; end killData.isCollapsed = count; end end KillLogFrame.selectedID = nil; KillLog_Update(); end -- --- ---- ----- XML Tied functions (for funstions closely tied to the xml file) ---- --- -- function KillLog_OnLoad() -- BEGIN added to allow enabling / disabling Kill Log -- (copied and pasted from Quest Share) -- Register with CSM (Cosmos Master Configuration) -- CosmosMaster_Register("COS_KILLLOG_SECTION","SEPARATOR","Kill Log","This section is devoted to Kill Log options"); CosmosMaster_Register("COS_ENABLEKILLLOG","CHECKBOX","Enable Kill Log", "Click here to turn on/off the Quest Sharing system,\nWhich allows you to share your quests with your party", KillLog_Enable, 1, 0, 0, 1, "", .01, 1, "\%" ); -- END added to allow enabling / disabling Kill Log -- Comparison functions for sorting by different fields. KillLogSortValues[1].compareFunction = CompareName; KillLogSortValues[2].compareFunction = CompareLevel; KillLogSortValues[3].compareFunction = CompareType; KillLogSortValues[4].compareFunction = CompareKills; KillLogSortValues[5].compareFunction = CompareDeaths; KillLogSortValues[6].compareFunction = CompareXP; KillLogSortValues[7].compareFunction = CompareDetail; KillLogSortValues[8].compareFunction = CompareDetail; KillLogSortValues[9].compareFunction = CompareDetail; KillLogSortValues[10].compareFunction = CompareDetail; KillLogSortValues[11].compareFunction = CompareDetail; KillLogSortValues[12].compareFunction = CompareDetail; -- Register events that have important info this:RegisterEvent("CHAT_MSG_COMBAT_LOG_ENEMY"); this:RegisterEvent("CHAT_MSG_COMBAT_LOG_SELF"); this:RegisterEvent("CHAT_MSG_COMBAT_LOG_PARTY"); this:RegisterEvent("CHAT_MSG_COMBAT_LOG_ERROR"); this:RegisterEvent("CHAT_MSG_COMBAT_LOG_MISC_INFO"); this:RegisterEvent("COMBATLOG_XPGAIN_FIRSTPERSON"); this:RegisterEvent("CHAT_MSG_SYSTEM"); -- This flag is only necisary because immediatly after load players don't have a name yet. -- Hopefully this will become unnecesary in a future patch this.initDone = nil; -- Hack put in for creatures that don't give xp this.lastTarget = ''; this.combatStats = nil; KillLogClearButton:Disable(); KillLogResetButton:Disable(); KillLog_HideMobDetails(); KillLogMobKillsLabel:SetText('Kills:'); KillLogMobDeathsLabel:SetText('Deaths:'); KillLogMobXPLabel:SetText('XP:'); KillLogStatsHitLabel:SetText('Hit %'); KillLogStatsCritLabel:SetText('Crit %'); KillLogStatsDamageLabel:SetText('Avg Dmg'); KillLogStatsPlayerLabel:SetText('Player'); KillLogStatsMobLabel:SetText('Mob'); KillLogVersionLabel:SetText('Version:'); KillLogVersionText:SetText(KILL_LOG_VERSION); end function KillLog_OnShow() if ( not this.initDone ) then if ( not KillLogInit() ) then local info = ChatTypeInfo["YELL"]; ChatFrame:AddMessage("Error loading Kill Log; this is usually because you are an Unknown Being.", info.r, info.g, info.b); ToggleKillLog(); return; end end UpdateMicroButtons(); PlaySound("igSpellBookOpen"); DefineKillLogDisplay(); -- reset display to initial settings -- this will clear the last selection and scroll the listing to the top KillLogListScrollFrameScrollBar:SetMinMaxValues(0, 0); KillLogListScrollFrameScrollBar:SetValue(0); KillLog_GetFirstSelectableMob(); KillLog_Update(); end function KillLog_OnHide() UpdateMicroButtons(); PlaySound("igSpellBookClose"); end function KillLogTitleButton_OnClick(button) if ( button == "LeftButton" ) then KillLogFrame.selectedService = this:GetID(); KillLogFrame.showMobDetails = 1; KillLog_SetSelection(this:GetID()) KillLog_Update(); end end function KillLogTitleButton_OnLoad() this:RegisterForClicks("LeftButtonUp", "RightButtonUp"); end function KillLogCollapseAllButton_OnClick() if ( this.collapsed ) then this.collapsed = nil; ExpandKillHeader(0); else this.collapsed = 1; KillLogListScrollFrameScrollBar:SetValue(0); CollapseKillHeader(0); KillLogFrame.selectedID = nil; KillLogFrame.selectedName = nil; end KillLog_Update(); end -- -- Event Handler ... it's a monster -- function KillLog_OnEvent(event) -- START -- is Kill Log enabled? if (not KillLog_ModEnabled) then return; end -- END if ( not this.initDone ) then if ( not KillLogInit() ) then return; end; end if (event == "CHAT_MSG_SYSTEM" and arg1) then for creatureName in string.gfind(arg1, "(.+) has slain you") do KillLog_Encounter(creatureName); KillLog_RecordDeath(creatureName); end end if ( event == "CHAT_MSG_COMBAT_LOG_SELF" ) then for creatureName, xp in string.gfind(arg1, "(.+) dies, you gain (%d+)") do KillLog_Encounter(creatureName); KillLog_RecordKill(creatureName, xp); return; end for spell, creatureName, damage in string.gfind(arg1, "Your (.+) hits (.+) for (%d+).") do KillLog_Encounter(creatureName); KillLog_RecordSpellDamageToMob(creatureName, damage); KillLog_RecordSpellHitToMob(creatureName); return; end for creatureName, damage in string.gfind(arg1, "You hit (.+) for (%d+)") do KillLog_Encounter(creatureName); KillLog_RecordDamageToMob(creatureName, damage); KillLog_RecordHitToMob(creatureName); return; end for spell, creatureName, damage in string.gfind(arg1, "Your (.+) crits (.+) for (%d+).") do KillLog_Encounter(creatureName); KillLog_RecordSpellDamageToMob(creatureName, damage); KillLog_RecordSpellCritToMob(creatureName); return; end for creatureName, damage in string.gfind(arg1, "You crit (.+) for (%d+)") do KillLog_Encounter(creatureName); KillLog_RecordDamageToMob(creatureName, damage); KillLog_RecordCritToMob(creatureName); return; end for creatureName in string.gfind(arg1, "You miss (.+).") do KillLog_Encounter(creatureName); KillLog_RecordMissToMob(creatureName); return; end for creatureName in string.gfind(arg1, "You attack. (.+) parries.") do KillLog_Encounter(creatureName); KillLog_RecordEvadeToMob(creatureName); return; end for creatureName in string.gfind(arg1, "You attack. (.+) evades.") do KillLog_Encounter(creatureName); KillLog_RecordEvadeToMob(creatureName); return; end for creatureName in string.gfind(arg1, "You attack. (.+) dodges.") do KillLog_Encounter(creatureName); KillLog_RecordEvadeToMob(creatureName); return; end for creatureName in string.gfind(arg1, "You attack. (.+) deflects.") do KillLog_Encounter(creatureName); KillLog_RecordEvadeToMob(creatureName); return; end for creatureName in string.gfind(arg1, "You attack. (.+) blocks.") do KillLog_Encounter(creatureName); KillLog_RecordEvadeToMob(creatureName); return; end for spell, creatureName in string.gfind(arg1, "Your (.+) was blocked by (.+).") do KillLog_Encounter(creatureName); KillLog_RecordSpellEvadeToMob(creatureName); return; end for spell, creatureName in string.gfind(arg1, "Your (.+) was deflected by (.+).") do KillLog_Encounter(creatureName); KillLog_RecordSpellEvadeToMob(creatureName); return; end for spell, creatureName in string.gfind(arg1, "Your (.+) was dodged by (.+).") do KillLog_Encounter(creatureName); KillLog_RecordSpellEvadeToMob(creatureName); return; end for spell, creatureName in string.gfind(arg1, "Your (.+) was evaded by (.+).") do KillLog_Encounter(creatureName); KillLog_RecordSpellEvadeToMob(creatureName); return; end for spell, creatureName in string.gfind(arg1, "Your (.+) is parried by (.+)") do KillLog_Encounter(creatureName); KillLog_RecordSpellEvadeToMob(creatureName); return; end for spell, creatureName in string.gfind(arg1, "Your (.+) was resisted by (.+).") do KillLog_Encounter(creatureName); KillLog_RecordSpellEvadeToMob(creatureName); return; end for spell, creatureName in string.gfind(arg1, "Your (.+) failed. (.+) is immune.") do KillLog_Encounter(creatureName); KillLog_RecordSpellHitToMob(creatureName); return; end for spell, creatureName in string.gfind(arg1, "Your (.+) missed (.+).") do KillLog_Encounter(creatureName); KillLog_RecordSpellMissToMob(creatureName); return; end for petName, spell, creatureName in string.gfind(arg1, "Your pet (.+)'s (.+) was blocked by (.+).") do KillLog_Encounter(creatureName); KillLog_RecordPetEvadeToMob(creatureName); return; end for petName, spell, creatureName in string.gfind(arg1, "Your pet (.+)'s (.+) was dodged by (.+).") do KillLog_Encounter(creatureName); KillLog_RecordPetEvadeToMob(creatureName); return; end for petName, spell, creatureName in string.gfind(arg1, "Your pet (.+)'s (.+) was evaded by (.+).") do KillLog_Encounter(creatureName); KillLog_RecordPetEvadeToMob(creatureName); return; end for petName, spell, creatureName in string.gfind(arg1, "Your pet (.+)'s (.+) failed. (.+) is immune.") do KillLog_Encounter(creatureName); KillLog_RecordPetHitToMob(creatureName); return; end for petName, spell, creatureName in string.gfind(arg1, "Your pet (.+)'s (.+) missed (.+).") do KillLog_Encounter(creatureName); KillLog_RecordPetMissToMob(creatureName); return; end for petName, spell, creatureName in string.gfind(arg1, "Your pet (.+)'s (.+) was resisted by (.+).") do KillLog_Encounter(creatureName); KillLog_RecordPetEvadeToMob(creatureName); return; end elseif ( event == "CHAT_MSG_COMBAT_LOG_ENEMY" ) then for creatureName in string.gfind(arg1, "(.+) dies") do KillLog_RecordNoXPKill(creatureName); return; end for creatureName, spell, damage in string.gfind(arg1, "(.+)'s (.+) hits you for (%d+).") do KillLog_Encounter(creatureName); KillLog_RecordSpellDamageToPlayer(creatureName, damage); KillLog_RecordSpellHitToPlayer(creatureName); return; end for creatureName, damage in string.gfind(arg1, "(.+) hits you for (%d+)") do KillLog_Encounter(creatureName); KillLog_RecordDamageToPlayer(creatureName, damage); KillLog_RecordHitToPlayer(creatureName); return; end for creatureName, spell, damage in string.gfind(arg1, "(.+)'s (.+) crits you for (%d+).") do KillLog_Encounter(creatureName); KillLog_RecordSpellDamageToPlayer(creatureName, damage); KillLog_RecordSpellCritToPlayer(creatureName); return; end for creatureName, damage in string.gfind(arg1, "(.+) crits you for (%d+).") do KillLog_Encounter(creatureName); KillLog_RecordDamageToPlayer(creatureName, damage); KillLog_RecordCritToPlayer(creatureName); return; end for creatureName, spell in string.gfind(arg1, "(.+)'s (.+) was blocked by your pet.") do KillLog_Encounter(creatureName); KillLog_RecordPetEvadeToPlayer(creatureName); return; end for creatureName, spell in string.gfind(arg1, "(.+)'s (.+) was dodged by your pet.") do KillLog_Encounter(creatureName); KillLog_RecordPetEvadeToPlayer(creatureName); return; end for creatureName, spell in string.gfind(arg1, "(.+)'s (.+) was evaded by your pet.") do KillLog_Encounter(creatureName); KillLog_RecordPetEvadeToPlayer(creatureName); return; end for creatureName, spell in string.gfind(arg1, "(.+)'s (.+) fails. Your pet is immune.") do KillLog_Encounter(creatureName); KillLog_RecordPetHitToPlayer(creatureName); return; end for creatureName, spell in string.gfind(arg1, "(.+)'s (.+) was resisted by your pet.") do KillLog_Encounter(creatureName); KillLog_RecordPetEvadeToPlayer(creatureName); return; end for creatureName, spell in string.gfind(arg1, "(.+)'s (.+) misses your pet.") do KillLog_Encounter(creatureName); KillLog_RecordPetMissToPlayer(creatureName); return; end for creatureName, spell in string.gfind(arg1, "(.+)'s (.+) was blocked%.") do KillLog_Encounter(creatureName); KillLog_RecordSpellEvadeToPlayer(creatureName); return; end for creatureName, spell in string.gfind(arg1, "(.+)'s (.+) was deflected%.") do KillLog_Encounter(creatureName); KillLog_RecordSpellEvadeToPlayer(creatureName); return; end for creatureName, spell in string.gfind(arg1, "(.+)'s (.+) was dodged%.") do KillLog_Encounter(creatureName); KillLog_RecordSpellEvadeToPlayer(creatureName); return; end for creatureName, spell in string.gfind(arg1, "(.+)'s (.+) was evaded%.") do KillLog_Encounter(creatureName); KillLog_RecordSpellEvadeToPlayer(creatureName); return; end for creatureName, spell in string.gfind(arg1, "(.+)'s (.+) failed. You are immune.") do KillLog_Encounter(creatureName); KillLog_RecordSpellHitToPlayer(creatureName); return; end for creatureName, spell in string.gfind(arg1, "(.+)'s (.+) misses you.") do KillLog_Encounter(creatureName); KillLog_RecordSpellMissToPlayer(creatureName); return; end for creatureName, spell in string.gfind(arg1, "You parry (.+)'s (.+)") do KillLog_Encounter(creatureName); KillLog_RecordSpellMissToPlayer(creatureName); return; end for creatureName, spell in string.gfind(arg1, "(.+)'s (.+) was resisted%.") do KillLog_Encounter(creatureName); KillLog_RecordSpellEvadeToPlayer(creatureName); return; end for creatureName in string.gfind(arg1, "(.+) misses you.") do KillLog_Encounter(creatureName); KillLog_RecordMissToPlayer(creatureName); return; end for creatureName in string.gfind(arg1, "(.+) attacks. You parry.") do KillLog_Encounter(creatureName); KillLog_RecordEvadeToPlayer(creatureName); return; end for creatureName in string.gfind(arg1, "(.+) attacks. You evade.") do KillLog_Encounter(creatureName); KillLog_RecordEvadeToPlayer(creatureName); return; end for creatureName in string.gfind(arg1, "(.+) attacks. You dodge.") do KillLog_Encounter(creatureName); KillLog_RecordEvadeToPlayer(creatureName); return; end for creatureName in string.gfind(arg1, "(.+) attacks. You deflect.") do KillLog_Encounter(creatureName); KillLog_RecordEvadeToPlayer(creatureName); return; end for creatureName in string.gfind(arg1, "(.+) attacks. You block.") do KillLog_Encounter(creatureName); KillLog_RecordEvadeToPlayer(creatureName); return; end for mob1, spell, mob2, damage in string.gfind(arg1, "(.+)'s (.+) hits (.+) for (%d+).") do if mob2 == UnitName('pet') then KillLog_Encounter(mob1); KillLog_RecordPetHitToPlayer(mob1); KillLog_RecordPetDamageToPlayer(mob1, damage); end return; end for mob1, mob2, damage in string.gfind(arg1, "(.+)'s (.+) crits (.+) for (%d+).") do if mob2 == UnitName('pet') then KillLog_Encounter(mob1); KillLog_RecordPetCritToPlayer(mob1); KillLog_RecordPetDamageToPlayer(mob1, damage); end return; end for mob1, mob2, damage in string.gfind(arg1, "(.+) hits (.+) for (%d+).") do if mob2 == UnitName('pet') then KillLog_Encounter(mob1); KillLog_RecordPetHitToPlayer(mob1); KillLog_RecordPetDamageToPlayer(mob1, damage); end return; end for mob1, mob2, damage in string.gfind(arg1, "(.+) crits (.+) for (%d+).") do if mob2 == UnitName('pet') then KillLog_Encounter(mob1); KillLog_RecordPetCritToPlayer(mob1); KillLog_RecordPetDamageToPlayer(mob1, damage); end return; end elseif (event == "CHAT_MSG_COMBAT_LOG_PARTY" ) then for mob1, spell, mob2, damage in string.gfind(arg1, "(.+)'s (.+) hits (.+) for (%d+).") do if mob1 == UnitName('pet') then KillLog_Encounter(mob2); KillLog_RecordPetHitToMob(mob2); KillLog_RecordPetDamageToMob(mob2, damage); end return; end for mob1, mob2, damage in string.gfind(arg1, "(.+)'s (.+) crits (.+) for (%d+).") do if mob1 == UnitName('pet') then KillLog_Encounter(mob2); KillLog_RecordPetCritToMob(mob2); KillLog_RecordPetDamageToMob(mob2, damage); end return; end for mob1, mob2, damage in string.gfind(arg1, "(.+) hits (.+) for (%d+).") do if mob1 == UnitName('pet') then KillLog_Encounter(mob2); KillLog_RecordPetHitToMob(mob2); KillLog_RecordPetDamageToMob(mob2, damage); end return; end for mob1, mob2, damage in string.gfind(arg1, "(.+) crits (.+) for (%d+).") do if mob1 == UnitName('pet') then KillLog_Encounter(mob2); KillLog_RecordPetCritToMob(mob2); KillLog_RecordPetDamageToMob(mob2, damage); end end end end -- -- Sort drop Down Menu -- function KillLogSortDropDown_OnLoad() -- default values KillLogFrame.sort = CompareType; KillLogFrame.sortText = 'Type'; KillLogFrame.sortDetail = 'Melee'; KillLogFrame.detailFilterText = 'Melee'; UIDropDownMenu_SetWidth(80); UIDropDownMenu_Initialize(this, KillLogSortDropDown_Load); end function KillLogSortDropDown_Load() local checked; for i=1, table.getn(KillLogSortValues) do if ( KillLogSortValues[i].value == KillLogFrame.sortText and (i < 7 or KillLogFrame.sortDetail == KillLogFrame.detailFilterText )) then checked = 1; UIDropDownMenu_SetText(KillLogSortValues[i].value, KillLogSortDropDown); else checked = nil; end local info = { }; info.text = KillLogSortValues[i].label; info.func = KillLogSortDropDownButton_OnClick; info.checked = checked; --UIDropDownMenu_AddButton(KillLogSortValues[i].label, KillLogSortDropDownButton_OnClick, checked, KillLogSortDropDown); UIDropDownMenu_AddButton(info); end end function KillLogSortDropDownButton_OnClick() sortID = this:GetID(); if ( SetKillLogSort(sortID) ) then UIDropDownMenu_SetSelectedID(KillLogSortDropDown, sortID); this:SetText(KillLogSortValues[sortID].value); DefineKillLogDisplay(); KillLogFrame.selectedID = nil; KillLog_Update(); end end function SetKillLogSort(id) for i=1, table.getn(KillLogSortValues) do if ( i == id ) then if ( KillLogFrame.sortText == KillLogSortValues[i].value and (i < 7 or KillLogFrame.sortDetail == KillLogFrame.detailFilterText )) then return nil; else KillLogFrame.sortText = KillLogSortValues[i].value; KillLogFrame.sort = KillLogSortValues[i].compareFunction; KillLogFrame.sortDetail = KillLogFrame.detailFilterText; return 1; end end end end -- -- Trivial -- function KillLogTrivial_OnLoad() local text = getglobal(this:GetName().."Text"); text:SetText('Trivial'); this:SetChecked(nil); KillLogFrame.filterTrivial = nil; end function KillLogTrivial_OnClick() if ( this:GetChecked() ) then KillLogFrame.filterTrivial = 1; else KillLogFrame.filterTrivial = nil; end KillLogFrame.selectedID = nil; DefineKillLogDisplay(); KillLog_Update(); end -- -- Encountered -- function KillLogEncountered_OnLoad() local text = getglobal(this:GetName().."Text"); text:SetText('Encountered'); this:SetChecked(nil); KillLogFrame.filterEncountered = nil; end function KillLogEncountered_OnShow() end function KillLogEncountered_OnClick() if ( this:GetChecked() ) then KillLogFrame.filterEncountered = 1; else KillLogFrame.filterEncountered = nil; end KillLogFrame.selectedID = nil; DefineKillLogDisplay(); KillLog_Update(); end -- -- Detail -- function KillLogDetailTypeDropDown_OnLoad() UIDropDownMenu_SetWidth(55); UIDropDownMenu_Initialize(this, KillLogDetailType_Load); end function KillLogDetailType_Load() local checked; for i=1, table.getn(KillLogDetailTypes) do if ( KillLogDetailTypes[i].value ~= KillLogFrame.detailFilterText ) then checked = nil; else checked = 1; UIDropDownMenu_SetText(KillLogDetailTypes[i].value, KillLogDetailTypeDropDown); end --UIDropDownMenu_AddButton(KillLogDetailTypes[i].label, KillLogDetailTypeDropDownButton_OnClick, checked, KillLogDetailTypeDropDown); local info = { }; info.text = KillLogDetailTypes[i].label; info.func = KillLogDetailTypeDropDownButton_OnClick; info.checked = checked; UIDropDownMenu_AddButton(info); end end function KillLogDetailTypeDropDownButton_OnClick() local detailID = this:GetID(); if ( SetKillLogDetailType(detailID) ) then UIDropDownMenu_SetSelectedID(KillLogDetailTypeDropDown, detailID); DebugLog:AddMessage(format('%s => %s', this:GetText(),KillLogDetailTypes[detailID].value), .5, .75, .5); this:SetText(KillLogDetailTypes[detailID].value); this.text = KillLogDetailTypes[detailID].value; --UIDropDownMenu_SetText(this.text, this); KillLog_UpdateDetails(); end end function SetKillLogDetailType(id) for i=1, table.getn(KillLogDetailTypes) do if ( i == id ) then if ( KillLogFrame.detailFilterText == KillLogDetailTypes[i].value ) then return nil; else local value = KillLogDetailTypes[i].value; KillLogFrame.detailFilterText = value; UpdateDetailType(value); return 1; end end end end function UpdateDetailType(detail) KillLogSortValues[7].label = 'Player '..detail..' Hit %'; KillLogSortValues[8].label = 'Player '..detail..' Crit %'; KillLogSortValues[9].label = 'Player '..detail..' Avg Dmg'; KillLogSortValues[10].label = 'Mob '..detail..' Hit %'; KillLogSortValues[11].label = 'Mob '..detail..' Crit %'; KillLogSortValues[12].label = 'Mob '..detail..' Avg Dmg'; --create aliases to stats used for detail information for i=1, table.getn(KillLogDisplay) do local value = KillLogDisplay[i]; if ( detail == 'Melee' ) then value.detailHitsToMob = value.meleeHitsToMob; value.detailCritsToMob = value.meleeCritsToMob; value.detailEvadesToMob = value.meleeEvadesToMob; value.detailMissesToMob = value.meleeMissesToMob; value.detailDamageToMob = value.meleeDamageToMob; value.detailHitsToPlayer = value.meleeHitsToPlayer; value.detailCritsToPlayer = value.meleeCritsToPlayer; value.detailEvadesToPlayer = value.meleeEvadesToPlayer; value.detailMissesToPlayer = value.meleeMissesToPlayer; value.detailDamageToPlayer = value.meleeDamageToPlayer; elseif ( detail == 'Spell' ) then value.detailHitsToMob = value.spellHitsToMob; value.detailCritsToMob = value.spellCritsToMob; value.detailEvadesToMob = value.spellEvadesToMob; value.detailMissesToMob = value.spellMissesToMob; value.detailDamageToMob = value.spellDamageToMob; value.detailHitsToPlayer = value.spellHitsToPlayer; value.detailCritsToPlayer = value.spellCritsToPlayer; value.detailEvadesToPlayer = value.spellEvadesToPlayer; value.detailMissesToPlayer = value.spellMissesToPlayer; value.detailDamageToPlayer = value.spellDamageToPlayer; elseif ( detail == 'Pet' ) then value.detailHitsToMob = value.petHitsToMob; value.detailCritsToMob = value.petCritsToMob; value.detailEvadesToMob = value.petEvadesToMob; value.detailMissesToMob = value.petMissesToMob; value.detailDamageToMob = value.petDamageToMob; value.detailHitsToPlayer = value.petHitsToPlayer; value.detailCritsToPlayer = value.petCritsToPlayer; value.detailEvadesToPlayer = value.petEvadesToPlayer; value.detailMissesToPlayer = value.petMissesToPlayer; value.detailDamageToPlayer = value.petDamageToPlayer; end end end -- --- ---- ----- Functions for adding and removing data from KillLogVars table ---- --- -- -- -- Kills, Deaths, & PvP -- function KillLog_RecordKill(creature, xp) KillLogByName[creature].kills = KillLogByName[creature].kills + 1; KillLogByName[creature].xpEarned = KillLogByName[creature].xpEarned + xp; KillLog_Save(); end function KillLog_RecordDeath(creature) KillLogByName[creature].deaths = KillLogByName[creature].deaths + 1; DeathCount = DeathCount + 1; DeathLog[DeathCount] = { atLevel = UnitLevel('player'), byMobIndex = KillLogByName[creature].index, time = 'long ago', deathType = 1} KillLog_Save(); end function KillLog_RecordNoXPKill(creature) if ( creature == this.lastTarget ) then KillLogByName[creature].kills = KillLogByName[creature].kills + 1; end KillLog_Save(); end -- -- Melee -- function KillLog_RecordHitToMob(creature) KillLogByName[creature].meleeHitsToMob = KillLogByName[creature].meleeHitsToMob + 1; KillLog_Save(); end function KillLog_RecordHitToPlayer(creature) KillLogByName[creature].meleeHitsToPlayer = KillLogByName[creature].meleeHitsToPlayer + 1; KillLog_Save(); end function KillLog_RecordCritToMob(creature) KillLogByName[creature].meleeCritsToMob = KillLogByName[creature].meleeCritsToMob + 1; KillLog_Save(); end function KillLog_RecordCritToPlayer(creature) KillLogByName[creature].meleeCritsToPlayer = KillLogByName[creature].meleeCritsToPlayer + 1; KillLog_Save(); end function KillLog_RecordMissToMob(creature) KillLogByName[creature].meleeMissesToMob = KillLogByName[creature].meleeMissesToMob + 1; KillLog_Save(); end function KillLog_RecordMissToPlayer(creature) KillLogByName[creature].meleeMissesToPlayer = KillLogByName[creature].meleeMissesToPlayer + 1; KillLog_Save(); end function KillLog_RecordEvadeToMob(creature) KillLogByName[creature].meleeEvadesToMob = KillLogByName[creature].meleeEvadesToMob + 1; KillLog_Save(); end function KillLog_RecordEvadeToPlayer(creature) KillLogByName[creature].meleeEvadesToPlayer = KillLogByName[creature].meleeEvadesToPlayer + 1; KillLog_Save(); end function KillLog_RecordDamageToMob(creature, dmg) KillLogByName[creature].meleeDamageToMob = KillLogByName[creature].meleeDamageToMob + dmg; KillLog_Save(); end function KillLog_RecordDamageToPlayer(creature, dmg) KillLogByName[creature].meleeDamageToPlayer = KillLogByName[creature].meleeDamageToPlayer + dmg; KillLog_Save(); end -- -- Pets -- function KillLog_RecordPetHitToMob(creature) KillLogByName[creature].petHitsToMob = KillLogByName[creature].petHitsToMob + 1; KillLog_Save(); end function KillLog_RecordPetHitToPlayer(creature) KillLogByName[creature].petHitsToPlayer = KillLogByName[creature].petHitsToPlayer + 1; KillLog_Save(); end function KillLog_RecordPetCritToMob(creature) KillLogByName[creature].petCritsToMob = KillLogByName[creature].petCritsToMob + 1; KillLog_Save(); end function KillLog_RecordPetCritToPlayer(creature) KillLogByName[creature].petCritsToPlayer = KillLogByName[creature].petCritsToPlayer + 1; KillLog_Save(); end function KillLog_RecordPetMissToMob(creature) KillLogByName[creature].petMissesToMob = KillLogByName[creature].petMissesToMob + 1; KillLog_Save(); end function KillLog_RecordPetMissToPlayer(creature) KillLogByName[creature].petMissesToPlayer = KillLogByName[creature].petMissesToPlayer + 1; KillLog_Save(); end function KillLog_RecordPetEvadeToMob(creature) KillLogByName[creature].petEvadesToMob = KillLogByName[creature].petEvadesToMob + 1; KillLog_Save(); end function KillLog_RecordPetEvadeToPlayer(creature) KillLogByName[creature].petEvadesToPlayer = KillLogByName[creature].petEvadesToPlayer + 1; KillLog_Save(); end function KillLog_RecordPetDamageToMob(creature, dmg) KillLogByName[creature].petDamageToMob = KillLogByName[creature].petDamageToMob + dmg; KillLog_Save(); end function KillLog_RecordPetDamageToPlayer(creature, dmg) KillLogByName[creature].petDamageToPlayer = KillLogByName[creature].petDamageToPlayer + dmg; KillLog_Save(); end -- -- Spells, Abilities & Ranged -- function KillLog_RecordSpellHitToMob(creature) KillLogByName[creature].spellHitsToMob = KillLogByName[creature].spellHitsToMob + 1; KillLog_Save(); end function KillLog_RecordSpellHitToPlayer(creature) KillLogByName[creature].spellHitsToPlayer = KillLogByName[creature].spellHitsToPlayer + 1; KillLog_Save(); end function KillLog_RecordSpellCritToMob(creature) KillLogByName[creature].spellCritsToMob = KillLogByName[creature].spellCritsToMob + 1; KillLog_Save(); end function KillLog_RecordSpellCritToPlayer(creature) KillLogByName[creature].spellCritsToPlayer = KillLogByName[creature].spellCritsToPlayer + 1; KillLog_Save(); end function KillLog_RecordSpellMissToMob(creature) KillLogByName[creature].spellMissesToMob = KillLogByName[creature].spellMissesToMob + 1; KillLog_Save(); end function KillLog_RecordSpellMissToPlayer(creature) KillLogByName[creature].spellMissesToPlayer = KillLogByName[creature].spellMissesToPlayer + 1; KillLog_Save(); end function KillLog_RecordSpellEvadeToMob(creature) KillLogByName[creature].spellEvadesToMob = KillLogByName[creature].spellEvadesToMob + 1; KillLog_Save(); end function KillLog_RecordSpellEvadeToPlayer(creature) KillLogByName[creature].spellEvadesToPlayer = KillLogByName[creature].spellEvadesToPlayer + 1; KillLog_Save(); end function KillLog_RecordSpellDamageToMob(creature, dmg) KillLogByName[creature].spellDamageToMob = KillLogByName[creature].spellDamageToMob + dmg; KillLog_Save(); end function KillLog_RecordSpellDamageToPlayer(creature, dmg) KillLogByName[creature].spellDamageToPlayer = KillLogByName[creature].spellDamageToPlayer + dmg; KillLog_Save(); end -- -- Other -- function KillLog_UpdateTargetInfo(creature) level = UnitLevel('target'); if ( UnitIsPlusMob('target') and KillLogByName[creature].isElite == 0 ) then KillLogByName[creature].isElite = 1; end if ( level < KillLogByName[creature].minLevel ) then KillLogByName[creature].minLevel = level; end if ( level > KillLogByName[creature].maxLevel ) then KillLogByName[creature].maxLevel = level; end -- if ((not KillLogByName[creature].portrait) and TargetPortrait) then -- KillLogByName[creature].portrait = TargetPortrait; -- end if KillLogByName[creature].creatureType == 'Unknown' then if ( getglobal(GameTooltip:GetName().."TextLeft1"):GetText() == KillLogByName[creature].name ) then for tt_level, CreatureType in string.gfind(getglobal(GameTooltip:GetName().."TextLeft2"):GetText(), "Level (%d+) (%w+)") do if CreatureType ~= 'Corpse' then KillLogByName[creature].creatureType = CreatureType; end end end end if ( string.lower(KillLogByName[creature].creatureType) == 'ambient' or level < UnitLevel('player') - 5 ) then this.lastTarget = creature; else this.lastTarget = 'Docile Jackalope'; -- Everyone Knows all Jackalopes are ferocious; end end function KillLogClearButton_OnClick() KillLogCVarMax = 0; KillLogVars = { }; KillLogRecentVars = { }; KillLogDisplay = { }; KillLog_Save(); KillLogFrame.combatStats = nil; KillLogClearButton:Disable(); KillLogResetButton:Disable(); KillLog_Update(); end function KillLogResetButton_OnClick() table.foreach(KillLogVars, KillLog_ClearDamage); KillLog_Save(); KillLogFrame.combatStats = nil; KillLogResetButton:Disable(); DefineKillLogDisplay(); KillLog_UpdateDetails(); end function KillLog_ClearDamage(index, value) KillLogVars[index].meleeHitsToMob = 0; KillLogVars[index].meleeHitsToPlayer = 0; KillLogVars[index].meleeCritsToMob = 0; KillLogVars[index].meleeCritsToPlayer = 0; KillLogVars[index].meleeMissesToMob = 0; KillLogVars[index].meleeMissesToPlayer = 0; KillLogVars[index].meleeEvadesToMob = 0; KillLogVars[index].meleeEvadesToPlayer = 0; KillLogVars[index].meleeDamageToMob = 0; KillLogVars[index].meleeDamageToPlayer = 0; KillLogVars[index].petHitsToMob = 0; KillLogVars[index].petHitsToPlayer = 0; KillLogVars[index].petCritsToMob = 0; KillLogVars[index].petCritsToPlayer = 0; KillLogVars[index].petMissesToMob = 0; KillLogVars[index].petMissesToPlayer = 0; KillLogVars[index].petEvadesToMob = 0; KillLogVars[index].petEvadesToPlayer = 0; KillLogVars[index].petDamageToMob = 0; KillLogVars[index].petDamageToPlayer = 0; KillLogVars[index].spellHitsToMob = 0; KillLogVars[index].spellHitsToPlayer = 0; KillLogVars[index].spellCritsToMob = 0; KillLogVars[index].spellCritsToPlayer = 0; KillLogVars[index].spellMissesToMob = 0; KillLogVars[index].spellMissesToPlayer = 0; KillLogVars[index].spellEvadesToMob = 0; KillLogVars[index].spellEvadesToPlayer = 0; KillLogVars[index].spellDamageToMob = 0; KillLogVars[index].spellDamageToPlayer = 0; end function KillLog_Encounter(creature) if ( not KillLogByName[creature] ) then KillLogCVarMax = KillLogCVarMax + 1; KillLogVars[KillLogCVarMax] = { index = KillLogCVarMax, name = creature, kills = 0, deaths = 0, maxLevel = -1, minLevel = 1000, isElite = 0, xpEarned = 0, creatureType = 'Unknown', meleeHitsToMob = 0, meleeHitsToPlayer = 0, meleeCritsToMob = 0, meleeCritsToPlayer = 0, meleeMissesToMob = 0, meleeMissesToPlayer = 0, meleeEvadesToMob = 0, meleeEvadesToPlayer = 0, meleeDamageToMob = 0, meleeDamageToPlayer = 0, zone = 'Twilight', spellHitsToMob = 0, spellHitsToPlayer = 0, spellCritsToMob = 0, spellCritsToPlayer = 0, spellMissesToMob = 0, spellMissesToPlayer = 0, spellEvadesToMob = 0, spellEvadesToPlayer = 0, spellDamageToMob = 0, spellDamageToPlayer = 0, petName = 'Fluffy', petHitsToMob = 0, petHitsToPlayer = 0, petCritsToMob = 0, petCritsToPlayer = 0, petMissesToMob = 0, petMissesToPlayer = 0, petEvadesToMob = 0, petEvadesToPlayer = 0, petDamageToMob = 0, petDamageToPlayer = 0, } KillLogByName[creature] = KillLogVars[KillLogCVarMax]; end if ( UnitName('target') == creature ) then KillLog_UpdateTargetInfo(creature); end -- record recent kill local priorRecent = RECENT_KILLS_DISPLAYED - 1; for i=1, RECENT_KILLS_DISPLAYED do if ( KillLogRecentVars[i] and KillLogRecentVars[i].name == creature ) then priorRecent = i - 1; break; end end for i=priorRecent, 1, -1 do if ( KillLogRecentVars[i] ) then KillLogRecentVars[i+1] = KillLogRecentVars[i]; end end KillLogRecentVars[1] = KillLogByName[creature]; KillLog_Save(); end -- --- ---- ----- Routines for Reading and Writing CVars ---- --- -- function DeathLogInit() RegisterCVar(UnitName('player') .. '_Death_Count'); DeathCount = GetCVar(UnitName('player') .. '_Death_Count') + 0; for i=1, DeathCount do local str = GetCVar(UnitName('player') .. '_Death_' .. i); if ( str == "0" ) then DeathCount = i; break; end for m1, m2, m3, m4 in string.gfind(str, "(%d+)##(%d+),(.+),(%d)") do DeathLog[i] = { atLevel = m1, byMobIndex = m2, time = m3, deathType = m4 } end end end function DeathLogSave() SetCVar(UnitName('player') .. '_Death_Count', DeathCount); for i=1, DeathCount do RegisterCVar(UnitName('player') .. '_Death_' .. i); SetCVar(UnitName('player') .. '_Death_' .. i, DeathLog[i].atLevel .. "##" .. DeathLog[i].byMobIndex .. "," .. DeathLog[i].time .. "," .. DeathLog[i].deathType); end end function KillLogInit() if ( UnitName("player") == '' or UnitName("player") == 'Unknown Being' ) then return nil; else KillLogCVarPrefix = UnitName("player") .. "_KillLog_"; RegisterCVar(KillLogCVarPrefix .. "Count"); RegisterCVar(KillLogCVarPrefix .. "Version"); local version = GetCVar(KillLogCVarPrefix .. 'Version'); KillLogCVarMax = GetCVar(KillLogCVarPrefix .. 'Count') + 0; for i=1, KillLogCVarMax do local str_a = GetCVar(KillLogCVarPrefix .. "a_" .. i); local str_b = GetCVar(KillLogCVarPrefix .. "b_" .. i); local parse_error = 0; if ( str_a == "0" ) then KillLogCVarMax= i; break; end for m1, m2, m3, m4, m5, m6, m7 in string.gfind(str_a, "(.+)##(%d+),(-?%d+),(%d),(%d+),(%d+),(%d+)") do KillLogVars[i] = { index = i, name = m1, minLevel = 0 + m2, maxLevel = 0 + m3, isElite = 0 + m4, kills = 0 + m5, deaths = 0 + m6, xpEarned = 0 + m7 }; parse_error = nil; KillLogByName[KillLogVars[i].name] = KillLogVars[i]; end if parse_error then KillLogCVarMax = i - 1; return 0 end parse_error = 0; for m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11 in string.gfind(str_b, "(.+)##(%d+),(%d+),(%d+),(%d+),(%d+),(%d+),(%d+),(%d+),(%d+),(%d+)") do KillLogVars[i].creatureType = m1; KillLogVars[i].meleeHitsToMob = 0 + m2; KillLogVars[i].meleeHitsToPlayer = 0 + m3; KillLogVars[i].meleeCritsToMob = 0 + m4; KillLogVars[i].meleeCritsToPlayer = 0 + m5; KillLogVars[i].meleeMissesToMob = 0 + m6; KillLogVars[i].meleeMissesToPlayer = 0 + m7; KillLogVars[i].meleeEvadesToMob = 0 + m8; KillLogVars[i].meleeEvadesToPlayer = 0 + m9; KillLogVars[i].meleeDamageToMob = 0 + m10; KillLogVars[i].meleeDamageToPlayer = 0 + m11; parse_error = nil; end if parse_error then KillLogCVarMax = i - 1; return 0 end if version == '1.0' then KillLogVars[i].petName = 'Fluffy'; KillLogVars[i].petHitsToMob = 0; KillLogVars[i].petHitsToPlayer = 0; KillLogVars[i].petCritsToMob = 0; KillLogVars[i].petCritsToPlayer = 0; KillLogVars[i].petMissesToMob = 0; KillLogVars[i].petMissesToPlayer = 0; KillLogVars[i].petEvadesToMob = 0; KillLogVars[i].petEvadesToPlayer = 0; KillLogVars[i].petDamageToMob = 0; KillLogVars[i].petDamageToPlayer = 0; KillLogVars[i].zone = 'Twilight'; KillLogVars[i].spellHitsToMob = 0; KillLogVars[i].spellHitsToPlayer = 0; KillLogVars[i].spellCritsToMob = 0; KillLogVars[i].spellCritsToPlayer = 0; KillLogVars[i].spellMissesToMob = 0; KillLogVars[i].spellMissesToPlayer = 0; KillLogVars[i].spellEvadesToMob = 0; KillLogVars[i].spellEvadesToPlayer = 0; KillLogVars[i].spellDamageToMob = 0; KillLogVars[i].spellDamageToPlayer = 0; if KillLogVars[i].creatureType == 'unknown' then KillLogVars[i].creatureType = 'Unknown'; end else local str_c = GetCVar(KillLogCVarPrefix .. "c_" .. i); local str_d = GetCVar(KillLogCVarPrefix .. "d_" .. i); parse_error = 0; for m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11 in string.gfind(str_c, "(.*)##(%d+),(%d+),(%d+),(%d+),(%d+),(%d+),(%d+),(%d+),(%d+),(%d+)") do KillLogVars[i].zone =m1; KillLogVars[i].spellHitsToMob = 0 + m2; KillLogVars[i].spellHitsToPlayer = 0 + m3; KillLogVars[i].spellCritsToMob = 0 + m4; KillLogVars[i].spellCritsToPlayer = 0 + m5; KillLogVars[i].spellMissesToMob = 0 + m6; KillLogVars[i].spellMissesToPlayer = 0 + m7; KillLogVars[i].spellEvadesToMob = 0 + m8; KillLogVars[i].spellEvadesToPlayer = 0 + m9; KillLogVars[i].spellDamageToMob = 0 + m10; KillLogVars[i].spellDamageToPlayer = 0 + m11; parse_error = nil; end if parse_error then KillLogCVarMax = i - 1; return 0 end parse_error = 0; for m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11 in string.gfind(str_d, "(.*)##(%d+),(%d+),(%d+),(%d+),(%d+),(%d+),(%d+),(%d+),(%d+),(%d+)") do KillLogVars[i].petName = m1; KillLogVars[i].petHitsToMob = 0 + m2; KillLogVars[i].petHitsToPlayer = 0 + m3; KillLogVars[i].petCritsToMob = 0 + m4; KillLogVars[i].petCritsToPlayer = 0 + m5; KillLogVars[i].petMissesToMob = 0 + m6; KillLogVars[i].petMissesToPlayer = 0 + m7; KillLogVars[i].petEvadesToMob = 0 + m8; KillLogVars[i].petEvadesToPlayer = 0 + m9; KillLogVars[i].petDamageToMob = 0 + m10; KillLogVars[i].petDamageToPlayer = 0 + m11; parse_error = nil; end if parse_error then KillLogCVarMax = i - 1; return 0 end end parse_error = 0; end DeathLogInit(); KillLogFrame.initDone = 1; return 1; end end function KillLog_Save() SetCVar(KillLogCVarPrefix .. 'Count', KillLogCVarMax); SetCVar(KillLogCVarPrefix .. 'Version', KILL_LOG_VERSION); for i=1, KillLogCVarMax do RegisterCVar(KillLogCVarPrefix .. 'a_' .. i); SetCVar(KillLogCVarPrefix .. 'a_' .. i, KillLogVars[i].name .. "##" .. KillLogVars[i].minLevel .. "," .. KillLogVars[i].maxLevel .. "," .. KillLogVars[i].isElite .. "," .. KillLogVars[i].kills .. "," .. KillLogVars[i].deaths .. "," .. KillLogVars[i].xpEarned ); RegisterCVar(KillLogCVarPrefix .. 'b_' .. i); SetCVar(KillLogCVarPrefix .. 'b_' .. i, KillLogVars[i].creatureType .. "##" .. KillLogVars[i].meleeHitsToMob .. "," .. KillLogVars[i].meleeHitsToPlayer .. "," .. KillLogVars[i].meleeCritsToMob .. "," .. KillLogVars[i].meleeCritsToPlayer .. "," .. KillLogVars[i].meleeMissesToMob .. "," .. KillLogVars[i].meleeMissesToPlayer .. "," .. KillLogVars[i].meleeEvadesToMob .. "," .. KillLogVars[i].meleeEvadesToPlayer .. "," .. KillLogVars[i].meleeDamageToMob .. "," .. KillLogVars[i].meleeDamageToPlayer ); RegisterCVar(KillLogCVarPrefix .. 'c_' .. i); SetCVar(KillLogCVarPrefix .. 'c_' .. i, KillLogVars[i].zone .. "##" .. KillLogVars[i].spellHitsToMob .. "," .. KillLogVars[i].spellHitsToPlayer .. "," .. KillLogVars[i].spellCritsToMob .. "," .. KillLogVars[i].spellCritsToPlayer .. "," .. KillLogVars[i].spellMissesToMob .. "," .. KillLogVars[i].spellMissesToPlayer .. "," .. KillLogVars[i].spellEvadesToMob .. "," .. KillLogVars[i].spellEvadesToPlayer .. "," .. KillLogVars[i].spellDamageToMob .. "," .. KillLogVars[i].spellDamageToPlayer ); RegisterCVar(KillLogCVarPrefix .. 'd_' .. i); SetCVar(KillLogCVarPrefix .. 'd_' .. i, KillLogVars[i].petName .. "##" .. KillLogVars[i].petHitsToMob .. "," .. KillLogVars[i].petHitsToPlayer .. "," .. KillLogVars[i].petCritsToMob .. "," .. KillLogVars[i].petCritsToPlayer .. "," .. KillLogVars[i].petMissesToMob .. "," .. KillLogVars[i].petMissesToPlayer .. "," .. KillLogVars[i].petEvadesToMob .. "," .. KillLogVars[i].petEvadesToPlayer .. "," .. KillLogVars[i].petDamageToMob .. "," .. KillLogVars[i].petDamageToPlayer ); end DeathLogSave(); end -- --- ---- ----- Compare Routines ---- --- -- function CompareName(a, b) if ( a.name ~= b.name ) then return string.lower(a.name) < string.lower(b.name); end return nil; end function CompareLevel(a, b) if ( a.maxLevel ~= b.maxLevel ) then return a.maxLevel > b.maxLevel; end if ( a.name ~= b.name ) then return string.lower(a.name) < string.lower(b.name); end return nil; end function CompareType(a, b) if ( a.creatureType ~= b.creatureType ) then return string.lower(a.creatureType) < string.lower(b.creatureType); end if ( a.mobDifficulty ~= b.mobDifficulty ) then return KillDifficultyOrder[a.mobDifficulty] < KillDifficultyOrder[b.mobDifficulty]; end if ( a.name ~= b.name ) then return string.lower(a.name) < string.lower(b.name); end return nil; end function CompareKills(a, b) if ( a.kills ~= b.kills ) then return a.kills > b.kills; end if ( a.name ~= b.name ) then return string.lower(a.name) < string.lower(b.name); end return nil; end function CompareDeaths(a, b) if ( a.deaths ~= b.deaths ) then return a.deaths > b.deaths; end if ( a.name ~= b.name ) then return string.lower(a.name) < string.lower(b.name); end return nil; end function CompareXP(a, b) if ( a.xpEarned ~= b.xpEarned ) then return a.xpEarned > b.xpEarned; end if ( a.name ~= b.name ) then return string.lower(a.name) < string.lower(b.name); end return nil; end function CompareDetail(a, b) if ( a.detailSort ~= b.detailSort ) then return a.detailSort > b.detailSort; end if ( a.name ~= b.name ) then return string.lower(a.name) < string.lower(b.name); end return nil; end -- --- ---- ----- Utility Functions (These have nothing to do with KillLog per say) ---- --- -- -- lua's table.sort function has bugs so I wrote my own function QuickSort(array, comp, low, high) -- If not specified sort entire array if (not high or not low) then high = table.getn(array); low = 1; end -- Terminateing Condition if (high == low ) then return; end -- Get Pivot local middle = math.floor((low+high)/2); if comp(array[low], array[middle]) and comp(array[low], array[high]) then if comp(array[middle], array[high]) then array[middle], array[low] = array[low], array[middle]; else array[high], array[low] = array[low], array[high]; end elseif comp(array[high], array[low]) and comp(array[middle], array[low]) then if comp(array[middle], array[high]) then array[high], array[low] = array[low], array[high]; else array[middle], array[low] = array[low], array[middle]; end end -- Pivot in Low now so now we partition local sortedLow = low+1; local sortedHigh = high; while (sortedLow < sortedHigh) do while (comp(array[sortedLow], array[low])) do sortedLow = sortedLow + 1; end while (comp(array[low], array[sortedHigh])) do sortedHigh = sortedHigh - 1; end if (sortedLow < sortedHigh) then array[sortedLow], array[sortedHigh] = array[sortedHigh], array[sortedLow]; end end -- Only 2 values ... swap if necesary if low + 1 == high then if comp(array[high], array[low]) then array[high], array[low] = array[low], array[high]; end pivot = low; elseif sortedLow + 1 == sortedHigh then pivot = sortedLow; else pivot = sortedHigh; end array[pivot], array[low] = array[low], array[pivot]; -- We are all partitioned ... time to recurse if pivot > low then QuickSort(array, comp, low, pivot - 1); end if pivot < high then QuickSort(array, comp, pivot + 1, high); end end