diff --git a/BetterCharacterStats.lua b/BetterCharacterStats.lua
index fb0adee..c1b16ad 100644
--- a/BetterCharacterStats.lua
+++ b/BetterCharacterStats.lua
@@ -1,1016 +1,1512 @@
-BCS = BCS or {}
-BCSConfig = BCSConfig or {}
-
-local L, IndexLeft, IndexRight
-L = BCS.L
-
-BCS.PLAYERSTAT_DROPDOWN_OPTIONS = {
- "PLAYERSTAT_BASE_STATS",
- "PLAYERSTAT_MELEE_COMBAT",
- "PLAYERSTAT_RANGED_COMBAT",
- "PLAYERSTAT_SPELL_COMBAT",
- "PLAYERSTAT_SPELL_SCHOOLS",
- "PLAYERSTAT_DEFENSES",
-}
-
-BCS.MELEEHIT = {
- ["ROGUE"] = {
- 5, -- pvp
- 8, -- yellow cap
- 24.6, -- white cap
- },
-}
-
-BCS.SPELLHIT = {
- -- soon(tm)
-}
-
-BCS.PaperDollFrame = PaperDollFrame
-
-BCS.Debug = false
-BCS.DebugStack = {}
-
-function BCS:DebugTrace(start, limit)
- BCS.Debug = nil
- local length = getn(BCS.DebugStack)
- if not start then start = 1 end
- if start > length then start = length end
- if not limit then limit = start + 30 end
-
- BCS:Print("length: " .. length)
- BCS:Print("start: " .. start)
- BCS:Print("limit: " .. limit)
-
- for i = start, length, 1 do
- BCS:Print("[" .. i .. "] Event: " .. BCS.DebugStack[i].E)
- BCS:Print(format(
- "[%d] `- Arguments: %s, %s, %s, %s, %s",
- i,
- BCS.DebugStack[i].arg1,
- BCS.DebugStack[i].arg2,
- BCS.DebugStack[i].arg3,
- BCS.DebugStack[i].arg4,
- BCS.DebugStack[i].arg5
- ))
- if i >= limit then i = length end
- end
-
-end
-
-function BCS:Print(message)
- ChatFrame2:AddMessage("[BCS] " .. message, 0.63, 0.86, 1.0)
-end
-
-function BCS:OnLoad()
- CharacterAttributesFrame:Hide()
- PaperDollFrame:UnregisterEvent('UNIT_DAMAGE')
- PaperDollFrame:UnregisterEvent('PLAYER_DAMAGE_DONE_MODS')
- PaperDollFrame:UnregisterEvent('UNIT_ATTACK_SPEED')
- PaperDollFrame:UnregisterEvent('UNIT_RANGEDDAMAGE')
- PaperDollFrame:UnregisterEvent('UNIT_ATTACK')
- PaperDollFrame:UnregisterEvent('UNIT_STATS')
- PaperDollFrame:UnregisterEvent('UNIT_ATTACK_POWER')
- PaperDollFrame:UnregisterEvent('UNIT_RANGED_ATTACK_POWER')
-
- self.Frame = BCSFrame
- self.needUpdate = nil
-
- self.Frame:RegisterEvent("ADDON_LOADED")
- self.Frame:RegisterEvent("UNIT_INVENTORY_CHANGED") -- fires when equipment changes
- self.Frame:RegisterEvent("CHARACTER_POINTS_CHANGED") -- fires when learning talent
- self.Frame:RegisterEvent("PLAYER_AURAS_CHANGED") -- buffs/warrior stances
-
- local _, classFileName = UnitClass("Player")
- self.playerClass = strupper(classFileName)
-end
-
-function BCS:OnEvent()
- --[[if BCS.Debug then
- local t = {
- E = event,
- arg1 = arg1 or "nil",
- arg2 = arg2 or "nil",
- arg3 = arg3 or "nil",
- arg4 = arg4 or "nil",
- arg5 = arg5 or "nil",
- }
- tinsert(BCS.DebugStack, t)
- end]]
-
- if
- event == "PLAYER_AURAS_CHANGED" or
- event == "CHARACTER_POINTS_CHANGED"
- then
- if BCS.PaperDollFrame:IsVisible() then
- BCS:UpdateStats()
- else
- BCS.needUpdate = true
- end
- elseif event == "UNIT_INVENTORY_CHANGED" and arg1 == "player" then
- if BCS.PaperDollFrame:IsVisible() then
- BCS:UpdateStats()
- else
- BCS.needUpdate = true
- end
- elseif event == "ADDON_LOADED" and arg1 == "BetterCharacterStats" then
- IndexLeft = BCSConfig["DropdownLeft"] or BCS.PLAYERSTAT_DROPDOWN_OPTIONS[1]
- IndexRight = BCSConfig["DropdownRight"] or BCS.PLAYERSTAT_DROPDOWN_OPTIONS[2]
-
- UIDropDownMenu_SetSelectedValue(PlayerStatFrameLeftDropDown, IndexLeft)
- UIDropDownMenu_SetSelectedValue(PlayerStatFrameRightDropDown, IndexRight)
- end
-end
-
-function BCS:OnShow()
- if BCS.needUpdate then
- BCS.needUpdate = nil
- BCS:UpdateStats()
- end
-end
-
--- debugging / profiling
---local avgV = {}
---local avg = 0
-function BCS:UpdateStats()
- --[[if BCS.Debug then
- local e = event or "nil"
- BCS:Print("Update due to " .. e)
- end
- local beginTime = debugprofilestop()]]
-
- BCS:UpdatePaperdollStats("PlayerStatFrameLeft", IndexLeft)
- BCS:UpdatePaperdollStats("PlayerStatFrameRight", IndexRight)
-
- --[[local timeUsed = debugprofilestop()-beginTime
- table.insert(avgV, timeUsed)
- avg = 0
-
- for i,v in ipairs(avgV) do
- avg = avg + v
- end
- avg = avg / getn(avgV)
-
- BCS:Print(format("Average: %d (%d results), Exact: %d", avg, getn(avgV), timeUsed))]]
-end
-
-function BCS:SetStat(statFrame, statIndex)
- local label = getglobal(statFrame:GetName().."Label")
- local text = getglobal(statFrame:GetName().."StatText")
- local stat
- local effectiveStat
- local posBuff
- local negBuff
- local statIndexTable = {
- "STRENGTH",
- "AGILITY",
- "STAMINA",
- "INTELLECT",
- "SPIRIT",
- }
-
- statFrame:SetScript("OnEnter", function()
- PaperDollStatTooltip("player", statIndexTable[statIndex])
- end)
-
- statFrame:SetScript("OnLeave", function()
- GameTooltip:Hide()
- end)
-
- label:SetText(TEXT(getglobal("SPELL_STAT"..(statIndex-1).."_NAME"))..":")
- stat, effectiveStat, posBuff, negBuff = UnitStat("player", statIndex)
-
- -- Set the tooltip text
- local tooltipText = HIGHLIGHT_FONT_COLOR_CODE..getglobal("SPELL_STAT"..(statIndex-1).."_NAME").." "
-
- if ( ( posBuff == 0 ) and ( negBuff == 0 ) ) then
- text:SetText(effectiveStat)
- statFrame.tooltip = tooltipText..effectiveStat..FONT_COLOR_CODE_CLOSE
- else
- tooltipText = tooltipText..effectiveStat
- if ( posBuff > 0 or negBuff < 0 ) then
- tooltipText = tooltipText.." ("..(stat - posBuff - negBuff)..FONT_COLOR_CODE_CLOSE
- end
- if ( posBuff > 0 ) then
- tooltipText = tooltipText..FONT_COLOR_CODE_CLOSE..GREEN_FONT_COLOR_CODE.."+"..posBuff..FONT_COLOR_CODE_CLOSE
- end
- if ( negBuff < 0 ) then
- tooltipText = tooltipText..RED_FONT_COLOR_CODE.." "..negBuff..FONT_COLOR_CODE_CLOSE
- end
- if ( posBuff > 0 or negBuff < 0 ) then
- tooltipText = tooltipText..HIGHLIGHT_FONT_COLOR_CODE..")"..FONT_COLOR_CODE_CLOSE
- end
- statFrame.tooltip = tooltipText
-
- -- If there are any negative buffs then show the main number in red even if there are
- -- positive buffs. Otherwise show in green.
- if ( negBuff < 0 ) then
- text:SetText(RED_FONT_COLOR_CODE..effectiveStat..FONT_COLOR_CODE_CLOSE)
- else
- text:SetText(GREEN_FONT_COLOR_CODE..effectiveStat..FONT_COLOR_CODE_CLOSE)
- end
- end
-end
-
-function BCS:SetArmor(statFrame)
-
- local base, effectiveArmor, armor, posBuff, negBuff = UnitArmor("player")
- local totalBufs = posBuff + negBuff
- local frame = statFrame
- local label = getglobal(frame:GetName() .. "Label")
- local text = getglobal(frame:GetName() .. "StatText")
-
- PaperDollFormatStat(ARMOR, base, posBuff, negBuff, frame, text)
- label:SetText(TEXT(ARMOR_COLON))
-
- local playerLevel = UnitLevel("player")
- local armorReduction = effectiveArmor/((85 * playerLevel) + 400)
- armorReduction = 100 * (armorReduction/(armorReduction + 1))
-
- frame.tooltipSubtext = format(ARMOR_TOOLTIP, playerLevel, armorReduction)
-
- frame:SetScript("OnEnter", function()
- GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
- GameTooltip:SetText(this.tooltip)
- GameTooltip:AddLine(this.tooltipSubtext, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
- GameTooltip:Show()
- end)
- frame:SetScript("OnLeave", function()
- GameTooltip:Hide()
- end)
-
-end
-
-function BCS:SetDamage(statFrame)
- local label = getglobal(statFrame:GetName() .. "Label")
- label:SetText(TEXT(DAMAGE_COLON))
- local damageText = getglobal(statFrame:GetName() .. "StatText")
- local damageFrame = statFrame
-
- damageFrame:SetScript("OnEnter", CharacterDamageFrame_OnEnter)
- damageFrame:SetScript("OnLeave", function()
- GameTooltip:Hide()
- end)
-
- local speed, offhandSpeed = UnitAttackSpeed("player")
-
- local minDamage
- local maxDamage
- local minOffHandDamage
- local maxOffHandDamage
- local physicalBonusPos
- local physicalBonusNeg
- local percent
- minDamage, maxDamage, minOffHandDamage, maxOffHandDamage, physicalBonusPos, physicalBonusNeg, percent = UnitDamage("player")
- local displayMin = max(floor(minDamage),1)
- local displayMax = max(ceil(maxDamage),1)
-
- minDamage = (minDamage / percent) - physicalBonusPos - physicalBonusNeg
- maxDamage = (maxDamage / percent) - physicalBonusPos - physicalBonusNeg
-
- local baseDamage = (minDamage + maxDamage) * 0.5
- local fullDamage = (baseDamage + physicalBonusPos + physicalBonusNeg) * percent
- local totalBonus = (fullDamage - baseDamage)
- local damagePerSecond = (max(fullDamage,1) / speed)
- local damageTooltip = max(floor(minDamage),1).." - "..max(ceil(maxDamage),1)
-
- local colorPos = "|cff20ff20"
- local colorNeg = "|cffff2020"
- if ( totalBonus == 0 ) then
- if ( ( displayMin < 100 ) and ( displayMax < 100 ) ) then
- damageText:SetText(displayMin.." - "..displayMax)
- else
- damageText:SetText(displayMin.."-"..displayMax)
- end
- else
-
- local color
- if ( totalBonus > 0 ) then
- color = colorPos
- else
- color = colorNeg
- end
- if ( ( displayMin < 100 ) and ( displayMax < 100 ) ) then
- damageText:SetText(color..displayMin.." - "..displayMax.."|r")
- else
- damageText:SetText(color..displayMin.."-"..displayMax.."|r")
- end
- if ( physicalBonusPos > 0 ) then
- damageTooltip = damageTooltip..colorPos.." +"..physicalBonusPos.."|r"
- end
- if ( physicalBonusNeg < 0 ) then
- damageTooltip = damageTooltip..colorNeg.." "..physicalBonusNeg.."|r"
- end
- if ( percent > 1 ) then
- damageTooltip = damageTooltip..colorPos.." x"..floor(percent*100+0.5).."%|r"
- elseif ( percent < 1 ) then
- damageTooltip = damageTooltip..colorNeg.." x"..floor(percent*100+0.5).."%|r"
- end
-
- end
- damageFrame.damage = damageTooltip
- damageFrame.attackSpeed = speed
- damageFrame.dps = damagePerSecond
-
- -- If there's an offhand speed then add the offhand info to the tooltip
- if ( offhandSpeed ) then
- minOffHandDamage = (minOffHandDamage / percent) - physicalBonusPos - physicalBonusNeg
- maxOffHandDamage = (maxOffHandDamage / percent) - physicalBonusPos - physicalBonusNeg
-
- local offhandBaseDamage = (minOffHandDamage + maxOffHandDamage) * 0.5
- local offhandFullDamage = (offhandBaseDamage + physicalBonusPos + physicalBonusNeg) * percent
- local offhandDamagePerSecond = (max(offhandFullDamage,1) / offhandSpeed)
- local offhandDamageTooltip = max(floor(minOffHandDamage),1).." - "..max(ceil(maxOffHandDamage),1)
- if ( physicalBonusPos > 0 ) then
- offhandDamageTooltip = offhandDamageTooltip..colorPos.." +"..physicalBonusPos.."|r"
- end
- if ( physicalBonusNeg < 0 ) then
- offhandDamageTooltip = offhandDamageTooltip..colorNeg.." "..physicalBonusNeg.."|r"
- end
- if ( percent > 1 ) then
- offhandDamageTooltip = offhandDamageTooltip..colorPos.." x"..floor(percent*100+0.5).."%|r"
- elseif ( percent < 1 ) then
- offhandDamageTooltip = offhandDamageTooltip..colorNeg.." x"..floor(percent*100+0.5).."%|r"
- end
- damageFrame.offhandDamage = offhandDamageTooltip
- damageFrame.offhandAttackSpeed = offhandSpeed
- damageFrame.offhandDps = offhandDamagePerSecond
- else
- damageFrame.offhandAttackSpeed = nil
- end
-
-end
-
-function BCS:SetAttackSpeed(statFrame)
- local speed, offhandSpeed = UnitAttackSpeed("player")
- speed = format("%.2f", speed)
- if ( offhandSpeed ) then
- offhandSpeed = format("%.2f", offhandSpeed)
- end
- local text
- if ( offhandSpeed ) then
- text = speed.." / "..offhandSpeed
- else
- text = speed
- end
-
- local label = getglobal(statFrame:GetName() .. "Label")
- local value = getglobal(statFrame:GetName() .. "StatText")
-
- label:SetText(TEXT(SPEED)..":")
- value:SetText(text)
-
- --[[statFrame.tooltip = HIGHLIGHT_FONT_COLOR_CODE..format(PAPERDOLLFRAME_TOOLTIP_FORMAT, ATTACK_SPEED).." "..text..FONT_COLOR_CODE_CLOSE;
- statFrame.tooltip2 = format(CR_HASTE_RATING_TOOLTIP, GetCombatRating(CR_HASTE_MELEE), GetCombatRatingBonus(CR_HASTE_MELEE));]]
-
- statFrame:Show()
-end
-
-function BCS:SetAttackPower(statFrame)
- local base, posBuff, negBuff = UnitAttackPower("player")
-
- local frame = statFrame
- local text = getglobal(statFrame:GetName() .. "StatText")
- local label = getglobal(statFrame:GetName() .. "Label")
-
- label:SetText(TEXT(ATTACK_POWER_COLON))
-
- PaperDollFormatStat(MELEE_ATTACK_POWER, base, posBuff, negBuff, frame, text)
- frame.tooltipSubtext = format(MELEE_ATTACK_POWER_TOOLTIP, max((base+posBuff+negBuff), 0)/ATTACK_POWER_MAGIC_NUMBER)
-end
-
-function BCS:SetSpellPower(statFrame, school)
- local frame = statFrame
- local text = getglobal(statFrame:GetName() .. "StatText")
- local label = getglobal(statFrame:GetName() .. "Label")
-
- local colorPos = "|cff20ff20"
- local colorNeg = "|cffff2020"
-
- if school then
- label:SetText(L["SPELL_SCHOOL_"..strupper(school)])
- local base = BCS:GetSpellPower()
- local fromSchool = BCS:GetSpellPower(school)
- local output = base + fromSchool
-
- if fromSchool > 0 then
- output = colorPos .. output .. "|r"
- end
-
- text:SetText(output)
- else
- local power, secondaryPower, secondaryName = BCS:GetSpellPower()
-
- label:SetText(L.SPELL_POWER_COLON)
- text:SetText(power+secondaryPower)
-
- if secondaryPower > 0 then
- frame.tooltip = format(L.SPELL_POWER_SECONDARY_TOOLTIP, (power+secondaryPower), power, secondaryPower, secondaryName)
-
- frame:SetScript("OnEnter", function()
- GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
- GameTooltip:SetText(this.tooltip)
- GameTooltip:Show()
- end)
- frame:SetScript("OnLeave", function()
- GameTooltip:Hide()
- end)
- end
-
- end
-end
-
-function BCS:SetRating(statFrame, ratingType)
- local frame = statFrame
- local text = getglobal(statFrame:GetName() .. "StatText")
- local label = getglobal(statFrame:GetName() .. "Label")
-
- label:SetText(L.MELEE_HIT_RATING_COLON)
-
- local colorPos = "|cff20ff20"
- local colorNeg = "|cffff2020"
-
- if ratingType == "MELEE" then
- local rating = BCS:GetHitRating()
- if BCS.MELEEHIT[BCS.playerClass] then
- if rating < BCS.MELEEHIT[BCS.playerClass][1] then
- rating = colorNeg .. rating .. "%|r"
- elseif rating >= BCS.MELEEHIT[BCS.playerClass][2] then
- rating = colorPos .. rating .. "%|r"
- else
- rating = rating .. "%"
- end
- else
- rating = rating .. "%"
- end
- text:SetText(rating)
-
- frame.tooltip = L.MELEE_HIT_TOOLTIP
- if L[BCS.playerClass .. "_MELEE_HIT_TOOLTIP"] then
- frame.tooltipSubtext = L[BCS.playerClass .. "_MELEE_HIT_TOOLTIP"]
- end
- elseif ratingType == "RANGED" then
- local rating = BCS:GetRangedHitRating()
- if BCS.MELEEHIT[BCS.playerClass] then
- if rating < BCS.MELEEHIT[BCS.playerClass][1] then
- rating = colorNeg .. rating .. "%|r"
- elseif rating >= BCS.MELEEHIT[BCS.playerClass][2] then
- rating = colorPos .. rating .. "%|r"
- else
- rating = rating .. "%"
- end
- else
- rating = rating .. "%"
- end
- text:SetText(rating)
-
- frame.tooltip = L.MELEE_HIT_TOOLTIP
- if L[BCS.playerClass .. "_MELEE_HIT_TOOLTIP"] then
- frame.tooltipSubtext = L[BCS.playerClass .. "_MELEE_HIT_TOOLTIP"]
- end
- elseif ratingType == "SPELL" then
- local spell_hit, spell_hit_fire, spell_hit_frost, spell_hit_arcane, spell_hit_shadow = BCS:GetSpellHitRating()
- --[[if BCS.SPELLHIT[BCS.playerClass] then
- if spell_hit < BCS.SPELLHIT[BCS.playerClass][1] then
- spell_hit = colorNeg .. spell_hit .. "%|r"
- elseif spell_hit >= BCS.SPELLHIT[BCS.playerClass][2] then
- spell_hit = colorPos .. spell_hit .. "%|r"
- else
- spell_hit = spell_hit .. "%"
- end
- else
- spell_hit = spell_hit .. "%"
- end]]
-
- if spell_hit_fire > 0 or spell_hit_frost > 0 or spell_hit_arcane > 0 or spell_hit_shadow > 0 then
- -- got spell hit from talents
- local spell_hit_other, spell_hit_other_type
-
- spell_hit_other = 0
- spell_hit_other_type = ""
-
- if spell_hit_fire > spell_hit_other then
- spell_hit_other = spell_hit_fire
- spell_hit_other_type = L.SPELL_SCHOOL_FIRE
- end
- if spell_hit_frost > spell_hit_other then
- spell_hit_other = spell_hit_frost
- spell_hit_other_type = L.SPELL_SCHOOL_FROST
- end
- if spell_hit_arcane > spell_hit_other then
- spell_hit_other = spell_hit_arcane
- spell_hit_other_type = L.SPELL_SCHOOL_ARCANE
- end
- if spell_hit_shadow > spell_hit_other then
- spell_hit_other = spell_hit_shadow
- spell_hit_other_type = L.SPELL_SCHOOL_SHADOW
- end
-
- frame.tooltip = format(L.SPELL_HIT_SECONDARY_TOOLTIP, spell_hit+spell_hit_other, spell_hit, spell_hit_other, spell_hit_other_type)
- text:SetText(spell_hit+spell_hit_other.."%")
- else
- frame.tooltip = L.SPELL_HIT_TOOLTIP
- text:SetText(spell_hit.."%")
- end
-
- -- class specific tooltip
- if L[BCS.playerClass .. "_SPELL_HIT_TOOLTIP"] then
- frame.tooltipSubtext = L[BCS.playerClass .. "_SPELL_HIT_TOOLTIP"]
- end
- end
-
- if frame.tooltip then
- frame:SetScript("OnEnter", function()
- GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
- GameTooltip:SetText(this.tooltip)
- GameTooltip:AddLine(this.tooltipSubtext, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
- GameTooltip:Show()
- end)
- frame:SetScript("OnLeave", function()
- GameTooltip:Hide()
- end)
- end
-
-end
-
-function BCS:SetMeleeCritChance(statFrame)
- local frame = statFrame
- local text = getglobal(statFrame:GetName() .. "StatText")
- local label = getglobal(statFrame:GetName() .. "Label")
-
- label:SetText(L.MELEE_CRIT_COLON)
- text:SetText(format("%.2f%%", BCS:GetCritChance()))
-end
-
-function BCS:SetSpellCritChance(statFrame)
- local frame = statFrame
- local text = getglobal(statFrame:GetName() .. "StatText")
- local label = getglobal(statFrame:GetName() .. "Label")
-
- label:SetText(L.SPELL_CRIT_COLON)
- text:SetText(format("%.2f%%", BCS:GetSpellCritChance()))
-end
-
-function BCS:SetRangedCritChance(statFrame)
- local frame = statFrame
- local text = getglobal(statFrame:GetName() .. "StatText")
- local label = getglobal(statFrame:GetName() .. "Label")
-
- label:SetText(L.RANGED_CRIT_COLON)
- text:SetText(format("%.2f%%", BCS:GetRangedCritChance()))
-end
-
-function BCS:SetHealing(statFrame)
- local frame = statFrame
- local text = getglobal(statFrame:GetName() .. "StatText")
- local label = getglobal(statFrame:GetName() .. "Label")
-
- local power,_,_,dmg = BCS:GetSpellPower()
- local heal = BCS:GetHealingPower()
-
- power = power-dmg
-
- label:SetText(L.HEAL_POWER_COLON)
- text:SetText(power+heal)
-
- frame.tooltip = format(L.SPELL_HEALING_POWER_TOOLTIP, (power+heal), power, heal)
-
- frame:SetScript("OnEnter", function()
- GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
- GameTooltip:SetText(this.tooltip)
- GameTooltip:Show()
- end)
- frame:SetScript("OnLeave", function()
- GameTooltip:Hide()
- end)
-end
-
-function BCS:SetManaRegen(statFrame)
- local frame = statFrame
- local text = getglobal(statFrame:GetName() .. "StatText")
- local label = getglobal(statFrame:GetName() .. "Label")
-
- local base, casting, mp5 = BCS:GetManaRegen()
- local mp2 = mp5*0.4
- local totalRegen = base + mp2
- local totalRegenWhileCasting = (casting/100)*base + mp2
-
- label:SetText(L.MANA_REGEN_COLON)
- text:SetText(format("%d |cffBF40BF(%d)|r", totalRegen, totalRegenWhileCasting))
-
- frame.tooltip = format(L.SPELL_MANA_REGEN_TOOLTIP, totalRegen, totalRegenWhileCasting, base, casting, mp5, mp2)
-
- frame:SetScript("OnEnter", function()
- GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
- GameTooltip:SetText(this.tooltip)
- GameTooltip:Show()
- end)
- frame:SetScript("OnLeave", function()
- GameTooltip:Hide()
- end)
-end
-
-function BCS:SetDodge(statFrame)
- local frame = statFrame
- local text = getglobal(statFrame:GetName() .. "StatText")
- local label = getglobal(statFrame:GetName() .. "Label")
-
- label:SetText(L.DODGE_COLON)
- text:SetText(format("%.2f%%", GetDodgeChance()))
-end
-
-function BCS:SetParry(statFrame)
- local frame = statFrame
- local text = getglobal(statFrame:GetName() .. "StatText")
- local label = getglobal(statFrame:GetName() .. "Label")
-
- label:SetText(L.PARRY_COLON)
- text:SetText(format("%.2f%%", GetParryChance()))
-end
-
-function BCS:SetBlock(statFrame)
- local frame = statFrame
- local text = getglobal(statFrame:GetName() .. "StatText")
- local label = getglobal(statFrame:GetName() .. "Label")
-
- label:SetText(L.BLOCK_COLON)
- text:SetText(format("%.2f%%", GetBlockChance()))
-end
-
-function BCS:SetDefense(statFrame)
- local base, modifier = UnitDefense("player")
-
- local frame = statFrame
- local label = getglobal(statFrame:GetName() .. "Label")
- local text = getglobal(statFrame:GetName() .. "StatText")
-
- label:SetText(TEXT(DEFENSE_COLON))
-
- local posBuff = 0
- local negBuff = 0
- if ( modifier > 0 ) then
- posBuff = modifier
- elseif ( modifier < 0 ) then
- negBuff = modifier
- end
- PaperDollFormatStat(DEFENSE_COLON, base, posBuff, negBuff, frame, text)
-end
-
-function BCS:SetRangedDamage(statFrame)
- local label = getglobal(statFrame:GetName() .. "Label")
- local damageText = getglobal(statFrame:GetName() .. "StatText")
- local damageFrame = statFrame
-
- label:SetText(TEXT(DAMAGE_COLON))
-
- damageFrame:SetScript("OnEnter", CharacterRangedDamageFrame_OnEnter)
- damageFrame:SetScript("OnLeave", function()
- GameTooltip:Hide()
- end)
-
- -- If no ranged attack then set to n/a
- if ( PaperDollFrame.noRanged ) then
- damageText:SetText(NOT_APPLICABLE)
- damageFrame.damage = nil
- return
- end
-
- local rangedAttackSpeed, minDamage, maxDamage, physicalBonusPos, physicalBonusNeg, percent = UnitRangedDamage("player")
- local displayMin = max(floor(minDamage),1)
- local displayMax = max(ceil(maxDamage),1)
-
- minDamage = (minDamage / percent) - physicalBonusPos - physicalBonusNeg
- maxDamage = (maxDamage / percent) - physicalBonusPos - physicalBonusNeg
-
- local baseDamage = (minDamage + maxDamage) * 0.5
- local fullDamage = (baseDamage + physicalBonusPos + physicalBonusNeg) * percent
- local totalBonus = (fullDamage - baseDamage)
- local damagePerSecond = (max(fullDamage,1) / rangedAttackSpeed)
- local tooltip = max(floor(minDamage),1).." - "..max(ceil(maxDamage),1)
-
- if ( totalBonus == 0 ) then
- if ( ( displayMin < 100 ) and ( displayMax < 100 ) ) then
- damageText:SetText(displayMin.." - "..displayMax)
- else
- damageText:SetText(displayMin.."-"..displayMax)
- end
- else
- local colorPos = "|cff20ff20"
- local colorNeg = "|cffff2020"
- local color
- if ( totalBonus > 0 ) then
- color = colorPos
- else
- color = colorNeg
- end
- if ( ( displayMin < 100 ) and ( displayMax < 100 ) ) then
- damageText:SetText(color..displayMin.." - "..displayMax.."|r")
- else
- damageText:SetText(color..displayMin.."-"..displayMax.."|r")
- end
- if ( physicalBonusPos > 0 ) then
- tooltip = tooltip..colorPos.." +"..physicalBonusPos.."|r"
- end
- if ( physicalBonusNeg < 0 ) then
- tooltip = tooltip..colorNeg.." "..physicalBonusNeg.."|r"
- end
- if ( percent > 1 ) then
- tooltip = tooltip..colorPos.." x"..floor(percent*100+0.5).."%|r"
- elseif ( percent < 1 ) then
- tooltip = tooltip..colorNeg.." x"..floor(percent*100+0.5).."%|r"
- end
- damageFrame.tooltip = tooltip.." "..format(TEXT(DPS_TEMPLATE), damagePerSecond)
- end
- damageFrame.attackSpeed = rangedAttackSpeed
- damageFrame.damage = tooltip
- damageFrame.dps = damagePerSecond
-end
-
-function BCS:SetRangedAttackSpeed(startFrame)
- local label = getglobal(startFrame:GetName() .. "Label")
- local damageText = getglobal(startFrame:GetName() .. "StatText")
- local damageFrame = startFrame
-
- label:SetText(TEXT(SPEED)..":")
-
- -- If no ranged attack then set to n/a
- if ( PaperDollFrame.noRanged ) then
- damageText:SetText(NOT_APPLICABLE)
- damageFrame.damage = nil
- return
- end
-
- local rangedAttackSpeed, minDamage, maxDamage, physicalBonusPos, physicalBonusNeg, percent = UnitRangedDamage("player")
- local displayMin = max(floor(minDamage),1)
- local displayMax = max(ceil(maxDamage),1)
-
- minDamage = (minDamage / percent) - physicalBonusPos - physicalBonusNeg
- maxDamage = (maxDamage / percent) - physicalBonusPos - physicalBonusNeg
-
- local baseDamage = (minDamage + maxDamage) * 0.5
- local fullDamage = (baseDamage + physicalBonusPos + physicalBonusNeg) * percent
- local totalBonus = (fullDamage - baseDamage)
- local damagePerSecond = (max(fullDamage,1) / rangedAttackSpeed)
- local tooltip = max(floor(minDamage),1).." - "..max(ceil(maxDamage),1)
-
- if ( totalBonus == 0 ) then
- if ( ( displayMin < 100 ) and ( displayMax < 100 ) ) then
- damageText:SetText(displayMin.." - "..displayMax)
- else
- damageText:SetText(displayMin.."-"..displayMax)
- end
- else
- local colorPos = "|cff20ff20"
- local colorNeg = "|cffff2020"
- local color
- if ( totalBonus > 0 ) then
- color = colorPos
- else
- color = colorNeg
- end
- if ( ( displayMin < 100 ) and ( displayMax < 100 ) ) then
- damageText:SetText(color..displayMin.." - "..displayMax.."|r")
- else
- damageText:SetText(color..displayMin.."-"..displayMax.."|r")
- end
- if ( physicalBonusPos > 0 ) then
- tooltip = tooltip..colorPos.." +"..physicalBonusPos.."|r"
- end
- if ( physicalBonusNeg < 0 ) then
- tooltip = tooltip..colorNeg.." "..physicalBonusNeg.."|r"
- end
- if ( percent > 1 ) then
- tooltip = tooltip..colorPos.." x"..floor(percent*100+0.5).."%|r"
- elseif ( percent < 1 ) then
- tooltip = tooltip..colorNeg.." x"..floor(percent*100+0.5).."%|r"
- end
- damageFrame.tooltip = tooltip.." "..format(TEXT(DPS_TEMPLATE), damagePerSecond)
- end
-
- damageText:SetText(format("%.2f", rangedAttackSpeed))
-
- damageFrame.attackSpeed = rangedAttackSpeed
- damageFrame.damage = tooltip
- damageFrame.dps = damagePerSecond
-end
-
-function BCS:SetRangedAttackPower(statFrame)
- local frame = statFrame
- local text = getglobal(statFrame:GetName() .. "StatText")
- local label = getglobal(statFrame:GetName() .. "Label")
-
- label:SetText(TEXT(ATTACK_POWER_COLON))
-
- -- If no ranged attack then set to n/a
- if ( PaperDollFrame.noRanged ) then
- text:SetText(NOT_APPLICABLE)
- frame.tooltip = nil
- return
- end
- if ( HasWandEquipped() ) then
- text:SetText("--")
- frame.tooltip = nil
- return
- end
-
- local base, posBuff, negBuff = UnitRangedAttackPower("player")
- PaperDollFormatStat(RANGED_ATTACK_POWER, base, posBuff, negBuff, frame, text)
- frame.tooltipSubtext = format(RANGED_ATTACK_POWER_TOOLTIP, base/ATTACK_POWER_MAGIC_NUMBER)
-end
-
-function BCS:UpdatePaperdollStats(prefix, index)
- local stat1 = getglobal(prefix..1)
- local stat2 = getglobal(prefix..2)
- local stat3 = getglobal(prefix..3)
- local stat4 = getglobal(prefix..4)
- local stat5 = getglobal(prefix..5)
- local stat6 = getglobal(prefix..6)
-
- stat1:SetScript("OnEnter", nil)
- stat2:SetScript("OnEnter", nil)
- stat3:SetScript("OnEnter", nil)
- stat4:SetScript("OnEnter", nil)
- stat4:SetScript("OnEnter", nil)
- stat5:SetScript("OnEnter", nil)
- stat6:SetScript("OnEnter", nil)
-
- stat1.tooltip = nil
- stat2.tooltip = nil
- stat3.tooltip = nil
- stat4.tooltip = nil
- stat4.tooltip = nil
- stat5.tooltip = nil
- stat6.tooltip = nil
-
- stat4:Show()
- stat5:Show()
- stat6:Show()
-
- if ( index == "PLAYERSTAT_BASE_STATS" ) then
- BCS:SetStat(stat1, 1)
- BCS:SetStat(stat2, 2)
- BCS:SetStat(stat3, 3)
- BCS:SetStat(stat4, 4)
- BCS:SetStat(stat5, 5)
- BCS:SetArmor(stat6)
- elseif ( index == "PLAYERSTAT_MELEE_COMBAT" ) then
- BCS:SetDamage(stat1)
- BCS:SetAttackSpeed(stat2)
- BCS:SetAttackPower(stat3)
- BCS:SetRating(stat4, "MELEE")
- BCS:SetMeleeCritChance(stat5)
- stat6:Hide()
- elseif ( index == "PLAYERSTAT_RANGED_COMBAT" ) then
- BCS:SetRangedDamage(stat1)
- BCS:SetRangedAttackSpeed(stat2)
- BCS:SetRangedAttackPower(stat3)
- BCS:SetRating(stat4, "RANGED")
- BCS:SetRangedCritChance(stat5)
- stat6:Hide()
- elseif ( index == "PLAYERSTAT_SPELL_COMBAT" ) then
- BCS:SetSpellPower(stat1)
- BCS:SetRating(stat2, "SPELL")
- BCS:SetSpellCritChance(stat3)
- BCS:SetHealing(stat4)
- BCS:SetManaRegen(stat5)
- stat6:Hide()
- elseif ( index == "PLAYERSTAT_SPELL_SCHOOLS" ) then
- BCS:SetSpellPower(stat1, "Arcane")
- BCS:SetSpellPower(stat2, "Fire")
- BCS:SetSpellPower(stat3, "Frost")
- BCS:SetSpellPower(stat4, "Holy")
- BCS:SetSpellPower(stat5, "Nature")
- BCS:SetSpellPower(stat6, "Shadow")
- elseif ( index == "PLAYERSTAT_DEFENSES" ) then
- BCS:SetArmor(stat1)
- BCS:SetDefense(stat2)
- BCS:SetDodge(stat3)
- BCS:SetParry(stat4)
- BCS:SetBlock(stat5)
- stat6:Hide()
- end
-end
-
-local function PlayerStatFrameLeftDropDown_OnClick()
- UIDropDownMenu_SetSelectedValue(getglobal(this.owner), this.value)
- IndexLeft = this.value
- BCSConfig["DropdownLeft"] = IndexLeft
- BCS:UpdatePaperdollStats("PlayerStatFrameLeft", this.value)
-end
-
-local function PlayerStatFrameRightDropDown_OnClick()
- UIDropDownMenu_SetSelectedValue(getglobal(this.owner), this.value)
- IndexRight = this.value
- BCSConfig["DropdownRight"] = IndexRight
- BCS:UpdatePaperdollStats("PlayerStatFrameRight", this.value)
-end
-
-local function PlayerStatFrameLeftDropDown_Initialize()
- local info = {}
- local checked = nil
- for i=1, getn(BCS.PLAYERSTAT_DROPDOWN_OPTIONS) do
- info.text = BCS.L[BCS.PLAYERSTAT_DROPDOWN_OPTIONS[i]]
- info.func = PlayerStatFrameLeftDropDown_OnClick
- info.value = BCS.PLAYERSTAT_DROPDOWN_OPTIONS[i]
- info.checked = checked
- info.owner = UIDROPDOWNMENU_OPEN_MENU
- UIDropDownMenu_AddButton(info)
- end
-end
-
-local function PlayerStatFrameRightDropDown_Initialize()
- local info = {}
- local checked = nil
- for i=1, getn(BCS.PLAYERSTAT_DROPDOWN_OPTIONS) do
- info.text = BCS.L[BCS.PLAYERSTAT_DROPDOWN_OPTIONS[i]]
- info.func = PlayerStatFrameRightDropDown_OnClick
- info.value = BCS.PLAYERSTAT_DROPDOWN_OPTIONS[i]
- info.checked = checked
- info.owner = UIDROPDOWNMENU_OPEN_MENU
- UIDropDownMenu_AddButton(info)
- end
-end
-
-function PlayerStatFrameLeftDropDown_OnLoad()
- RaiseFrameLevel(this)
- RaiseFrameLevel(getglobal(this:GetName() .. "Button"))
- UIDropDownMenu_Initialize(this, PlayerStatFrameLeftDropDown_Initialize)
- UIDropDownMenu_SetWidth(99, this)
- UIDropDownMenu_JustifyText("LEFT")
-end
-
-function PlayerStatFrameRightDropDown_OnLoad()
- RaiseFrameLevel(this)
- RaiseFrameLevel(getglobal(this:GetName() .. "Button"))
- UIDropDownMenu_Initialize(this, PlayerStatFrameRightDropDown_Initialize)
- UIDropDownMenu_SetWidth(99, this)
- UIDropDownMenu_JustifyText("LEFT")
-end
-
---pfUI.api.strsplit
-function hcstrsplit(delimiter, subject)
- if not subject then return nil end
- local delimiter, fields = delimiter or ":", {}
- local pattern = string.format("([^%s]+)", delimiter)
- string.gsub(subject, pattern, function(c) fields[table.getn(fields)+1] = c end)
- return unpack(fields)
-end
-
---Update announcing code taken from pfUI
-local major, minor, fix = hcstrsplit(".", tostring(GetAddOnMetadata("BetterCharacterStats", "Version")))
-
-local alreadyshown = false
-local localversion = tonumber(major*10000 + minor*100 + fix)
-local remoteversion = tonumber(bcsupdateavailable) or 0
-local loginchannels = { "BATTLEGROUND", "RAID", "GUILD", "PARTY" }
-local groupchannels = { "BATTLEGROUND", "RAID", "PARTY" }
-
-bcsupdater = CreateFrame("Frame")
-bcsupdater:RegisterEvent("CHAT_MSG_ADDON")
-bcsupdater:RegisterEvent("PLAYER_ENTERING_WORLD")
-bcsupdater:RegisterEvent("PARTY_MEMBERS_CHANGED")
-bcsupdater:SetScript("OnEvent", function()
- if event == "CHAT_MSG_ADDON" and arg1 == "bcs" then
- local v, remoteversion = hcstrsplit(":", arg2)
- local remoteversion = tonumber(remoteversion)
- if v == "VERSION" and remoteversion then
- if remoteversion > localversion then
- bcsupdateavailable = remoteversion
- if not alreadyshown then
- DEFAULT_CHAT_FRAME:AddMessage("|cffffffffBetterCharacterStats|r New version available! https://github.com/Lexiebean/BetterCharacterStats")
- alreadyshown = true
- end
- end
- end
- --This is a little check that I can use to see if people are actually using the addon.
- if v == "PING?" then
- for _, chan in pairs(loginchannels) do
- SendAddonMessage("bcs", "PONG!:"..GetAddOnMetadata("BetterCharacterStats", "Version"), chan)
- end
- end
- if v == "PONG!" then
- --print(arg1 .." "..arg2.." "..arg3.." "..arg4)
- end
- end
-
- if event == "PARTY_MEMBERS_CHANGED" then
- local groupsize = GetNumRaidMembers() > 0 and GetNumRaidMembers() or GetNumPartyMembers() > 0 and GetNumPartyMembers() or 0
- if ( this.group or 0 ) < groupsize then
- for _, chan in pairs(groupchannels) do
- SendAddonMessage("bcs", "VERSION:" .. localversion, chan)
- end
- end
- this.group = groupsize
- end
-
- if event == "PLAYER_ENTERING_WORLD" then
- if not alreadyshown and localversion < remoteversion then
- DEFAULT_CHAT_FRAME:AddMessage("|cffffffffBetterCharacterStats|r New version available! https://github.com/Lexiebean/BetterCharacterStats")
- bcsupdateavailable = localversion
- alreadyshown = true
- end
-
- for _, chan in pairs(loginchannels) do
- SendAddonMessage("bcs", "VERSION:" .. localversion, chan)
- end
- end
- end)
\ No newline at end of file
+BCS = BCS or {}
+BCSConfig = BCSConfig or {}
+
+local L, IndexLeft, IndexRight
+L = BCS.L
+
+local AceEvent = AceLibrary:HasInstance("AceEvent-2.0") and AceLibrary("AceEvent-2.0")
+
+-- Tree of Life aura bonus from other players, your own is calculated in GetHealingPower()
+local aura = .0
+
+BCS.PLAYERSTAT_DROPDOWN_OPTIONS = {
+ "PLAYERSTAT_BASE_STATS",
+ "PLAYERSTAT_MELEE_COMBAT",
+ "PLAYERSTAT_MELEE_BOSS",
+ "PLAYERSTAT_RANGED_COMBAT",
+ "PLAYERSTAT_SPELL_COMBAT",
+ "PLAYERSTAT_SPELL_SCHOOLS",
+ "PLAYERSTAT_DEFENSES",
+ "PLAYERSTAT_DEFENSES_BOSS",
+}
+
+BCS.PaperDollFrame = PaperDollFrame
+
+BCS.Debug = false
+BCS.DebugStack = {}
+
+function BCS:DebugTrace(start, limit)
+ BCS.Debug = nil
+ local length = getn(BCS.DebugStack)
+ if not start then
+ start = 1
+ end
+ if start > length then
+ start = length
+ end
+ if not limit then
+ limit = start + 30
+ end
+
+ BCS:Print("length: " .. length)
+ BCS:Print("start: " .. start)
+ BCS:Print("limit: " .. limit)
+
+ for i = start, length, 1 do
+ BCS:Print("[" .. i .. "] Event: " .. BCS.DebugStack[i].E)
+ BCS:Print(format(
+ "[%d] `- Arguments: %s, %s, %s, %s, %s",
+ i,
+ BCS.DebugStack[i].arg1,
+ BCS.DebugStack[i].arg2,
+ BCS.DebugStack[i].arg3,
+ BCS.DebugStack[i].arg4,
+ BCS.DebugStack[i].arg5
+ ))
+ if i >= limit then
+ i = length
+ end
+ end
+
+end
+
+function BCS:Print(message)
+ ChatFrame2:AddMessage("[BCS] " .. message, 0.63, 0.86, 1.0)
+end
+
+function BCS:OnLoad()
+ CharacterAttributesFrame:Hide()
+ PaperDollFrame:UnregisterEvent('UNIT_DAMAGE')
+ PaperDollFrame:UnregisterEvent('PLAYER_DAMAGE_DONE_MODS')
+ PaperDollFrame:UnregisterEvent('UNIT_ATTACK_SPEED')
+ PaperDollFrame:UnregisterEvent('UNIT_RANGEDDAMAGE')
+ PaperDollFrame:UnregisterEvent('UNIT_ATTACK')
+ PaperDollFrame:UnregisterEvent('UNIT_STATS')
+ PaperDollFrame:UnregisterEvent('UNIT_ATTACK_POWER')
+ PaperDollFrame:UnregisterEvent('UNIT_RANGED_ATTACK_POWER')
+
+ self.Frame = BCSFrame
+ self.needUpdate = nil
+
+ self.Frame:RegisterEvent("ADDON_LOADED")
+ self.Frame:RegisterEvent("CHARACTER_POINTS_CHANGED") -- fires when learning talent
+ self.Frame:RegisterEvent("PLAYER_AURAS_CHANGED") -- buffs/warrior stances
+ self.Frame:RegisterEvent("CHAT_MSG_SKILL") --gaining weapon skill
+ self.Frame:RegisterEvent("CHAT_MSG_ADDON") --needed to recieve aura bonuses from other people
+ AceEvent:RegisterBucketEvent("UNIT_INVENTORY_CHANGED", 0.3, function(args)
+ if args["player"] then
+ BCS.needScanGear = true
+ BCS.needScanSkills = true
+ if BCS.PaperDollFrame:IsVisible() then
+ BCS:UpdateStats()
+ else
+ BCS.needUpdate = true
+ end
+ end
+ end)
+end
+
+local function PostHookFunction(original, hook)
+ return function(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
+ original(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
+ hook(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
+ end
+end
+
+-- there is less space for player character model with this addon, zoom out and move it up slightly
+local z, x, y = -0.2, 0, 0.1
+function BCS_PaperDollFrame_OnEvent(event, unit)
+ if (event == "PLAYER_ENTERING_WORLD") then
+ CharacterModelFrame:SetPosition(0, 0, 0)
+ CharacterModelFrame:SetUnit("player")
+ CharacterModelFrame:SetPosition(z, x, y)
+ return
+ end
+ if (unit and unit == "player") then
+ if (event == "UNIT_MODEL_CHANGED") then
+ CharacterModelFrame:SetPosition(0, 0, 0)
+ CharacterModelFrame:SetUnit("player")
+ CharacterModelFrame:SetPosition(z, x, y)
+ return
+ end
+ end
+end
+
+function BCS_PaperDollFrame_OnShow()
+ CharacterModelFrame:SetPosition(0, 0, 0)
+ CharacterModelFrame:SetUnit("player")
+ CharacterModelFrame:SetPosition(z, x, y)
+end
+
+PaperDollFrame_OnShow = PostHookFunction(PaperDollFrame_OnShow, BCS_PaperDollFrame_OnShow)
+PaperDollFrame_OnEvent = PostHookFunction(PaperDollFrame_OnEvent, BCS_PaperDollFrame_OnEvent)
+
+local function strsplit(delimiter, subject)
+ if not subject then
+ return nil
+ end
+ local delimiter, fields = delimiter or ":", {}
+ local pattern = string.format("([^%s]+)", delimiter)
+ string.gsub(subject, pattern, function(c)
+ fields[table.getn(fields) + 1] = c
+ end)
+ return unpack(fields)
+end
+
+-- Scan stuff depending on event, but make sure to scan everything when addon is loaded
+function BCS:OnEvent()
+ --[[if BCS.Debug then
+ local t = {
+ E = event,
+ arg1 = arg1 or "nil",
+ arg2 = arg2 or "nil",
+ arg3 = arg3 or "nil",
+ arg4 = arg4 or "nil",
+ arg5 = arg5 or "nil",
+ }
+ tinsert(BCS.DebugStack, t)
+ end]]
+ if event == "CHAT_MSG_ADDON" and arg1 == "bcs" then
+ BCS.needScanAuras = true
+ local type, player, amount = strsplit(",", arg2)
+ if type and player and amount then
+ if player ~= UnitName("player") then
+ amount = tonumber(amount)
+ if type == "TREE" then
+ --BCS:Print("got tree response amount="..amount)
+ if amount >= aura then
+ aura = amount
+ if BCS.PaperDollFrame:IsVisible() then
+ BCS:UpdateStats()
+ else
+ BCS.needUpdate = true
+ end
+ end
+ end
+ end
+ end
+ elseif event == "PLAYER_AURAS_CHANGED" then
+ BCS.needScanAuras = true
+ if not BCS:GetPlayerAura("Tree of Life Aura") then
+ aura = 0
+ end
+ if BCS.PaperDollFrame:IsVisible() then
+ BCS:UpdateStats()
+ else
+ BCS.needUpdate = true
+ end
+ elseif event == "CHARACTER_POINTS_CHANGED" then
+ BCS.needScanTalents = true
+ if BCS.PaperDollFrame:IsVisible() then
+ BCS:UpdateStats()
+ else
+ BCS.needUpdate = true
+ end
+ elseif event == "CHAT_MSG_SKILL" then
+ BCS.needScanSkills = true
+ if BCS.PaperDollFrame:IsVisible() then
+ BCS:UpdateStats()
+ else
+ BCS.needUpdate = true
+ end
+ elseif event == "ADDON_LOADED" and arg1 == "BetterCharacterStats" then
+ BCSFrame:UnregisterEvent("ADDON_LOADED")
+
+ local _, race = UnitRace("player")
+ if race == "Gnome" then
+ y = 0
+ elseif race == "Dwarf" then
+ y = 0.05
+ elseif race == "Troll" then
+ y = 0.15
+ end
+
+ BCS.needScanGear = true
+ BCS.needScanTalents = true
+ BCS.needScanAuras = true
+ BCS.needScanSkills = true
+
+ IndexLeft = BCSConfig["DropdownLeft"] or BCS.PLAYERSTAT_DROPDOWN_OPTIONS[1]
+ IndexRight = BCSConfig["DropdownRight"] or BCS.PLAYERSTAT_DROPDOWN_OPTIONS[2]
+
+ UIDropDownMenu_SetSelectedValue(PlayerStatFrameLeftDropDown, IndexLeft)
+ UIDropDownMenu_SetSelectedValue(PlayerStatFrameRightDropDown, IndexRight)
+ end
+end
+
+--sending messages
+local sender = CreateFrame("Frame", "BCSsender")
+sender:RegisterEvent("PLAYER_AURAS_CHANGED")
+sender:RegisterEvent("CHAT_MSG_ADDON")
+sender:SetScript("OnEvent", function()
+ if not (UnitInParty("player") or UnitInRaid("player")) then
+ return
+ end
+ if event then
+ local player = UnitName("player")
+ if event == "PLAYER_AURAS_CHANGED" then
+ if BCS:GetPlayerAura("Tree of Life Aura") then
+ SendAddonMessage("bcs", "TREE"..","..player, "PARTY")
+ --BCS:Print("sent tree request")
+ end
+ end
+ if event == "CHAT_MSG_ADDON" and arg1 == "bcs" then
+ local type, name, amount = strsplit(",", arg2)
+ if name ~= player then
+ local _, treebonus = BCS:GetHealingPower()
+ if not amount and type == "TREE" and treebonus then
+ SendAddonMessage("bcs", "TREE"..","..player..","..treebonus, "PARTY")
+ --BCS:Print("sent tree response, amount="..treebonus)
+ end
+ end
+ end
+ end
+end)
+
+function BCS:OnShow()
+ if BCS.needUpdate then
+ BCS.needUpdate = nil
+ BCS:UpdateStats()
+ end
+end
+
+-- debugging / profiling
+--local avgV = {}
+--local avg = 0
+function BCS:UpdateStats()
+ --[[if BCS.Debug then
+ local e = event or "nil"
+ BCS:Print("Update due to " .. e)
+ end
+ local beginTime = debugprofilestop()]]
+
+ BCS:UpdatePaperdollStats("PlayerStatFrameLeft", IndexLeft)
+ BCS:UpdatePaperdollStats("PlayerStatFrameRight", IndexRight)
+ BCS.needScanGear = false
+ BCS.needScanTalents = false
+ BCS.needScanAuras = false
+ BCS.needScanSkills = false
+ --[[local timeUsed = debugprofilestop()-beginTime
+ table.insert(avgV, timeUsed)
+ avg = 0
+
+ for i,v in ipairs(avgV) do
+ avg = avg + v
+ end
+ avg = avg / getn(avgV)
+
+ BCS:Print(format("Average: %d (%d results), Exact: %d", avg, getn(avgV), timeUsed))]]
+end
+
+local function BCS_AddTooltip(statFrame, tooltipExtra)
+ if statFrame.tooltip then
+ statFrame:SetScript("OnEnter", function()
+ GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
+ GameTooltip:SetText(this.tooltip)
+ GameTooltip:AddLine(this.tooltipSubtext, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
+ if tooltipExtra then
+ GameTooltip:AddLine(tooltipExtra)
+ end
+ GameTooltip:Show()
+ end)
+ statFrame:SetScript("OnLeave", function()
+ GameTooltip:Hide()
+ end)
+ end
+end
+
+local function BCS_AddDamageTooltip(damageText, statFrame, speed, offhandSpeed, ranged)
+ local rangedAttackSpeed, minDamage, maxDamage, physicalBonusPos, physicalBonusNeg, percent
+ local minOffHandDamage, maxOffHandDamage
+
+ if ranged then
+ rangedAttackSpeed, minDamage, maxDamage, physicalBonusPos, physicalBonusNeg, percent = UnitRangedDamage("player")
+ speed = rangedAttackSpeed
+ else
+ minDamage, maxDamage, minOffHandDamage, maxOffHandDamage, physicalBonusPos, physicalBonusNeg, percent = UnitDamage("player")
+ end
+
+ local displayMin = max(floor(minDamage), 1)
+ local displayMax = max(ceil(maxDamage), 1)
+
+ minDamage = (minDamage / percent) - physicalBonusPos - physicalBonusNeg
+ maxDamage = (maxDamage / percent) - physicalBonusPos - physicalBonusNeg
+
+ local baseDamage = (minDamage + maxDamage) * 0.5
+ local fullDamage = (baseDamage + physicalBonusPos + physicalBonusNeg) * percent
+ local totalBonus = (fullDamage - baseDamage)
+ local damagePerSecond = (max(fullDamage, 1) / speed)
+ local damageTooltip = max(floor(minDamage), 1) .. " - " .. max(ceil(maxDamage), 1)
+ local green = "|cff20ff20"
+ local red = "|cffff2020"
+
+ if (totalBonus == 0) then
+ if ((displayMin < 100) and (displayMax < 100)) then
+ damageText:SetText(displayMin .. " - " .. displayMax)
+ else
+ damageText:SetText(displayMin .. "-" .. displayMax)
+ end
+ else
+ local color
+ if (totalBonus > 0) then
+ color = green
+ else
+ color = red
+ end
+ if ((displayMin < 100) and (displayMax < 100)) then
+ damageText:SetText(color .. displayMin .. " - " .. displayMax .. "|r")
+ else
+ damageText:SetText(color .. displayMin .. "-" .. displayMax .. "|r")
+ end
+ if (physicalBonusPos > 0) then
+ damageTooltip = damageTooltip .. green .. " +" .. physicalBonusPos .. "|r"
+ end
+ if (physicalBonusNeg < 0) then
+ damageTooltip = damageTooltip .. red .. " " .. physicalBonusNeg .. "|r"
+ end
+ if (percent > 1) then
+ damageTooltip = damageTooltip .. green .. " x" .. floor(percent * 100 + 0.5) .. "%|r"
+ elseif (percent < 1) then
+ damageTooltip = damageTooltip .. red .. " x" .. floor(percent * 100 + 0.5) .. "%|r"
+ end
+ end
+ statFrame.damage = damageTooltip
+ statFrame.attackSpeed = speed
+ statFrame.dps = damagePerSecond
+
+ -- If there's an offhand speed then add the offhand info to the tooltip
+ if (offhandSpeed) then
+ minOffHandDamage = (minOffHandDamage / percent) - physicalBonusPos - physicalBonusNeg
+ maxOffHandDamage = (maxOffHandDamage / percent) - physicalBonusPos - physicalBonusNeg
+
+ local offhandBaseDamage = (minOffHandDamage + maxOffHandDamage) * 0.5
+ local offhandFullDamage = (offhandBaseDamage + physicalBonusPos + physicalBonusNeg) * percent
+ local offhandDamagePerSecond = (max(offhandFullDamage, 1) / offhandSpeed)
+ local offhandDamageTooltip = max(floor(minOffHandDamage), 1) .. " - " .. max(ceil(maxOffHandDamage), 1)
+ if (physicalBonusPos > 0) then
+ offhandDamageTooltip = offhandDamageTooltip .. green .. " +" .. physicalBonusPos .. "|r"
+ end
+ if (physicalBonusNeg < 0) then
+ offhandDamageTooltip = offhandDamageTooltip .. red .. " " .. physicalBonusNeg .. "|r"
+ end
+ if (percent > 1) then
+ offhandDamageTooltip = offhandDamageTooltip .. green .. " x" .. floor(percent * 100 + 0.5) .. "%|r"
+ elseif (percent < 1) then
+ offhandDamageTooltip = offhandDamageTooltip .. red .. " x" .. floor(percent * 100 + 0.5) .. "%|r"
+ end
+ statFrame.offhandDamage = offhandDamageTooltip
+ statFrame.offhandAttackSpeed = offhandSpeed
+ statFrame.offhandDps = offhandDamagePerSecond
+ else
+ statFrame.offhandAttackSpeed = nil
+ end
+
+ if ranged then
+ statFrame:SetScript("OnEnter", CharacterRangedDamageFrame_OnEnter)
+ else
+ statFrame:SetScript("OnEnter", CharacterDamageFrame_OnEnter)
+ end
+
+ statFrame:SetScript("OnLeave", function()
+ GameTooltip:Hide()
+ end)
+end
+
+function BCS:SetStat(statFrame, statIndex)
+ local label = getglobal(statFrame:GetName() .. "Label")
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local statIndexTable = {
+ "STRENGTH",
+ "AGILITY",
+ "STAMINA",
+ "INTELLECT",
+ "SPIRIT",
+ }
+
+ statFrame:SetScript("OnEnter", function()
+ PaperDollStatTooltip("player", statIndexTable[statIndex])
+ end)
+
+ statFrame:SetScript("OnLeave", function()
+ GameTooltip:Hide()
+ end)
+
+ label:SetText(TEXT(getglobal("SPELL_STAT" .. (statIndex - 1) .. "_NAME")) .. ":")
+ local stat, effectiveStat, posBuff, negBuff = UnitStat("player", statIndex)
+
+ -- Set the tooltip text
+ local tooltipText = HIGHLIGHT_FONT_COLOR_CODE .. getglobal("SPELL_STAT" .. (statIndex - 1) .. "_NAME") .. " "
+
+ if ((posBuff == 0) and (negBuff == 0)) then
+ text:SetText(effectiveStat)
+ statFrame.tooltip = tooltipText .. effectiveStat .. FONT_COLOR_CODE_CLOSE
+ else
+ tooltipText = tooltipText .. effectiveStat
+ if (posBuff > 0 or negBuff < 0) then
+ tooltipText = tooltipText .. " (" .. (stat - posBuff - negBuff) .. FONT_COLOR_CODE_CLOSE
+ end
+ if (posBuff > 0) then
+ tooltipText = tooltipText .. FONT_COLOR_CODE_CLOSE .. GREEN_FONT_COLOR_CODE .. "+" .. posBuff .. FONT_COLOR_CODE_CLOSE
+ end
+ if (negBuff < 0) then
+ tooltipText = tooltipText .. RED_FONT_COLOR_CODE .. " " .. negBuff .. FONT_COLOR_CODE_CLOSE
+ end
+ if (posBuff > 0 or negBuff < 0) then
+ tooltipText = tooltipText .. HIGHLIGHT_FONT_COLOR_CODE .. ")" .. FONT_COLOR_CODE_CLOSE
+ end
+ statFrame.tooltip = tooltipText
+
+ -- If there are any negative buffs then show the main number in red even if there are
+ -- positive buffs. Otherwise show in green.
+ if (negBuff < 0) then
+ text:SetText(RED_FONT_COLOR_CODE .. effectiveStat .. FONT_COLOR_CODE_CLOSE)
+ else
+ text:SetText(GREEN_FONT_COLOR_CODE .. effectiveStat .. FONT_COLOR_CODE_CLOSE)
+ end
+ end
+end
+
+function BCS:SetArmor(statFrame)
+ local base, effectiveArmor, armor, posBuff, negBuff = UnitArmor("player")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ local text = getglobal(statFrame:GetName() .. "StatText")
+
+ PaperDollFormatStat(ARMOR, base, posBuff, negBuff, statFrame, text)
+ label:SetText(TEXT(ARMOR_COLON))
+
+ local playerLevel = UnitLevel("player")
+ local armorReduction = effectiveArmor / ((85 * playerLevel) + 400)
+ armorReduction = 100 * (armorReduction / (armorReduction + 1))
+
+ statFrame.tooltipSubtext = format(ARMOR_TOOLTIP, playerLevel, armorReduction)
+
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:GetMissChanceRaw(wepSkill)
+ local _, ver = pcall(GetBuildInfo)
+ local diff = wepSkill - 315
+ local miss = 5
+
+ if ver == "1.17.2" then
+ miss = miss - (diff * 0.2) - BCS:GetHitRating()
+ else
+ if diff < -10 then
+ miss = miss - diff * 0.2;
+ else
+ miss = miss - diff * 0.1;
+ end
+
+ local hitChance = BCS:GetHitRating()
+ -- if skill diff < -10 then subtract one from +hit, if there is any +hit
+ if (diff < -10) and (hitChance > 0) then
+ hitChance = hitChance - 1
+ end
+ miss = miss - hitChance
+ end
+ return miss
+end
+
+function BCS:GetMissChance(wepSkill)
+ return max(0, min(BCS:GetMissChanceRaw(wepSkill), 60))
+end
+
+function BCS:GetDualWieldMissChance(wepSkill)
+ return max(0, min(BCS:GetMissChanceRaw(wepSkill) + 19, 60))
+end
+
+function BCS:GetGlanceChance(wepSkill)
+ return 10 + 15 * 2;
+end
+
+function BCS:GetGlanceReduction(wepSkill)
+ local _, ver = pcall(GetBuildInfo)
+ if ver == "1.17.2" then
+ return 65 + (wepSkill - 300) * 2
+ else
+ local diff = 315 - wepSkill;
+ local low = math.max(math.min(1.3 - 0.05 * diff, 0.91), 0.01);
+ local high = math.max(math.min(1.2 - 0.03 * diff, 0.99), 0.2);
+ return 100 * ((high - low) / 2 + low);
+ end
+end
+
+function BCS:GetDodgeChance(wepSkill)
+ return math.max(5 + (315 - wepSkill) * 0.1, 0);
+end
+
+function BCS:GetDualWieldCritCap(wepSkill)
+ local cap = 100 - self:GetDualWieldMissChance(wepSkill) - self:GetGlanceChance(wepSkill) - self:GetDodgeChance(wepSkill);
+ if (cap > 100) then
+ cap = 100;
+ end
+ if (cap < 0) then
+ cap = 0;
+ end
+ return cap;
+end
+
+function BCS:GetCritCap(wepSkill)
+ local cap = 100 - self:GetMissChance(wepSkill) - self:GetGlanceChance(wepSkill) - self:GetDodgeChance(wepSkill);
+ if (cap > 100) then
+ cap = 100;
+ end
+ if (cap < 0) then
+ cap = 0;
+ end
+ return cap;
+end
+
+function BCS:GetEffectiveBlockChance(leveldiff)
+ local block = GetBlockChance() - ((5 * leveldiff) * 0.04)
+ if block < 0 then
+ block = 0
+ end
+ return block
+end
+
+function BCS:GetEffectiveParryChance(leveldiff)
+ local parry = GetParryChance() - ((5 * leveldiff) * 0.04)
+ if parry < 0 then
+ parry = 0
+ end
+ return parry
+end
+
+function BCS:GetEffectiveDodgeChance(leveldiff)
+ local dodge = GetDodgeChance() - ((5 * leveldiff) * 0.04)
+ if dodge < 0 then
+ dodge = 0
+ end
+ return dodge
+end
+
+function BCS:SetDamage(statFrame)
+ local damageText = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ local speed, offhandSpeed = UnitAttackSpeed("player")
+
+ BCS_AddDamageTooltip(damageText, statFrame, speed, offhandSpeed)
+
+ label:SetText(TEXT(DAMAGE_COLON))
+end
+
+function BCS:SetAttackSpeed(statFrame)
+ local damageText = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ local speed, offhandSpeed = UnitAttackSpeed("player")
+
+ speed = format("%.2f", speed)
+ if (offhandSpeed) then
+ offhandSpeed = format("%.2f", offhandSpeed)
+ end
+ local text
+ if (offhandSpeed) then
+ text = speed .. " | " .. offhandSpeed
+ else
+ text = speed
+ end
+
+ BCS_AddDamageTooltip(damageText, statFrame, speed, offhandSpeed)
+
+ label:SetText(TEXT(SPEED) .. ":")
+ damageText:SetText(text)
+end
+
+function BCS:SetAttackPower(statFrame)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ local tooltipText = HIGHLIGHT_FONT_COLOR_CODE .. MELEE_ATTACK_POWER .. " "
+ local base, posBuff, negBuff = UnitAttackPower("player")
+ local effectiveStat = base + posBuff + negBuff
+
+ if ((posBuff == 0) and (negBuff == 0)) then
+ text:SetText(effectiveStat)
+ statFrame.tooltip = tooltipText .. base .. FONT_COLOR_CODE_CLOSE
+ else
+ tooltipText = tooltipText .. effectiveStat
+ if (posBuff > 0 or negBuff < 0) then
+ tooltipText = tooltipText .. " (" .. (base - posBuff - negBuff) .. FONT_COLOR_CODE_CLOSE
+ end
+ if (posBuff > 0) then
+ tooltipText = tooltipText .. FONT_COLOR_CODE_CLOSE .. GREEN_FONT_COLOR_CODE .. "+" .. posBuff .. FONT_COLOR_CODE_CLOSE
+ end
+ if (negBuff < 0) then
+ tooltipText = tooltipText .. RED_FONT_COLOR_CODE .. " " .. negBuff .. FONT_COLOR_CODE_CLOSE
+ end
+ if (posBuff > 0 or negBuff < 0) then
+ tooltipText = tooltipText .. HIGHLIGHT_FONT_COLOR_CODE .. ")" .. FONT_COLOR_CODE_CLOSE
+ end
+ statFrame.tooltip = tooltipText
+
+ if (negBuff < 0) then
+ text:SetText(RED_FONT_COLOR_CODE .. effectiveStat .. FONT_COLOR_CODE_CLOSE)
+ else
+ text:SetText(GREEN_FONT_COLOR_CODE .. effectiveStat .. FONT_COLOR_CODE_CLOSE)
+ end
+ end
+
+ label:SetText(TEXT(ATTACK_POWER_COLON))
+ PaperDollFormatStat(MELEE_ATTACK_POWER, base, posBuff, negBuff, statFrame, text)
+ statFrame.tooltipSubtext = format(MELEE_ATTACK_POWER_TOOLTIP, max((base + posBuff + negBuff), 0) / ATTACK_POWER_MAGIC_NUMBER)
+
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetSpellPower(statFrame, school)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ local green = "|cff20ff20"
+
+ if school then
+ local base, _, _, dmgOnly = BCS:GetSpellPower()
+ local fromSchool = BCS:GetSpellPower(school)
+ base = base + dmgOnly
+ local output = base + fromSchool
+
+ if fromSchool > 0 then
+ output = green .. output .. "|r"
+ end
+
+ label:SetText(L["SPELL_SCHOOL_" .. strupper(school)])
+ text:SetText(output)
+
+ if fromSchool > 0 then
+ statFrame.tooltip = format(L.SPELL_SCHOOL_SECONDARY_TOOLTIP, school, base + fromSchool, base, fromSchool)
+ else
+ statFrame.tooltip = format(L.SPELL_SCHOOL_TOOLTIP, school, base)
+ end
+ statFrame.tooltipSubtext = format(L.SPELL_SCHOOL_TOOLTIP_SUB, strlower(school))
+ else
+ local damageAndHealing, secondaryPower, secondaryName, damageOnly = BCS:GetSpellPower()
+ local total = damageAndHealing + damageOnly
+
+ label:SetText(L.SPELL_POWER_COLON)
+ if secondaryPower > 0 then
+ text:SetText(green .. total + secondaryPower)
+ else
+ text:SetText(total + secondaryPower)
+ end
+
+ if secondaryPower ~= 0 then
+ statFrame.tooltip = format(L.SPELL_POWER_SECONDARY_TOOLTIP, (total + secondaryPower), total, secondaryPower, secondaryName)
+ statFrame.tooltipSubtext = format(L.SPELL_POWER_SECONDARY_TOOLTIP_SUB)
+ else
+ statFrame.tooltip = format(L.SPELL_POWER_TOOLTIP, total)
+ statFrame.tooltipSubtext = format(L.SPELL_POWER_TOOLTIP_SUB)
+ end
+ end
+
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetHitRating(statFrame, ratingType)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ local _, class = UnitClass("player")
+ label:SetText(L.MELEE_HIT_RATING_COLON)
+
+ if ratingType == "MELEE" then
+ local rating = BCS:GetHitRating()
+ rating = rating .. "%"
+ text:SetText(rating)
+
+ statFrame.tooltip = (L.MELEE_HIT_TOOLTIP)
+ statFrame.tooltipSubtext = format(L.MELEE_HIT_TOOLTIP_SUB)
+
+ elseif ratingType == "RANGED" then
+ -- If no ranged attack then set to n/a
+ if UnitHasRelicSlot("player") or not (GetInventoryItemLink("player", 18)) then
+ text:SetText(NOT_APPLICABLE)
+ return
+ end
+
+ local rating = BCS:GetRangedHitRating()
+ rating = rating .. "%"
+ text:SetText(rating)
+
+ statFrame.tooltip = (L.RANGED_HIT_TOOLTIP)
+ statFrame.tooltipSubtext = format(L.RANGED_HIT_TOOLTIP_SUB)
+
+ elseif ratingType == "SPELL" then
+ local spell_hit, spell_hit_fire, spell_hit_frost, spell_hit_arcane, spell_hit_shadow, spell_hit_holy = BCS:GetSpellHitRating()
+
+ text:SetText(spell_hit .. "%")
+
+ statFrame.tooltip = format(L.SPELL_HIT_TOOLTIP)
+ statFrame.tooltipSubtext = format(L.SPELL_HIT_TOOLTIP_SUB)
+
+ if statFrame.tooltip then
+ statFrame:SetScript("OnEnter", function()
+ GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
+ GameTooltip:SetText(this.tooltip)
+ GameTooltip:AddLine(this.tooltipSubtext, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
+
+ if spell_hit_fire > 0 then
+ GameTooltip:AddLine(format(L.SPELL_SCHOOL_FIRE .. " spells: %.f%%", spell_hit + spell_hit_fire))
+ end
+ if spell_hit_frost > 0 then
+ GameTooltip:AddLine(format(L.SPELL_SCHOOL_FROST .. " spells: %.f%%", spell_hit + spell_hit_frost))
+ end
+ if spell_hit_arcane > 0 then
+ GameTooltip:AddLine(format(L.SPELL_SCHOOL_ARCANE .. " spells: %.f%%", spell_hit + spell_hit_arcane))
+ end
+ if spell_hit_shadow > 0 then
+ if class == "WARLOCK" then
+ GameTooltip:AddLine(format("Affliction spells: %.f%%", spell_hit + spell_hit_shadow))
+ else
+ GameTooltip:AddLine(format(L.SPELL_SCHOOL_SHADOW .. " spells: %.f%%", spell_hit + spell_hit_shadow))
+ end
+ end
+ if spell_hit_holy > 0 then
+ GameTooltip:AddLine(format(L.SPELL_SCHOOL_HOLY .. " and Discipline spells: %.f%%", spell_hit + spell_hit_holy))
+ end
+
+ GameTooltip:Show()
+ end)
+
+ statFrame:SetScript("OnLeave", function()
+ GameTooltip:Hide()
+ end)
+ end
+ end
+
+ if ratingType ~= "SPELL" then
+ BCS_AddTooltip(statFrame)
+ end
+end
+
+function BCS:SetMeleeCritChance(statFrame)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+
+ label:SetText(L.MELEE_CRIT_COLON)
+ text:SetText(format("%.2f%%", BCS:GetCritChance()))
+
+ statFrame.tooltip = (L.MELEE_CRIT_TOOLTIP)
+ statFrame.tooltipSubtext = (L.MELEE_CRIT_TOOLTIP_SUB)
+
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetWeaponSkill(statFrame)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+
+ label:SetText(L.WEAPON_SKILL_COLON)
+
+ if OffhandHasWeapon() == 1 then
+ text:SetText(format("%d | %d", BCS:GetMHWeaponSkill(), BCS:GetOHWeaponSkill()))
+ else
+ text:SetText(format("%d", BCS:GetMHWeaponSkill()))
+ end
+
+ statFrame.tooltip = format(L.MELEE_WEAPON_SKILL_TOOLTIP)
+ statFrame.tooltipSubtext = format(L.MELEE_WEAPON_SKILL_TOOLTIP_SUB)
+
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetRangedWeaponSkill(statFrame)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+
+ label:SetText(L.WEAPON_SKILL_COLON)
+
+ -- If no ranged attack then set to n/a
+ if UnitHasRelicSlot("player") or not (GetInventoryItemLink("player", 18)) then
+ text:SetText(NOT_APPLICABLE)
+ return
+ end
+
+ text:SetText(format("%d", BCS:GetRangedWeaponSkill()))
+
+ statFrame.tooltip = format(L.RANGED_WEAPON_SKILL_TOOLTIP)
+ statFrame.tooltipSubtext = format(L.RANGED_WEAPON_SKILL_TOOLTIP_SUB)
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetBossMissChance(statFrame)
+ local frame = statFrame
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ label:SetText(L.MISS_CHANCE_COLON)
+
+ local mh_miss = BCS:GetMissChance(BCS:GetMHWeaponSkill())
+
+ if OffhandHasWeapon() == 1 then
+ text:SetText(format("%.1f%% | %.1f%%",
+ BCS:GetDualWieldMissChance(BCS:GetMHWeaponSkill()),
+ BCS:GetDualWieldMissChance(BCS:GetOHWeaponSkill())))
+ else
+ text:SetText(format("%.1f%%", mh_miss))
+ end
+
+ statFrame.tooltip = format(L.MELEE_MISS_VS_BOSS_TOOLTIP)
+ statFrame.tooltipSubtext = format(L.MELEE_MISS_VS_BOSS_TOOLTIP_SUB)
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetBossGlanceReduction(statFrame)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ label:SetText(L.GLANCE_REDUCTION_COLON)
+
+ if OffhandHasWeapon() == 1 then
+ text:SetText(format("%d%% | %d%%",
+ BCS:GetGlanceReduction(BCS:GetMHWeaponSkill()),
+ BCS:GetGlanceReduction(BCS:GetOHWeaponSkill())))
+ else
+ text:SetText(format("%d%%", BCS:GetGlanceReduction(BCS:GetMHWeaponSkill())))
+ end
+
+ statFrame.tooltip = format(L.MELEE_GLANCE_VS_BOSS_TOOLTIP)
+ statFrame.tooltipSubtext = format(L.MELEE_GLANCE_VS_BOSS_TOOLTIP_SUB)
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetBossDodgeChance(statFrame)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ label:SetText(L.DODGE_CHANCE_COLON)
+ if OffhandHasWeapon() == 1 then
+ text:SetText(format("%.1f%% | %.1f%%",
+ BCS:GetDodgeChance(BCS:GetMHWeaponSkill()),
+ BCS:GetDodgeChance(BCS:GetOHWeaponSkill())))
+ else
+ text:SetText(format("%.1f%%", BCS:GetDodgeChance(BCS:GetMHWeaponSkill())))
+ end
+
+ statFrame.tooltip = format(L.MELEE_DODGE_VS_BOSS_TOOLTIP)
+ statFrame.tooltipSubtext = format(L.MELEE_DODGE_VS_BOSS_TOOLTIP_SUB)
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetBossCritCap(statFrame)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ label:SetText(L.CRIT_CAP_COLON)
+
+ if OffhandHasWeapon() == 1 then
+ text:SetText(format("%.1f%% | %.1f%%",
+ BCS:GetDualWieldCritCap(BCS:GetMHWeaponSkill()),
+ BCS:GetDualWieldCritCap(BCS:GetOHWeaponSkill())))
+ else
+ text:SetText(format("%.1f%%", BCS:GetCritCap(BCS:GetMHWeaponSkill())))
+ end
+
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetEffectiveBossCrit(statFrame)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ label:SetText(L.BOSS_CRIT_COLON)
+
+ local critChance = BCS:GetCritChance() - 3 -- 3 % crit reduction vs lvl 63
+ if OffhandHasWeapon() == 1 then
+ text:SetText(format("%.1f%% | %.1f%%",
+ math.min(critChance, BCS:GetDualWieldCritCap(BCS:GetMHWeaponSkill())),
+ math.min(critChance, BCS:GetDualWieldCritCap(BCS:GetOHWeaponSkill()))
+ ))
+ else
+ text:SetText(format("%.1f%%", math.min(critChance, BCS:GetCritCap(BCS:GetMHWeaponSkill()))))
+ end
+
+ statFrame.tooltip = format(L.MELEE_EFF_CRIT_VS_BOSS_TOOLTIP)
+ statFrame.tooltipSubtext = format(L.MELEE_EFF_CRIT_VS_BOSS_TOOLTIP_SUB)
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetSpellCritChance(statFrame)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ local _, class = UnitClass("player")
+
+ label:SetText(L.SPELL_CRIT_COLON)
+
+ local generic = BCS:GetSpellCritChance()
+ local spell1, spell2, spell3, spell4, spell5, spell6 = BCS:GetSpellCritFromClass(class)
+ local total1 = generic + spell1
+ local total2 = generic + spell2
+ local total3 = generic + spell3
+ local total4 = generic + spell4
+ local total5 = generic + spell5
+ local total6 = generic + spell6
+
+ if total1 > 100 then
+ total1 = 100
+ end
+ if total2 > 100 then
+ total2 = 100
+ end
+ if total3 > 100 then
+ total3 = 100
+ end
+ if total4 > 100 then
+ total4 = 100
+ end
+ if total5 > 100 then
+ total5 = 100
+ end
+ if total6 > 100 then
+ total6 = 100
+ end
+
+ text:SetText(format("%.2f%%", generic))
+
+ -- warlock spells that can crit are all destruction so just add this to generic
+ if class == "WARLOCK" and spell1 > 0 then
+ text:SetText(format("%.2f%%", generic + spell1))
+
+ -- if priest have both talents add lowest to generic cos there will be no more spells left that can crit
+ elseif class == "PRIEST" and spell3 > 0 and spell2 > 0 then
+ if spell2 < spell3 then
+ text:SetText(format("%.2f%%", generic + spell2))
+ elseif spell2 >= spell3 then
+ text:SetText(format("%.2f%%", generic + spell3))
+ end
+ end
+
+ statFrame.tooltip = format(L.SPELL_CRIT_TOOLTIP)
+ statFrame.tooltipSubtext = format(L.SPELL_CRIT_TOOLTIP_SUB)
+
+ statFrame:SetScript("OnEnter", function()
+ GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
+ GameTooltip:SetText(this.tooltip)
+ GameTooltip:AddLine(this.tooltipSubtext, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
+
+ if class == "DRUID" then
+ if spell1 > 0 then
+ GameTooltip:AddLine(format("Moonfire: %.2f%%", total1))
+ end
+ if spell2 > 0 then
+ GameTooltip:AddLine(format("Regrowth: %.2f%%", total2))
+ end
+
+ elseif class == "PALADIN" then
+ if spell1 > 0 then
+ GameTooltip:AddLine(format("Holy Light: %.2f%%", total1))
+ end
+ if spell2 > 0 then
+ GameTooltip:AddLine(format("Flash of Light: %.2f%%", total2))
+ end
+ if spell3 > 0 then
+ GameTooltip:AddLine(format("Holy Shock: %.2f%%", total3))
+ end
+
+ elseif class == "WARLOCK" then
+ if spell2 > 0 and spell2 ~= spell1 then
+ GameTooltip:AddLine(format("Searing Pain: %.2f%%", total2))
+ end
+
+ elseif class == "PRIEST" then
+ -- all healing spells are holy, change tooltip if player have both talents
+ if spell1 > 0 then
+ if spell3 > 0 then
+ GameTooltip:AddLine(format("Healing spells: %.2f%%", total1))
+ end
+ GameTooltip:AddLine(format("Holy spells: %.2f%%", total1 + spell3))
+ end
+ if spell2 > 0 then
+ GameTooltip:AddLine(format("Discipline spells: %.2f%%", total2 + spell3))
+ end
+ if spell3 > 0 then
+ if spell2 > 0 then
+ GameTooltip:AddLine(format("Shadow spells: %.2f%%", total3))
+ else
+ GameTooltip:AddLine(format("Offensive spells: %.2f%%", total3))
+ end
+ GameTooltip:AddLine(format("Smite: %.2f%%", total4))
+ end
+ if spell4 > 0 then
+ GameTooltip:AddLine(format("Prayer of Healing: %.2f%%", total4 + spell1))
+ end
+
+ elseif class == "MAGE" then
+ -- dont show specific spells if they have same chance as fire spells
+ if spell1 > 0 then
+ GameTooltip:AddLine(format("Arcane spells: %.2f%%", total1))
+ end
+ if spell2 > 0 then
+ GameTooltip:AddLine(format("Fire spells: %.2f%%", total2))
+ end
+ if spell3 > 0 and spell3 ~= spell2 then
+ GameTooltip:AddLine(format("Fire Blast: %.2f%%", total3))
+ end
+ if spell4 > 0 and spell4 ~= spell2 then
+ GameTooltip:AddLine(format("Scorch: %.2f%%", total4))
+ end
+ if spell5 > 0 and spell5 ~= spell2 then
+ GameTooltip:AddLine(format("Flamestrike: %.2f%%", total5))
+ end
+ if spell6 > 0 then
+ GameTooltip:AddLine(format("Frozen targets: %.2f%%", total6))
+ end
+
+ elseif class == "SHAMAN" then
+ if spell1 > 0 then
+ GameTooltip:AddLine(format("Lightning Bolt: %.2f%%", total1))
+ end
+ if spell2 > 0 then
+ GameTooltip:AddLine(format("Chain Lightning: %.2f%%", total2))
+ end
+ if spell3 > 0 then
+ GameTooltip:AddLine(format("Lightning Shield: %.2f%%", total3))
+ end
+ if spell4 > 0 then
+ GameTooltip:AddLine(format("Fire and Frost spells: %.2f%%", total4))
+ end
+ if spell5 > 0 then
+ GameTooltip:AddLine(format("Healing spells: %.2f%%", total5))
+ end
+ end
+
+ GameTooltip:Show()
+ end)
+
+ statFrame:SetScript("OnLeave", function()
+ GameTooltip:Hide()
+ end)
+end
+
+function BCS:SetRangedCritChance(statFrame)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+
+ label:SetText(L.RANGED_CRIT_COLON)
+
+ -- If no ranged attack then set to n/a
+ if UnitHasRelicSlot("player") or not (GetInventoryItemLink("player", 18)) then
+ text:SetText(NOT_APPLICABLE)
+ return
+ end
+
+ local crit = BCS:GetRangedCritChance()
+ -- apply skill difference modifier
+ local skill = BCS:GetRangedWeaponSkill()
+ local level = UnitLevel("player")
+ local skillDiff = skill - (level * 5)
+
+ if (skill >= (level * 5)) then
+ crit = crit + (skillDiff * 0.04)
+ else
+ crit = crit + (skillDiff * 0.2)
+ end
+
+ if crit < 0 then
+ crit = 0
+ end
+
+ text:SetText(format("%.2f%%", crit))
+
+ statFrame.tooltip = (L.RANGED_CRIT_TOOLTIP)
+ statFrame.tooltipSubtext = (L.RANGED_CRIT_TOOLTIP_SUB)
+
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetHealing(statFrame)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ local damageAndHealing = BCS:GetSpellPower()
+ local healingOnly, treebonus, ironclad = BCS:GetHealingPower()
+ local total = damageAndHealing + healingOnly
+ local tooltipExtra
+
+ if ironclad > 0 then
+ tooltipExtra = format("Healing power from Ironclad: %d", ironclad)
+ end
+
+ if treebonus and aura <= treebonus then
+ total = total + treebonus
+ elseif (not treebonus and aura > 0) or (treebonus and aura > treebonus) then
+ total = total + aura
+ end
+
+ label:SetText(L.HEAL_POWER_COLON)
+ text:SetText(format("%d", total))
+
+ if healingOnly ~= 0 then
+ statFrame.tooltip = format(L.SPELL_HEALING_POWER_SECONDARY_TOOLTIP, (total), damageAndHealing, healingOnly)
+ else
+ statFrame.tooltip = format(L.SPELL_HEALING_POWER_TOOLTIP, (total))
+ end
+ statFrame.tooltipSubtext = format(L.SPELL_HEALING_POWER_TOOLTIP_SUB)
+
+ BCS_AddTooltip(statFrame, tooltipExtra)
+end
+
+function BCS:SetManaRegen(statFrame)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+
+ label:SetText(L.MANA_REGEN_COLON)
+
+ -- if not a mana user and not a druid set to N/A
+ local _, class = UnitClass("player")
+ if (UnitPowerType("player") ~= 0 and not (class == "DRUID")) then
+ text:SetText(NOT_APPLICABLE)
+ statFrame.tooltip = nil
+ return
+ end
+
+ local base, casting, mp5 = BCS:GetManaRegen()
+ local mp2 = mp5 * 0.4
+ local totalRegen = base + mp2
+ local totalRegenWhileCasting = (casting / 100) * base + mp2
+
+ text:SetText(format("%d (%d)", totalRegen, totalRegenWhileCasting))
+
+ statFrame.tooltip = format(L.SPELL_MANA_REGEN_TOOLTIP, totalRegen, totalRegenWhileCasting)
+ statFrame.tooltipSubtext = format(L.SPELL_MANA_REGEN_TOOLTIP_SUB, base, casting, mp5, mp2)
+
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetDodge(statFrame, leveldiff)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ local dodge = BCS:GetEffectiveDodgeChance(leveldiff)
+
+ label:SetText(L.DODGE_COLON)
+ text:SetText(format("%.2f%%", dodge))
+
+ statFrame.tooltip = format(L.PLAYER_DODGE_TOOLTIP)
+ statFrame.tooltipSubtext = format(L.PLAYER_DODGE_TOOLTIP_SUB)
+
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetParry(statFrame, leveldiff)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ local parry = BCS:GetEffectiveParryChance(leveldiff)
+
+ label:SetText(L.PARRY_COLON)
+ text:SetText(format("%.2f%%", parry))
+
+ statFrame.tooltip = format(L.PLAYER_PARRY_TOOLTIP)
+ statFrame.tooltipSubtext = format(L.PLAYER_PARRY_TOOLTIP_SUB)
+
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetBlock(statFrame, leveldiff)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ local block = BCS:GetEffectiveBlockChance(leveldiff)
+ local tooltipExtra
+
+ if block > 0 then
+ tooltipExtra = "Block Value: " .. BCS:GetBlockValue()
+ end
+
+ label:SetText(L.BLOCK_COLON)
+ text:SetText(format("%.2f%%", block))
+
+ statFrame.tooltip = format(L.PLAYER_BLOCK_TOOLTIP)
+ statFrame.tooltipSubtext = format(L.PLAYER_BLOCK_TOOLTIP_SUB)
+
+ BCS_AddTooltip(statFrame, tooltipExtra)
+end
+
+function BCS:SetTotalAvoidance(statFrame, leveldiff)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ -- apply skill modifier
+ local base, mod = UnitDefense("player")
+ local skillDiff = (base + mod) - ((300) + (leveldiff * 5))
+ local missChance = 5 + (skillDiff * 0.04)
+
+ local block = BCS:GetEffectiveBlockChance(leveldiff)
+ local parry = BCS:GetEffectiveParryChance(leveldiff)
+ local dodge = BCS:GetEffectiveDodgeChance(leveldiff)
+
+ local total = missChance + (block + parry + dodge)
+
+ if total < 0 then
+ total = 0
+ end
+
+ label:SetText(L.TOTAL_COLON)
+ text:SetText(format("%.2f%%", total))
+
+ statFrame.tooltip = format(L.TOTAL_AVOIDANCE_TOOLTIP)
+ statFrame.tooltipSubtext = format(L.TOTAL_AVOIDANCE_TOOLTIP_SUB)
+
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetDefense(statFrame)
+ local base, modifier = UnitDefense("player")
+ local label = getglobal(statFrame:GetName() .. "Label")
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local posBuff = 0
+ local negBuff = 0
+
+ label:SetText(TEXT(DEFENSE_COLON))
+
+ if (modifier > 0) then
+ posBuff = modifier
+ elseif (modifier < 0) then
+ negBuff = modifier
+ end
+
+ PaperDollFormatStat(DEFENSE_COLON, base, posBuff, negBuff, statFrame, text)
+ statFrame.tooltip = format(L.DEFENSE_TOOLTIP)
+ statFrame.tooltipSubtext = format(L.DEFENSE_TOOLTIP_SUB)
+
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:SetRangedDamage(statFrame)
+ local label = getglobal(statFrame:GetName() .. "Label")
+ local damageText = getglobal(statFrame:GetName() .. "StatText")
+
+ label:SetText(TEXT(DAMAGE_COLON))
+
+ -- If no ranged attack then set to n/a
+ if UnitHasRelicSlot("player") or not (GetInventoryItemLink("player", 18)) then
+ damageText:SetText(NOT_APPLICABLE)
+ statFrame.damage = nil
+ return
+ end
+
+ BCS_AddDamageTooltip(damageText, statFrame, nil, nil, true)
+end
+
+function BCS:SetRangedAttackSpeed(statFrame)
+ local label = getglobal(statFrame:GetName() .. "Label")
+ local damageText = getglobal(statFrame:GetName() .. "StatText")
+
+ label:SetText(TEXT(SPEED) .. ":")
+
+ -- If no ranged attack then set to n/a
+ if UnitHasRelicSlot("player") or not (GetInventoryItemLink("player", 18)) then
+ damageText:SetText(NOT_APPLICABLE)
+ statFrame.damage = nil
+ return
+ end
+
+ BCS_AddDamageTooltip(damageText, statFrame, nil, nil, true)
+
+ damageText:SetText(format("%.2f", UnitRangedDamage("player")))
+end
+
+function BCS:SetRangedAttackPower(statFrame)
+ local text = getglobal(statFrame:GetName() .. "StatText")
+ local label = getglobal(statFrame:GetName() .. "Label")
+
+ label:SetText(TEXT(ATTACK_POWER_COLON))
+
+ -- If no ranged attack then set to n/a
+ if UnitHasRelicSlot("player") or not (GetInventoryItemLink("player", 18)) then
+ text:SetText(NOT_APPLICABLE)
+ statFrame.tooltip = nil
+ return
+ end
+
+ if (HasWandEquipped()) then
+ text:SetText("--");
+ statFrame.tooltip = nil;
+ return ;
+ end
+
+ local base, posBuff, negBuff = UnitRangedAttackPower("player")
+ local tooltipText = HIGHLIGHT_FONT_COLOR_CODE .. RANGED_ATTACK_POWER .. " "
+ local effectiveStat = base + posBuff + negBuff
+
+ PaperDollFormatStat(RANGED_ATTACK_POWER, base, posBuff, negBuff, statFrame, text)
+ statFrame.tooltipSubtext = format(RANGED_ATTACK_POWER_TOOLTIP, base / ATTACK_POWER_MAGIC_NUMBER)
+
+ if ((posBuff == 0) and (negBuff == 0)) then
+ text:SetText(effectiveStat)
+ statFrame.tooltip = tooltipText .. base .. FONT_COLOR_CODE_CLOSE
+ else
+ tooltipText = tooltipText .. effectiveStat
+ if (posBuff > 0 or negBuff < 0) then
+ tooltipText = tooltipText .. " (" .. (base - posBuff - negBuff) .. FONT_COLOR_CODE_CLOSE
+ end
+ if (posBuff > 0) then
+ tooltipText = tooltipText .. FONT_COLOR_CODE_CLOSE .. GREEN_FONT_COLOR_CODE .. "+" .. posBuff .. FONT_COLOR_CODE_CLOSE
+ end
+ if (negBuff < 0) then
+ tooltipText = tooltipText .. RED_FONT_COLOR_CODE .. " " .. negBuff .. FONT_COLOR_CODE_CLOSE
+ end
+ if (posBuff > 0 or negBuff < 0) then
+ tooltipText = tooltipText .. HIGHLIGHT_FONT_COLOR_CODE .. ")" .. FONT_COLOR_CODE_CLOSE
+ end
+ statFrame.tooltip = tooltipText
+
+ if (negBuff < 0) then
+ text:SetText(RED_FONT_COLOR_CODE .. effectiveStat .. FONT_COLOR_CODE_CLOSE)
+ else
+ text:SetText(GREEN_FONT_COLOR_CODE .. effectiveStat .. FONT_COLOR_CODE_CLOSE)
+ end
+ end
+
+ label:SetText(TEXT(ATTACK_POWER_COLON))
+
+ PaperDollFormatStat(RANGED_ATTACK_POWER, base, posBuff, negBuff, statFrame, text)
+ statFrame.tooltipSubtext = format(RANGED_ATTACK_POWER_TOOLTIP, max((base + posBuff + negBuff), 0) / ATTACK_POWER_MAGIC_NUMBER)
+
+ BCS_AddTooltip(statFrame)
+end
+
+function BCS:UpdatePaperdollStats(prefix, index)
+ local stat1 = getglobal(prefix .. 1)
+ local stat2 = getglobal(prefix .. 2)
+ local stat3 = getglobal(prefix .. 3)
+ local stat4 = getglobal(prefix .. 4)
+ local stat5 = getglobal(prefix .. 5)
+ local stat6 = getglobal(prefix .. 6)
+
+ stat1:SetScript("OnEnter", nil)
+ stat2:SetScript("OnEnter", nil)
+ stat3:SetScript("OnEnter", nil)
+ stat4:SetScript("OnEnter", nil)
+ stat4:SetScript("OnEnter", nil)
+ stat5:SetScript("OnEnter", nil)
+ stat6:SetScript("OnEnter", nil)
+
+ stat1.tooltip = nil
+ stat2.tooltip = nil
+ stat3.tooltip = nil
+ stat4.tooltip = nil
+ stat4.tooltip = nil
+ stat5.tooltip = nil
+ stat6.tooltip = nil
+
+ stat4:Show()
+ stat5:Show()
+ stat6:Show()
+
+ if (index == "PLAYERSTAT_BASE_STATS") then
+ BCS:SetStat(stat1, 1)
+ BCS:SetStat(stat2, 2)
+ BCS:SetStat(stat3, 3)
+ BCS:SetStat(stat4, 4)
+ BCS:SetStat(stat5, 5)
+ BCS:SetArmor(stat6)
+ elseif (index == "PLAYERSTAT_MELEE_COMBAT") then
+ BCS:SetWeaponSkill(stat1)
+ BCS:SetDamage(stat2)
+ BCS:SetAttackSpeed(stat3)
+ BCS:SetAttackPower(stat4)
+ BCS:SetHitRating(stat5, "MELEE")
+ BCS:SetMeleeCritChance(stat6)
+ elseif (index == "PLAYERSTAT_MELEE_BOSS") then
+ BCS:SetWeaponSkill(stat1)
+ BCS:SetBossMissChance(stat2)
+ BCS:SetBossDodgeChance(stat3)
+ BCS:SetBossGlanceReduction(stat4)
+ BCS:SetBossCritCap(stat5)
+ BCS:SetEffectiveBossCrit(stat6)
+ elseif (index == "PLAYERSTAT_RANGED_COMBAT") then
+ BCS:SetRangedWeaponSkill(stat1)
+ BCS:SetRangedDamage(stat2)
+ BCS:SetRangedAttackSpeed(stat3)
+ BCS:SetRangedAttackPower(stat4)
+ BCS:SetHitRating(stat5, "RANGED")
+ BCS:SetRangedCritChance(stat6)
+ elseif (index == "PLAYERSTAT_SPELL_COMBAT") then
+ BCS:SetSpellPower(stat1)
+ BCS:SetHitRating(stat2, "SPELL")
+ BCS:SetSpellCritChance(stat3)
+ BCS:SetHealing(stat4)
+ BCS:SetManaRegen(stat5)
+ stat6:Hide()
+ elseif (index == "PLAYERSTAT_SPELL_SCHOOLS") then
+ BCS:SetSpellPower(stat1, "Arcane")
+ BCS:SetSpellPower(stat2, "Fire")
+ BCS:SetSpellPower(stat3, "Frost")
+ BCS:SetSpellPower(stat4, "Holy")
+ BCS:SetSpellPower(stat5, "Nature")
+ BCS:SetSpellPower(stat6, "Shadow")
+ elseif (index == "PLAYERSTAT_DEFENSES") then
+ BCS:SetArmor(stat1)
+ BCS:SetDefense(stat2)
+ BCS:SetDodge(stat3, 0)
+ BCS:SetParry(stat4, 0)
+ BCS:SetBlock(stat5, 0)
+ BCS:SetTotalAvoidance(stat6, 0)
+ elseif (index == "PLAYERSTAT_DEFENSES_BOSS") then
+ BCS:SetArmor(stat1)
+ BCS:SetDefense(stat2)
+ BCS:SetDodge(stat3, 3)
+ BCS:SetParry(stat4, 3)
+ BCS:SetBlock(stat5, 3)
+ BCS:SetTotalAvoidance(stat6, 3)
+ end
+end
+
+local function PlayerStatFrameLeftDropDown_OnClick()
+ BCS.needScanGear = true
+ BCS.needScanTalents = true
+ BCS.needScanAuras = true
+ BCS.needScanSkills = true
+
+ UIDropDownMenu_SetSelectedValue(getglobal(this.owner), this.value)
+ IndexLeft = this.value
+ BCSConfig["DropdownLeft"] = IndexLeft
+ BCS:UpdatePaperdollStats("PlayerStatFrameLeft", this.value)
+
+ BCS.needScanGear = false
+ BCS.needScanTalents = false
+ BCS.needScanAuras = false
+ BCS.needScanSkills = false
+end
+
+local function PlayerStatFrameRightDropDown_OnClick()
+ BCS.needScanGear = true
+ BCS.needScanTalents = true
+ BCS.needScanAuras = true
+ BCS.needScanSkills = true
+
+ UIDropDownMenu_SetSelectedValue(getglobal(this.owner), this.value)
+ IndexRight = this.value
+ BCSConfig["DropdownRight"] = IndexRight
+ BCS:UpdatePaperdollStats("PlayerStatFrameRight", this.value)
+
+ BCS.needScanGear = false
+ BCS.needScanTalents = false
+ BCS.needScanAuras = false
+ BCS.needScanSkills = false
+end
+
+local function PlayerStatFrameLeftDropDown_Initialize()
+ local info = {}
+ local checked = nil
+ for i = 1, getn(BCS.PLAYERSTAT_DROPDOWN_OPTIONS) do
+ info.text = BCS.L[BCS.PLAYERSTAT_DROPDOWN_OPTIONS[i]]
+ info.func = PlayerStatFrameLeftDropDown_OnClick
+ info.value = BCS.PLAYERSTAT_DROPDOWN_OPTIONS[i]
+ info.checked = checked
+ info.owner = UIDROPDOWNMENU_OPEN_MENU
+ if not (UnitHasRelicSlot("player") and info.value == "PLAYERSTAT_RANGED_COMBAT") then
+ UIDropDownMenu_AddButton(info)
+ end
+ end
+end
+
+local function PlayerStatFrameRightDropDown_Initialize()
+ local info = {}
+ local checked = nil
+ for i = 1, getn(BCS.PLAYERSTAT_DROPDOWN_OPTIONS) do
+ info.text = BCS.L[BCS.PLAYERSTAT_DROPDOWN_OPTIONS[i]]
+ info.func = PlayerStatFrameRightDropDown_OnClick
+ info.value = BCS.PLAYERSTAT_DROPDOWN_OPTIONS[i]
+ info.checked = checked
+ info.owner = UIDROPDOWNMENU_OPEN_MENU
+ if not (UnitHasRelicSlot("player") and info.value == "PLAYERSTAT_RANGED_COMBAT") then
+ UIDropDownMenu_AddButton(info)
+ end
+ end
+end
+
+function PlayerStatFrameLeftDropDown_OnLoad()
+ RaiseFrameLevel(this)
+ RaiseFrameLevel(getglobal(this:GetName() .. "Button"))
+ UIDropDownMenu_Initialize(this, PlayerStatFrameLeftDropDown_Initialize)
+ UIDropDownMenu_SetWidth(99, this)
+ UIDropDownMenu_JustifyText("LEFT")
+end
+
+function PlayerStatFrameRightDropDown_OnLoad()
+ RaiseFrameLevel(this)
+ RaiseFrameLevel(getglobal(this:GetName() .. "Button"))
+ UIDropDownMenu_Initialize(this, PlayerStatFrameRightDropDown_Initialize)
+ UIDropDownMenu_SetWidth(99, this)
+ UIDropDownMenu_JustifyText("LEFT")
+end
diff --git a/BetterCharacterStats.toc b/BetterCharacterStats.toc
index 8eaba5f..f60ef0e 100644
--- a/BetterCharacterStats.toc
+++ b/BetterCharacterStats.toc
@@ -1,7 +1,11 @@
## Interface: 11200
## Title: BetterCharacterStats
-## Author: moh, Bennylava, |cffbe5effLexie|r
-## Version: 1.12.5
-## SavedVariablesPerCharacter: BCSConfig bcsupdateavailable
+## Author: moh, Bennylava, |cffbe5effLexie|r, Spit, Pepopo
+## Version: 1.12.7
+## SavedVariablesPerCharacter: BCSConfig BCScache
-BetterCharacterStats.xml
\ No newline at end of file
+Libs\Ace2\AceLibrary\AceLibrary.lua
+Libs\Ace2\AceOO-2.0\AceOO-2.0.lua
+Libs\Ace2\AceEvent-2.0\AceEvent-2.0.lua
+
+BetterCharacterStats.xml
diff --git a/BetterCharacterStats.xml b/BetterCharacterStats.xml
index e0ea65a..5540da1 100644
--- a/BetterCharacterStats.xml
+++ b/BetterCharacterStats.xml
@@ -43,6 +43,10 @@
--GameTooltip:Hide();
+
+ getglobal(this:GetName() .. "Label"):SetFont("Interface\\AddOns\\BetterCharacterStats\\Fonts\\Myriad-Pro.ttf", 11);
+ getglobal(this:GetName() .. "StatText"):SetFont("Interface\\AddOns\\BetterCharacterStats\\Fonts\\Myriad-Pro.ttf", 11);
+
@@ -298,4 +302,4 @@
-
\ No newline at end of file
+
diff --git a/Fonts/Myriad-Pro.ttf b/Fonts/Myriad-Pro.ttf
new file mode 100644
index 0000000..42237fc
Binary files /dev/null and b/Fonts/Myriad-Pro.ttf differ
diff --git a/Libs/Ace2/AceEvent-2.0/AceEvent-2.0.lua b/Libs/Ace2/AceEvent-2.0/AceEvent-2.0.lua
new file mode 100644
index 0000000..c654568
--- /dev/null
+++ b/Libs/Ace2/AceEvent-2.0/AceEvent-2.0.lua
@@ -0,0 +1,973 @@
+--[[
+Name: AceEvent-2.0
+Revision: $Rev: 17803 $
+Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
+Inspired By: Ace 1.x by Turan (turan@gryphon.com)
+Website: http://www.wowace.com/
+Documentation: http://www.wowace.com/index.php/AceEvent-2.0
+SVN: http://svn.wowace.com/root/trunk/Ace2/AceEvent-2.0
+Description: Mixin to allow for event handling, scheduling, and inter-addon
+ communication.
+Dependencies: AceLibrary, AceOO-2.0
+]]
+
+local MAJOR_VERSION = "AceEvent-2.0"
+local MINOR_VERSION = "$Revision: 17803 $"
+
+if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
+if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
+
+if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
+if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
+
+local AceOO = AceLibrary:GetInstance("AceOO-2.0")
+local Mixin = AceOO.Mixin
+local AceEvent = Mixin {
+ "RegisterEvent",
+ "RegisterAllEvents",
+ "UnregisterEvent",
+ "UnregisterAllEvents",
+ "TriggerEvent",
+ "ScheduleEvent",
+ "ScheduleRepeatingEvent",
+ "CancelScheduledEvent",
+ "CancelAllScheduledEvents",
+ "IsEventRegistered",
+ "IsEventScheduled",
+ "RegisterBucketEvent",
+ "UnregisterBucketEvent",
+ "UnregisterAllBucketEvents",
+ "IsBucketEventRegistered",
+ }
+
+local table_setn
+do
+ local version = GetBuildInfo()
+ if string.find(version, "^2%.") then
+ -- 2.0.0
+ table_setn = function() end
+ else
+ table_setn = table.setn
+ end
+end
+
+local weakKey = {__mode="k"}
+local new, del
+do
+ local list = setmetatable({}, weakKey)
+ function new()
+ local t = next(list)
+ if t then
+ list[t] = nil
+ return t
+ else
+ return {}
+ end
+ end
+
+ function del(t)
+ setmetatable(t, nil)
+ for k in pairs(t) do
+ t[k] = nil
+ end
+ list[t] = true
+ end
+end
+
+local FAKE_NIL
+local RATE
+
+local eventsWhichHappenOnce = {
+ PLAYER_LOGIN = true,
+ AceEvent_FullyInitialized = true,
+ VARIABLES_LOADED = true,
+ PLAYER_LOGOUT = true,
+}
+
+local registeringFromAceEvent
+function AceEvent:RegisterEvent(event, method, once)
+ AceEvent:argCheck(event, 2, "string")
+ if self == AceEvent and not registeringFromAceEvent then
+ AceEvent:argCheck(method, 3, "function")
+ self = method
+ else
+ AceEvent:argCheck(method, 3, "string", "function", "nil", "boolean", "number")
+ if type(method) == "boolean" or type(method) == "number" then
+ AceEvent:argCheck(once, 4, "nil")
+ once, method = method, event
+ end
+ end
+ AceEvent:argCheck(once, 4, "number", "boolean", "nil")
+ if eventsWhichHappenOnce[event] then
+ once = true
+ end
+ local throttleRate
+ if type(once) == "number" then
+ throttleRate, once = once
+ end
+ if not method then
+ method = event
+ end
+ if type(method) == "string" and type(self[method]) ~= "function" then
+ AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
+ else
+ assert(type(method) == "function" or type(method) == "string")
+ end
+
+ local AceEvent_registry = AceEvent.registry
+ if not AceEvent_registry[event] then
+ AceEvent_registry[event] = new()
+ AceEvent.frame:RegisterEvent(event)
+ end
+
+ local remember = true
+ if AceEvent_registry[event][self] then
+ remember = false
+ end
+ AceEvent_registry[event][self] = method
+
+ local AceEvent_onceRegistry = AceEvent.onceRegistry
+ if once then
+ if not AceEvent_onceRegistry then
+ AceEvent.onceRegistry = new()
+ AceEvent_onceRegistry = AceEvent.onceRegistry
+ end
+ if not AceEvent_onceRegistry[event] then
+ AceEvent_onceRegistry[event] = new()
+ end
+ AceEvent_onceRegistry[event][self] = true
+ else
+ if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
+ AceEvent_onceRegistry[event][self] = nil
+ if not next(AceEvent_onceRegistry[event]) then
+ AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
+ end
+ end
+ end
+
+ local AceEvent_throttleRegistry = AceEvent.throttleRegistry
+ if throttleRate then
+ if not AceEvent_throttleRegistry then
+ AceEvent.throttleRegistry = new()
+ AceEvent_throttleRegistry = AceEvent.throttleRegistry
+ end
+ if not AceEvent_throttleRegistry[event] then
+ AceEvent_throttleRegistry[event] = new()
+ end
+ if AceEvent_throttleRegistry[event][self] then
+ AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
+ end
+ AceEvent_throttleRegistry[event][self] = setmetatable(new(), weakKey)
+ local t = AceEvent_throttleRegistry[event][self]
+ t[RATE] = throttleRate
+ else
+ if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] then
+ if AceEvent_throttleRegistry[event][self] then
+ AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
+ end
+ if not next(AceEvent_throttleRegistry[event]) then
+ AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
+ end
+ end
+ end
+
+ if remember then
+ AceEvent:TriggerEvent("AceEvent_EventRegistered", self, event)
+ end
+end
+
+local ALL_EVENTS
+
+function AceEvent:RegisterAllEvents(method)
+ if self == AceEvent then
+ AceEvent:argCheck(method, 1, "function")
+ self = method
+ else
+ AceEvent:argCheck(method, 1, "string", "function")
+ if type(method) == "string" and type(self[method]) ~= "function" then
+ AceEvent:error("Cannot register all events to method %q, it does not exist", method)
+ end
+ end
+
+ local AceEvent_registry = AceEvent.registry
+ if not AceEvent_registry[ALL_EVENTS] then
+ AceEvent_registry[ALL_EVENTS] = new()
+ AceEvent.frame:RegisterAllEvents()
+ end
+
+ AceEvent_registry[ALL_EVENTS][self] = method
+end
+
+local _G = getfenv(0)
+local memstack, timestack = {}, {}
+local memdiff, timediff
+function AceEvent:TriggerEvent(event, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ AceEvent:argCheck(event, 2, "string")
+ local AceEvent_registry = AceEvent.registry
+ if (not AceEvent_registry[event] or not next(AceEvent_registry[event])) and (not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS])) then
+ return
+ end
+ local _G_event = _G.event
+ _G.event = event
+ local lastEvent = AceEvent.currentEvent
+ AceEvent.currentEvent = event
+
+ local AceEvent_onceRegistry = AceEvent.onceRegistry
+ local AceEvent_debugTable = AceEvent.debugTable
+ if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
+ local tmp = new()
+ for obj, method in pairs(AceEvent_onceRegistry[event]) do
+ tmp[obj] = AceEvent_registry[event] and AceEvent_registry[event][obj] or nil
+ end
+ local obj = next(tmp)
+ while obj do
+ local mem, time
+ if AceEvent_debugTable then
+ if not AceEvent_debugTable[event] then
+ AceEvent_debugTable[event] = new()
+ end
+ if not AceEvent_debugTable[event][obj] then
+ AceEvent_debugTable[event][obj] = new()
+ AceEvent_debugTable[event][obj].mem = 0
+ AceEvent_debugTable[event][obj].time = 0
+ AceEvent_debugTable[event][obj].count = 0
+ end
+ if memdiff then
+ table.insert(memstack, memdiff)
+ table.insert(timestack, timediff)
+ end
+ memdiff, timediff = 0, 0
+ mem, time = gcinfo(), GetTime()
+ end
+ local method = tmp[obj]
+ AceEvent.UnregisterEvent(obj, event)
+ if type(method) == "string" then
+ local obj_method = obj[method]
+ if obj_method then
+ obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ end
+ elseif method then -- function
+ method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ end
+ if AceEvent_debugTable then
+ local dmem, dtime = memdiff, timediff
+ mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
+ AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
+ AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
+ AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1
+
+ memdiff, timediff = table.remove(memstack), table.remove(timestack)
+ if memdiff then
+ memdiff = memdiff + mem + dmem
+ timediff = timediff + time + dtime
+ end
+ end
+ tmp[obj] = nil
+ obj = next(tmp)
+ end
+ del(tmp)
+ end
+
+ local AceEvent_throttleRegistry = AceEvent.throttleRegistry
+ local throttleTable = AceEvent_throttleRegistry and AceEvent_throttleRegistry[event]
+ if AceEvent_registry[event] then
+ local tmp = new()
+ for obj, method in pairs(AceEvent_registry[event]) do
+ tmp[obj] = method
+ end
+ local obj = next(tmp)
+ while obj do
+ local method = tmp[obj]
+ local continue = false
+ if throttleTable and throttleTable[obj] then
+ local a1 = a1
+ if a1 == nil then
+ a1 = FAKE_NIL
+ end
+ if not throttleTable[obj][a1] or GetTime() - throttleTable[obj][a1] >= throttleTable[obj][RATE] then
+ throttleTable[obj][a1] = GetTime()
+ else
+ continue = true
+ end
+ end
+ if not continue then
+ local mem, time
+ if AceEvent_debugTable then
+ if not AceEvent_debugTable[event] then
+ AceEvent_debugTable[event] = new()
+ end
+ if not AceEvent_debugTable[event][obj] then
+ AceEvent_debugTable[event][obj] = new()
+ AceEvent_debugTable[event][obj].mem = 0
+ AceEvent_debugTable[event][obj].time = 0
+ AceEvent_debugTable[event][obj].count = 0
+ end
+ if memdiff then
+ table.insert(memstack, memdiff)
+ table.insert(timestack, timediff)
+ end
+ memdiff, timediff = 0, 0
+ mem, time = gcinfo(), GetTime()
+ end
+ if type(method) == "string" then
+ local obj_method = obj[method]
+ if obj_method then
+ obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ end
+ elseif method then -- function
+ method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ end
+ if AceEvent_debugTable then
+ local dmem, dtime = memdiff, timediff
+ mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
+ AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
+ AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
+ AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1
+
+ memdiff, timediff = table.remove(memstack), table.remove(timestack)
+ if memdiff then
+ memdiff = memdiff + mem + dmem
+ timediff = timediff + time + dtime
+ end
+ end
+ end
+ tmp[obj] = nil
+ obj = next(tmp)
+ end
+ del(tmp)
+ end
+ if AceEvent_registry[ALL_EVENTS] then
+ local tmp = new()
+ for obj, method in pairs(AceEvent_registry[ALL_EVENTS]) do
+ tmp[obj] = method
+ end
+ local obj = next(tmp)
+ while obj do
+ local method = tmp[obj]
+ local mem, time
+ if AceEvent_debugTable then
+ if not AceEvent_debugTable[event] then
+ AceEvent_debugTable[event] = new()
+ end
+ if not AceEvent_debugTable[event][obj] then
+ AceEvent_debugTable[event][obj] = new()
+ AceEvent_debugTable[event][obj].mem = 0
+ AceEvent_debugTable[event][obj].time = 0
+ AceEvent_debugTable[event][obj].count = 0
+ end
+ if memdiff then
+ table.insert(memstack, memdiff)
+ table.insert(timestack, timediff)
+ end
+ memdiff, timediff = 0, 0
+ mem, time = gcinfo(), GetTime()
+ end
+ if type(method) == "string" then
+ local obj_method = obj[method]
+ if obj_method then
+ obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ end
+ elseif method then -- function
+ method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ end
+ if AceEvent_debugTable then
+ local dmem, dtime = memdiff, timediff
+ mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
+ AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
+ AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
+ AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1
+
+ memdiff, timediff = table.remove(memstack), table.remove(timestack)
+ if memdiff then
+ memdiff = memdiff + mem + dmem
+ timediff = timediff + time + dtime
+ end
+ end
+ tmp[obj] = nil
+ obj = next(tmp)
+ end
+ del(tmp)
+ end
+ _G.event = _G_event
+ AceEvent.currentEvent = lastEvent
+end
+
+-- local accessors
+local getn = table.getn
+local tinsert = table.insert
+local tremove = table.remove
+local floor = math.floor
+local GetTime = GetTime
+local next = next
+local pairs = pairs
+local unpack = unpack
+
+local delayRegistry
+local tmp = {}
+local function OnUpdate()
+ local t = GetTime()
+ for k,v in pairs(delayRegistry) do
+ tmp[k] = true
+ end
+ for k in pairs(tmp) do
+ local v = delayRegistry[k]
+ if v then
+ local v_time = v.time
+ if not v_time then
+ delayRegistry[k] = del(v)
+ elseif v_time <= t then
+ local v_repeatDelay = v.repeatDelay
+ if v_repeatDelay then
+ -- use the event time, not the current time, else timing inaccuracies add up over time
+ v.time = v_time + v_repeatDelay
+ end
+ local event = v.event
+ local mem, time
+ if AceEvent_debugTable then
+ mem, time = gcinfo(), GetTime()
+ end
+ if type(event) == "function" then
+ event(unpack(v))
+ else
+ AceEvent:TriggerEvent(event, unpack(v))
+ end
+ if AceEvent_debugTable then
+ mem, time = gcinfo() - mem, GetTime() - time
+ v.mem = v.mem + mem
+ v.timeSpent = v.timeSpent + time
+ v.count = v.count + 1
+ end
+ if not v_repeatDelay then
+ local x = delayRegistry[k]
+ if x and x.time == v_time then -- check if it was manually reset
+ delayRegistry[k] = del(v)
+ end
+ end
+ end
+ end
+ end
+ for k in pairs(tmp) do
+ tmp[k] = nil
+ end
+ if not next(delayRegistry) then
+ AceEvent.frame:Hide()
+ end
+end
+
+local function ScheduleEvent(self, repeating, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ local id
+ if type(event) == "string" or type(event) == "table" then
+ if type(event) == "table" then
+ if not delayRegistry or not delayRegistry[event] then
+ AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
+ end
+ end
+ if type(delay) ~= "number" then
+ id, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20
+ AceEvent:argCheck(event, 3, "string", "function", --[[ so message is right ]] "number")
+ AceEvent:argCheck(delay, 4, "number")
+ self:CancelScheduledEvent(id)
+ end
+ else
+ AceEvent:argCheck(event, 2, "string", "function")
+ AceEvent:argCheck(delay, 3, "number")
+ end
+
+ if not delayRegistry then
+ AceEvent.delayRegistry = new()
+ delayRegistry = AceEvent.delayRegistry
+ AceEvent.frame:SetScript("OnUpdate", OnUpdate)
+ end
+ local t
+ if type(id) == "table" then
+ for k in pairs(id) do
+ id[k] = nil
+ end
+ t = id
+ else
+ t = new()
+ end
+ t[1] = a1
+ t[2] = a2
+ t[3] = a3
+ t[4] = a4
+ t[5] = a5
+ t[6] = a6
+ t[7] = a7
+ t[8] = a8
+ t[9] = a9
+ t[10] = a10
+ t[11] = a11
+ t[12] = a12
+ t[13] = a13
+ t[14] = a14
+ t[15] = a15
+ t[16] = a16
+ t[17] = a17
+ t[18] = a18
+ t[19] = a19
+ t[20] = a20
+ table_setn(t, 20)
+ t.event = event
+ t.time = GetTime() + delay
+ t.self = self
+ t.id = id or t
+ t.repeatDelay = repeating and delay
+ if AceEvent_debugTable then
+ t.mem = 0
+ t.count = 0
+ t.timeSpent = 0
+ end
+ delayRegistry[t.id] = t
+ AceEvent.frame:Show()
+ return t.id
+end
+
+function AceEvent:ScheduleEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ if type(event) == "string" or type(event) == "table" then
+ if type(event) == "table" then
+ if not delayRegistry or not delayRegistry[event] then
+ AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
+ end
+ end
+ if type(delay) ~= "number" then
+ AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
+ AceEvent:argCheck(a1, 4, "number")
+ end
+ else
+ AceEvent:argCheck(event, 2, "string", "function")
+ AceEvent:argCheck(delay, 3, "number")
+ end
+
+ return ScheduleEvent(self, false, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+end
+
+function AceEvent:ScheduleRepeatingEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ if type(event) == "string" or type(event) == "table" then
+ if type(event) == "table" then
+ if not delayRegistry or not delayRegistry[event] then
+ AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
+ end
+ end
+ if type(delay) ~= "number" then
+ AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
+ AceEvent:argCheck(a1, 4, "number")
+ end
+ else
+ AceEvent:argCheck(event, 2, "string", "function")
+ AceEvent:argCheck(delay, 3, "number")
+ end
+
+ return ScheduleEvent(self, true, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+end
+
+function AceEvent:CancelScheduledEvent(t)
+ AceEvent:argCheck(t, 2, "string", "table")
+ if delayRegistry then
+ local v = delayRegistry[t]
+ if v then
+ delayRegistry[t] = del(v)
+ if not next(delayRegistry) then
+ AceEvent.frame:Hide()
+ end
+ return true
+ end
+ end
+ return false
+end
+
+function AceEvent:IsEventScheduled(t)
+ AceEvent:argCheck(t, 2, "string", "table")
+ if delayRegistry then
+ local v = delayRegistry[t]
+ if v then
+ return true, v.time - GetTime()
+ end
+ end
+ return false, nil
+end
+
+function AceEvent:UnregisterEvent(event)
+ AceEvent:argCheck(event, 2, "string")
+ local AceEvent_registry = AceEvent.registry
+ if AceEvent_registry[event] and AceEvent_registry[event][self] then
+ AceEvent_registry[event][self] = nil
+ local AceEvent_onceRegistry = AceEvent.onceRegistry
+ if AceEvent_onceRegistry and AceEvent_onceRegistry[event] and AceEvent_onceRegistry[event][self] then
+ AceEvent_onceRegistry[event][self] = nil
+ if not next(AceEvent_onceRegistry[event]) then
+ AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
+ end
+ end
+ local AceEvent_throttleRegistry = AceEvent.throttleRegistry
+ if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] and AceEvent_throttleRegistry[event][self] then
+ AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
+ if not next(AceEvent_throttleRegistry[event]) then
+ AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
+ end
+ end
+ if not next(AceEvent_registry[event]) then
+ AceEvent_registry[event] = del(AceEvent_registry[event])
+ if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then
+ AceEvent.frame:UnregisterEvent(event)
+ end
+ end
+ else
+ if self == AceEvent then
+ error(string.format("Cannot unregister event %q. Improperly unregistering from AceEvent-2.0.", event), 2)
+ else
+ AceEvent:error("Cannot unregister event %q. %q is not registered with it.", event, self)
+ end
+ end
+ AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
+end
+
+function AceEvent:UnregisterAllEvents()
+ local AceEvent_registry = AceEvent.registry
+ if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then
+ AceEvent_registry[ALL_EVENTS][self] = nil
+ if not next(AceEvent_registry[ALL_EVENTS]) then
+ del(AceEvent_registry[ALL_EVENTS])
+ AceEvent.frame:UnregisterAllEvents()
+ for k,v in pairs(AceEvent_registry) do
+ if k ~= ALL_EVENTS then
+ AceEvent.frame:RegisterEvent(k)
+ end
+ end
+ AceEvent_registry[event] = nil
+ end
+ end
+ local first = true
+ for event, data in pairs(AceEvent_registry) do
+ if first then
+ if AceEvent_registry.AceEvent_EventUnregistered then
+ event = "AceEvent_EventUnregistered"
+ else
+ first = false
+ end
+ end
+ local x = data[self]
+ data[self] = nil
+ if x and event ~= ALL_EVENTS then
+ if not next(data) then
+ del(data)
+ if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then
+ AceEvent.frame:UnregisterEvent(event)
+ end
+ AceEvent_registry[event] = nil
+ end
+ AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
+ end
+ if first then
+ event = nil
+ end
+ end
+ if AceEvent.onceRegistry then
+ for event, data in pairs(AceEvent.onceRegistry) do
+ data[self] = nil
+ end
+ end
+end
+
+function AceEvent:CancelAllScheduledEvents()
+ if delayRegistry then
+ for k,v in pairs(delayRegistry) do
+ if v.self == self then
+ delayRegistry[k] = del(v)
+ end
+ end
+ if not next(delayRegistry) then
+ AceEvent.frame:Hide()
+ end
+ end
+end
+
+function AceEvent:IsEventRegistered(event)
+ AceEvent:argCheck(event, 2, "string")
+ local AceEvent_registry = AceEvent.registry
+ if self == AceEvent then
+ return AceEvent_registry[event] and next(AceEvent_registry[event]) and true or false
+ end
+ if AceEvent_registry[event] and AceEvent_registry[event][self] then
+ return true, AceEvent_registry[event][self]
+ end
+ return false, nil
+end
+
+local bucketfunc
+function AceEvent:RegisterBucketEvent(event, delay, method)
+ AceEvent:argCheck(event, 2, "string", "table")
+ if type(event) == "table" then
+ for k,v in pairs(event) do
+ if type(k) ~= "number" then
+ AceEvent:error("All keys to argument #2 to `RegisterBucketEvent' must be numbers.")
+ elseif type(v) ~= "string" then
+ AceEvent:error("All values to argument #2 to `RegisterBucketEvent' must be strings.")
+ end
+ end
+ end
+ AceEvent:argCheck(delay, 3, "number")
+ if AceEvent == self then
+ AceEvent:argCheck(method, 4, "function")
+ self = method
+ else
+ if type(event) == "string" then
+ AceEvent:argCheck(method, 4, "string", "function", "nil")
+ if not method then
+ method = event
+ end
+ else
+ AceEvent:argCheck(method, 4, "string", "function")
+ end
+
+ if type(method) == "string" and type(self[method]) ~= "function" then
+ AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
+ end
+ end
+ if not AceEvent.buckets then
+ AceEvent.buckets = new()
+ end
+ if not AceEvent.buckets[event] then
+ AceEvent.buckets[event] = new()
+ end
+ if not AceEvent.buckets[event][self] then
+ AceEvent.buckets[event][self] = new()
+ AceEvent.buckets[event][self].current = new()
+ AceEvent.buckets[event][self].self = self
+ else
+ AceEvent.CancelScheduledEvent(self, AceEvent.buckets[event][self].id)
+ end
+ local bucket = AceEvent.buckets[event][self]
+ bucket.method = method
+
+ local func = function(arg1)
+ bucket.run = true
+ if arg1 then
+ bucket.current[arg1] = true
+ end
+ end
+ AceEvent.buckets[event][self].func = func
+ if type(event) == "string" then
+ AceEvent.RegisterEvent(self, event, func)
+ else
+ for _,v in ipairs(event) do
+ AceEvent.RegisterEvent(self, v, func)
+ end
+ end
+ if not bucketfunc then
+ bucketfunc = function(bucket)
+ local current = bucket.current
+ local method = bucket.method
+ local self = bucket.self
+ if bucket.run then
+ if type(method) == "string" then
+ self[method](self, current)
+ elseif method then -- function
+ method(current)
+ end
+ for k in pairs(current) do
+ current[k] = nil
+ k = nil
+ end
+ bucket.run = false
+ end
+ end
+ end
+ bucket.id = AceEvent.ScheduleRepeatingEvent(self, bucketfunc, delay, bucket)
+end
+
+function AceEvent:IsBucketEventRegistered(event)
+ AceEvent:argCheck(event, 2, "string", "table")
+ return AceEvent.buckets and AceEvent.buckets[event] and AceEvent.buckets[event][self]
+end
+
+function AceEvent:UnregisterBucketEvent(event)
+ AceEvent:argCheck(event, 2, "string", "table")
+ if not AceEvent.buckets or not AceEvent.buckets[event] or not AceEvent.buckets[event][self] then
+ AceEvent:error("Cannot unregister bucket event %q. %q is not registered with it.", event, self)
+ end
+
+ local bucket = AceEvent.buckets[event][self]
+
+ if type(event) == "string" then
+ AceEvent.UnregisterEvent(self, event)
+ else
+ for _,v in ipairs(event) do
+ AceEvent.UnregisterEvent(self, v)
+ end
+ end
+ AceEvent:CancelScheduledEvent(bucket.id)
+
+ del(bucket.current)
+ AceEvent.buckets[event][self] = del(AceEvent.buckets[event][self])
+ if not next(AceEvent.buckets[event]) then
+ AceEvent.buckets[event] = del(AceEvent.buckets[event])
+ end
+end
+
+function AceEvent:UnregisterAllBucketEvents()
+ if not AceEvent.buckets or not next(AceEvent.buckets) then
+ return
+ end
+ for k,v in pairs(AceEvent.buckets) do
+ if v == self then
+ AceEvent.UnregisterBucketEvent(self, k)
+ k = nil
+ end
+ end
+end
+
+function AceEvent:OnEmbedDisable(target)
+ self.UnregisterAllEvents(target)
+
+ self.CancelAllScheduledEvents(target)
+
+ self.UnregisterAllBucketEvents(target)
+end
+
+function AceEvent:EnableDebugging()
+ if not self.debugTable then
+ self.debugTable = new()
+
+ if delayRegistry then
+ for k,v in pairs(self.delayRegistry) do
+ if not v.mem then
+ v.mem = 0
+ v.count = 0
+ v.timeSpent = 0
+ end
+ end
+ end
+ end
+end
+
+function AceEvent:IsFullyInitialized()
+ return self.postInit or false
+end
+
+function AceEvent:IsPostPlayerLogin()
+ return self.playerLogin or false
+end
+
+function AceEvent:activate(oldLib, oldDeactivate)
+ AceEvent = self
+
+ if oldLib then
+ self.onceRegistry = oldLib.onceRegistry
+ self.throttleRegistry = oldLib.throttleRegistry
+ self.delayRegistry = oldLib.delayRegistry
+ self.buckets = oldLib.buckets
+ self.registry = oldLib.registry
+ self.frame = oldLib.frame
+ self.debugTable = oldLib.debugTable
+ self.playerLogin = oldLib.pew or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage and true
+ self.postInit = oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true
+ self.ALL_EVENTS = oldLib.ALL_EVENTS
+ self.FAKE_NIL = oldLib.FAKE_NIL
+ self.RATE = oldLib.RATE
+ end
+ if not self.registry then
+ self.registry = {}
+ end
+ if not self.frame then
+ self.frame = CreateFrame("Frame", "AceEvent20Frame")
+ end
+ if not self.ALL_EVENTS then
+ self.ALL_EVENTS = {}
+ end
+ if not self.FAKE_NIL then
+ self.FAKE_NIL = {}
+ end
+ if not self.RATE then
+ self.RATE = {}
+ end
+ ALL_EVENTS = self.ALL_EVENTS
+ FAKE_NIL = self.FAKE_NIL
+ RATE = self.RATE
+ local inPlw = false
+ local blacklist = {
+ UNIT_INVENTORY_CHANGED = true,
+ BAG_UPDATE = true,
+ ITEM_LOCK_CHANGED = true,
+ ACTIONBAR_SLOT_CHANGED = true,
+ }
+ self.frame:SetScript("OnEvent", function()
+ local event = event
+ if event == "PLAYER_ENTERING_WORLD" then
+ inPlw = false
+ elseif event == "PLAYER_LEAVING_WORLD" then
+ inPlw = true
+ end
+ if event and (not inPlw or not blacklist[event]) then
+ self:TriggerEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
+ end
+ end)
+ if self.delayRegistry then
+ delayRegistry = self.delayRegistry
+ self.frame:SetScript("OnUpdate", OnUpdate)
+ end
+
+ self:UnregisterAllEvents()
+ self:CancelAllScheduledEvents()
+
+ registeringFromAceEvent = true
+ self:RegisterEvent("LOOT_OPENED", function()
+ SendAddonMessage("LOOT_OPENED", "", "RAID")
+ end)
+ registeringFromAceEvent = nil
+
+ if not self.playerLogin then
+ registeringFromAceEvent = true
+ self:RegisterEvent("PLAYER_LOGIN", function()
+ self.playerLogin = true
+ end, true)
+ registeringFromAceEvent = nil
+ end
+
+ if not self.postInit then
+ local isReload = true
+ local function func()
+ self.postInit = true
+ self:TriggerEvent("AceEvent_FullyInitialized")
+ if self.registry["CHAT_MSG_CHANNEL_NOTICE"] and self.registry["CHAT_MSG_CHANNEL_NOTICE"][self] then
+ self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE")
+ end
+ if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
+ self:UnregisterEvent("MEETINGSTONE_CHANGED")
+ end
+ if self.registry["MINIMAP_ZONE_CHANGED"] and self.registry["MINIMAP_ZONE_CHANGED"][self] then
+ self:UnregisterEvent("MINIMAP_ZONE_CHANGED")
+ end
+ if self.registry["LANGUAGE_LIST_CHANGED"] and self.registry["LANGUAGE_LIST_CHANGED"][self] then
+ self:UnregisterEvent("LANGUAGE_LIST_CHANGED")
+ end
+ end
+ registeringFromAceEvent = true
+ local f = function()
+ self.playerLogin = true
+ self:ScheduleEvent("AceEvent_FullyInitialized", func, 1)
+ end
+ self:RegisterEvent("MEETINGSTONE_CHANGED", f, true)
+ self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", function()
+ self:ScheduleEvent("AceEvent_FullyInitialized", func, 0.05)
+ end)
+ self:RegisterEvent("LANGUAGE_LIST_CHANGED", function()
+ if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
+ registeringFromAceEvent = true
+ self:UnregisterEvent("MEETINGSTONE_CHANGED")
+ self:RegisterEvent("MINIMAP_ZONE_CHANGED", f, true)
+ registeringFromAceEvent = nil
+ end
+ end)
+ self:ScheduleEvent("AceEvent_FullyInitialized", func, 10)
+ registeringFromAceEvent = nil
+ end
+
+ self.super.activate(self, oldLib, oldDeactivate)
+ if oldLib then
+ oldDeactivate(oldLib)
+ end
+end
+
+AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, AceEvent.activate)
+AceEvent = AceLibrary(MAJOR_VERSION)
diff --git a/Libs/Ace2/AceLibrary/AceLibrary.lua b/Libs/Ace2/AceLibrary/AceLibrary.lua
new file mode 100644
index 0000000..0f2b237
--- /dev/null
+++ b/Libs/Ace2/AceLibrary/AceLibrary.lua
@@ -0,0 +1,757 @@
+--[[
+Name: AceLibrary
+Revision: $Rev: 17722 $
+Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
+Inspired By: Iriel (iriel@vigilance-committee.org)
+ Tekkub (tekkub@gmail.com)
+ Revision: $Rev: 17722 $
+Website: http://www.wowace.com/
+Documentation: http://www.wowace.com/index.php/AceLibrary
+SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary
+Description: Versioning library to handle other library instances, upgrading,
+ and proper access.
+ It also provides a base for libraries to work off of, providing
+ proper error tools. It is handy because all the errors occur in the
+ file that called it, not in the library file itself.
+Dependencies: None
+]]
+
+local ACELIBRARY_MAJOR = "AceLibrary"
+local ACELIBRARY_MINOR = "$Revision: 17722 $"
+
+if loadstring("return function(...) return ... end") and AceLibrary and AceLibrary:HasInstance(ACELIBRARY_MAJOR) then return end -- lua51 check
+local table_setn
+do
+ local version = GetBuildInfo()
+ if string.find(version, "^2%.") then
+ -- 2.0.0
+ table_setn = function() end
+ else
+ table_setn = table.setn
+ end
+end
+
+local string_gfind = string.gmatch or string.gfind
+
+local _G = getfenv(0)
+local previous = _G[ACELIBRARY_MAJOR]
+if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end
+
+local function safecall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
+ local success, err = pcall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
+ if not success then geterrorhandler()(err) end
+end
+
+-- @table AceLibrary
+-- @brief System to handle all versioning of libraries.
+local AceLibrary = {}
+local AceLibrary_mt = {}
+setmetatable(AceLibrary, AceLibrary_mt)
+
+local tmp
+local function error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ if type(self) ~= "table" then
+ _G.error(string.format("Bad argument #1 to `error' (table expected, got %s)", type(self)), 2)
+ end
+ if not tmp then
+ tmp = {}
+ else
+ for k in pairs(tmp) do tmp[k] = nil end
+ table_setn(tmp, 0)
+ end
+
+ table.insert(tmp, a1)
+ table.insert(tmp, a2)
+ table.insert(tmp, a3)
+ table.insert(tmp, a4)
+ table.insert(tmp, a5)
+ table.insert(tmp, a6)
+ table.insert(tmp, a7)
+ table.insert(tmp, a8)
+ table.insert(tmp, a9)
+ table.insert(tmp, a10)
+ table.insert(tmp, a11)
+ table.insert(tmp, a12)
+ table.insert(tmp, a13)
+ table.insert(tmp, a14)
+ table.insert(tmp, a15)
+ table.insert(tmp, a16)
+ table.insert(tmp, a17)
+ table.insert(tmp, a18)
+ table.insert(tmp, a19)
+ table.insert(tmp, a20)
+
+ local stack = debugstack()
+ if not message then
+ local _,_,second = string.find(stack, "\n(.-)\n")
+ message = "error raised! " .. second
+ else
+ for i = 1,table.getn(tmp) do
+ tmp[i] = tostring(tmp[i])
+ end
+ for i = 1,10 do
+ table.insert(tmp, "nil")
+ end
+ message = string.format(message, unpack(tmp))
+ end
+
+ if getmetatable(self) and getmetatable(self).__tostring then
+ message = string.format("%s: %s", tostring(self), message)
+ elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then
+ message = string.format("%s: %s", self:GetLibraryVersion(), message)
+ elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then
+ message = string.format("%s: %s", self.class:GetLibraryVersion(), message)
+ end
+
+ local first = string.gsub(stack, "\n.*", "")
+ local file = string.gsub(first, ".*\\(.*).lua:%d+: .*", "%1")
+ file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
+
+ local i = 0
+ for s in string_gfind(stack, "\n([^\n]*)") do
+ i = i + 1
+ if not string.find(s, file .. "%.lua:%d+:") then
+ file = string.gsub(s, "^.*\\(.*).lua:%d+: .*", "%1")
+ file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
+ break
+ end
+ end
+ local j = 0
+ for s in string_gfind(stack, "\n([^\n]*)") do
+ j = j + 1
+ if j > i and not string.find(s, file .. "%.lua:%d+:") then
+ _G.error(message, j + 1)
+ return
+ end
+ end
+ _G.error(message, 2)
+ return
+end
+
+local function assert(self, condition, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ if not condition then
+ if not message then
+ local stack = debugstack()
+ local _,_,second = string.find(stack, "\n(.-)\n")
+ message = "assertion failed! " .. second
+ end
+ error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ return
+ end
+ return condition
+end
+
+local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5)
+ if type(num) ~= "number" then
+ error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num))
+ elseif type(kind) ~= "string" then
+ error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind))
+ end
+ local errored = false
+ arg = type(arg)
+ if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then
+ local _,_,func = string.find(debugstack(), "`argCheck'.-([`<].-['>])")
+ if not func then
+ _,_,func = string.find(debugstack(), "([`<].-['>])")
+ end
+ if kind5 then
+ error(self, "Bad argument #%s to %s (%s, %s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, kind5, arg)
+ elseif kind4 then
+ error(self, "Bad argument #%s to %s (%s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, arg)
+ elseif kind3 then
+ error(self, "Bad argument #%s to %s (%s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, arg)
+ elseif kind2 then
+ error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg)
+ else
+ error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg)
+ end
+ end
+end
+
+local function pcall(self, func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = _G.pcall(func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ if not a1 then
+ error(self, string.gsub(a2, ".-%.lua:%d-: ", ""))
+ else
+ return a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20
+ end
+end
+
+local recurse = {}
+local function addToPositions(t, major)
+ if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then
+ rawset(t, recurse, true)
+ AceLibrary.positions[t] = major
+ for k,v in pairs(t) do
+ if type(v) == "table" and not rawget(v, recurse) then
+ addToPositions(v, major)
+ end
+ if type(k) == "table" and not rawget(k, recurse) then
+ addToPositions(k, major)
+ end
+ end
+ local mt = getmetatable(t)
+ if mt and not rawget(mt, recurse) then
+ addToPositions(mt, major)
+ end
+ rawset(t, recurse, nil)
+ end
+end
+
+local function svnRevisionToNumber(text)
+ if type(text) == "string" then
+ if string.find(text, "^%$Revision: (%d+) %$$") then
+ return tonumber((string.gsub(text, "^%$Revision: (%d+) %$$", "%1")))
+ elseif string.find(text, "^%$Rev: (%d+) %$$") then
+ return tonumber((string.gsub(text, "^%$Rev: (%d+) %$$", "%1")))
+ elseif string.find(text, "^%$LastChangedRevision: (%d+) %$$") then
+ return tonumber((string.gsub(text, "^%$LastChangedRevision: (%d+) %$$", "%1")))
+ end
+ elseif type(text) == "number" then
+ return text
+ end
+ return nil
+end
+
+local crawlReplace
+do
+ local recurse = {}
+ local function func(t, to, from)
+ if recurse[t] then
+ return
+ end
+ recurse[t] = true
+ local mt = getmetatable(t)
+ setmetatable(t, nil)
+ rawset(t, to, rawget(t, from))
+ rawset(t, from, nil)
+ for k,v in pairs(t) do
+ if v == from then
+ t[k] = to
+ elseif type(v) == "table" then
+ if not recurse[v] then
+ func(v, to, from)
+ end
+ end
+
+ if type(k) == "table" then
+ if not recurse[k] then
+ func(k, to, from)
+ end
+ end
+ end
+ setmetatable(t, mt)
+ if mt then
+ if mt == from then
+ setmetatable(t, to)
+ elseif not recurse[mt] then
+ func(mt, to, from)
+ end
+ end
+ end
+ function crawlReplace(t, to, from)
+ func(t, to, from)
+ for k in pairs(recurse) do
+ recurse[k] = nil
+ end
+ end
+end
+
+-- @function destroyTable
+-- @brief remove all the contents of a table
+-- @param t table to destroy
+local function destroyTable(t)
+ setmetatable(t, nil)
+ for k,v in pairs(t) do t[k] = nil end
+ table_setn(t, 0)
+end
+
+local function isFrame(frame)
+ return type(frame) == "table" and type(rawget(frame, 0)) == "userdata" and type(rawget(frame, 'IsFrameType')) == "function" and getmetatable(frame) and type(rawget(getmetatable(frame), '__index')) == "function"
+end
+
+local new, del
+do
+ local tables = setmetatable({}, {__mode = "k"})
+
+ function new()
+ local t = next(tables)
+ if t then
+ tables[t] = nil
+ return t
+ else
+ return {}
+ end
+ end
+
+ function del(t, depth)
+ if depth and depth > 0 then
+ for k,v in pairs(t) do
+ if type(v) == "table" and not isFrame(v) then
+ del(v, depth - 1)
+ end
+ end
+ end
+ destroyTable(t)
+ tables[t] = true
+ end
+end
+
+-- @function copyTable
+-- @brief Create a shallow copy of a table and return it.
+-- @param from The table to copy from
+-- @return A shallow copy of the table
+local function copyTable(from)
+ local to = new()
+ for k,v in pairs(from) do to[k] = v end
+ table_setn(to, table.getn(from))
+ setmetatable(to, getmetatable(from))
+ return to
+end
+
+-- @function deepTransfer
+-- @brief Fully transfer all data, keeping proper previous table
+-- backreferences stable.
+-- @param to The table with which data is to be injected into
+-- @param from The table whose data will be injected into the first
+-- @param saveFields If available, a shallow copy of the basic data is saved
+-- in here.
+-- @param list The account of table references
+-- @param list2 The current status on which tables have been traversed.
+local deepTransfer
+do
+ -- @function examine
+ -- @brief Take account of all the table references to be shared
+ -- between the to and from tables.
+ -- @param to The table with which data is to be injected into
+ -- @param from The table whose data will be injected into the first
+ -- @param list An account of the table references
+ local function examine(to, from, list, major)
+ list[from] = to
+ for k,v in pairs(from) do
+ if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then
+ if from[k] == to[k] then
+ list[from[k]] = to[k]
+ elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then
+ list[from[k]] = from[k]
+ elseif not list[from[k]] then
+ examine(to[k], from[k], list, major)
+ end
+ end
+ end
+ return list
+ end
+
+ function deepTransfer(to, from, saveFields, major, list, list2)
+ setmetatable(to, nil)
+ local createdList
+ if not list then
+ createdList = true
+ list = new()
+ list2 = new()
+ examine(to, from, list, major)
+ end
+ list2[to] = to
+ for k,v in pairs(to) do
+ if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then
+ if saveFields then
+ saveFields[k] = v
+ end
+ to[k] = nil
+ elseif v ~= _G then
+ if saveFields then
+ saveFields[k] = copyTable(v)
+ end
+ end
+ end
+ for k in pairs(from) do
+ if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then
+ if not list2[to[k]] then
+ deepTransfer(to[k], from[k], nil, major, list, list2)
+ end
+ to[k] = list[to[k]] or list2[to[k]]
+ else
+ rawset(to, k, from[k])
+ end
+ end
+ table_setn(to, table.getn(from))
+ setmetatable(to, getmetatable(from))
+ local mt = getmetatable(to)
+ if mt then
+ if list[mt] then
+ setmetatable(to, list[mt])
+ elseif mt.__index and list[mt.__index] then
+ mt.__index = list[mt.__index]
+ end
+ end
+ destroyTable(from)
+ if createdList then
+ del(list)
+ del(list2)
+ end
+ end
+end
+
+-- @method TryToLoadStandalone
+-- @brief Attempt to find and load a standalone version of the requested library
+-- @param major A string representing the major version
+-- @return If library is found, return values from the call to LoadAddOn are returned
+-- If the library has been requested previously, nil is returned.
+local function TryToLoadStandalone(major)
+ if not AceLibrary.scannedlibs then AceLibrary.scannedlibs = {} end
+ if AceLibrary.scannedlibs[major] then return end
+
+ AceLibrary.scannedlibs[major] = true
+
+ local name, _, _, enabled, loadable = GetAddOnInfo(major)
+ if loadable then
+ return LoadAddOn(name)
+ end
+
+ for i=1,GetNumAddOns() do
+ if GetAddOnMetadata(i, "X-AceLibrary-"..major) then
+ local name, _, _, enabled, loadable = GetAddOnInfo(i)
+ if loadable then
+ return LoadAddOn(name)
+ end
+ end
+ end
+end
+
+-- @method IsNewVersion
+-- @brief Obtain whether the supplied version would be an upgrade to the
+-- current version. This allows for bypass code in library
+-- declaration.
+-- @param major A string representing the major version
+-- @param minor An integer or an svn revision string representing the minor version
+-- @return whether the supplied version would be newer than what is
+-- currently available.
+function AceLibrary:IsNewVersion(major, minor)
+ argCheck(self, major, 2, "string")
+ TryToLoadStandalone(major)
+
+ if type(minor) == "string" then
+ local m = svnRevisionToNumber(minor)
+ if m then
+ minor = m
+ else
+ _G.error(string.format("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
+ end
+ end
+ argCheck(self, minor, 3, "number")
+ local data = self.libs[major]
+ if not data then
+ return true
+ end
+ return data.minor < minor
+end
+
+-- @method HasInstance
+-- @brief Returns whether an instance exists. This allows for optional support of a library.
+-- @param major A string representing the major version.
+-- @param minor (optional) An integer or an svn revision string representing the minor version.
+-- @return Whether an instance exists.
+function AceLibrary:HasInstance(major, minor)
+ argCheck(self, major, 2, "string")
+ TryToLoadStandalone(major)
+
+ if minor then
+ if type(minor) == "string" then
+ local m = svnRevisionToNumber(minor)
+ if m then
+ minor = m
+ else
+ _G.error(string.format("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
+ end
+ end
+ argCheck(self, minor, 3, "number")
+ if not self.libs[major] then
+ return
+ end
+ return self.libs[major].minor == minor
+ end
+ return self.libs[major] and true
+end
+
+-- @method GetInstance
+-- @brief Returns the library with the given major/minor version.
+-- @param major A string representing the major version.
+-- @param minor (optional) An integer or an svn revision string representing the minor version.
+-- @return The library with the given major/minor version.
+function AceLibrary:GetInstance(major, minor)
+ argCheck(self, major, 2, "string")
+ TryToLoadStandalone(major)
+
+ local data = self.libs[major]
+ if not data then
+ _G.error(string.format("Cannot find a library instance of %s.", major), 2)
+ return
+ end
+ if minor then
+ if type(minor) == "string" then
+ local m = svnRevisionToNumber(minor)
+ if m then
+ minor = m
+ else
+ _G.error(string.format("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
+ end
+ end
+ argCheck(self, minor, 2, "number")
+ if data.minor ~= minor then
+ _G.error(string.format("Cannot find a library instance of %s, minor version %d.", major, minor), 2)
+ return
+ end
+ end
+ return data.instance
+end
+
+-- Syntax sugar. AceLibrary("FooBar-1.0")
+AceLibrary_mt.__call = AceLibrary.GetInstance
+
+local donothing
+
+local AceEvent
+
+-- @method Register
+-- @brief Registers a new version of a given library.
+-- @param newInstance the library to register
+-- @param major the major version of the library
+-- @param minor the minor version of the library
+-- @param activateFunc (optional) A function to be called when the library is
+-- fully activated. Takes the arguments
+-- (newInstance [, oldInstance, oldDeactivateFunc]). If
+-- oldInstance is given, you should probably call
+-- oldDeactivateFunc(oldInstance).
+-- @param deactivateFunc (optional) A function to be called by a newer library's
+-- activateFunc.
+-- @param externalFunc (optional) A function to be called whenever a new
+-- library is registered.
+function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc)
+ argCheck(self, newInstance, 2, "table")
+ argCheck(self, major, 3, "string")
+ if type(minor) == "string" then
+ local m = svnRevisionToNumber(minor)
+ if m then
+ minor = m
+ else
+ _G.error(string.format("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
+ end
+ end
+ argCheck(self, minor, 4, "number")
+ if math.floor(minor) ~= minor or minor < 0 then
+ error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor)
+ end
+ argCheck(self, activateFunc, 5, "function", "nil")
+ argCheck(self, deactivateFunc, 6, "function", "nil")
+ argCheck(self, externalFunc, 7, "function", "nil")
+ if not deactivateFunc then
+ if not donothing then
+ donothing = function() end
+ end
+ deactivateFunc = donothing
+ end
+ local data = self.libs[major]
+ if not data then
+ -- This is new
+ local instance = copyTable(newInstance)
+ crawlReplace(instance, instance, newInstance)
+ destroyTable(newInstance)
+ if AceLibrary == newInstance then
+ self = instance
+ AceLibrary = instance
+ end
+ self.libs[major] = {
+ instance = instance,
+ minor = minor,
+ deactivateFunc = deactivateFunc,
+ externalFunc = externalFunc,
+ }
+ rawset(instance, 'GetLibraryVersion', function(self)
+ return major, minor
+ end)
+ if not rawget(instance, 'error') then
+ rawset(instance, 'error', error)
+ end
+ if not rawget(instance, 'assert') then
+ rawset(instance, 'assert', assert)
+ end
+ if not rawget(instance, 'argCheck') then
+ rawset(instance, 'argCheck', argCheck)
+ end
+ if not rawget(instance, 'pcall') then
+ rawset(instance, 'pcall', pcall)
+ end
+ addToPositions(instance, major)
+ if activateFunc then
+ safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil
+ end
+
+ if externalFunc then
+ for k,data in pairs(self.libs) do
+ if k ~= major then
+ safecall(externalFunc, instance, k, data.instance)
+ end
+ end
+ end
+
+ for k,data in pairs(self.libs) do
+ if k ~= major and data.externalFunc then
+ safecall(data.externalFunc, data.instance, major, instance)
+ end
+ end
+ if major == "AceEvent-2.0" then
+ AceEvent = instance
+ end
+ if AceEvent then
+ AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance)
+ end
+
+ return instance
+ end
+ local instance = data.instance
+ if minor <= data.minor then
+ -- This one is already obsolete, raise an error.
+ _G.error(string.format("Obsolete library registered. %s is already registered at version %d. You are trying to register version %d. Hint: if not AceLibrary:IsNewVersion(%q, %d) then return end", major, data.minor, minor, major, minor), 2)
+ return
+ end
+ -- This is an update
+ local oldInstance = new()
+
+ addToPositions(newInstance, major)
+ local isAceLibrary = (AceLibrary == newInstance)
+ local old_error, old_assert, old_argCheck, old_pcall
+ if isAceLibrary then
+ self = instance
+ AceLibrary = instance
+
+ old_error = instance.error
+ old_assert = instance.assert
+ old_argCheck = instance.argCheck
+ old_pcall = instance.pcall
+
+ self.error = error
+ self.assert = assert
+ self.argCheck = argCheck
+ self.pcall = pcall
+ end
+ deepTransfer(instance, newInstance, oldInstance, major)
+ crawlReplace(instance, instance, newInstance)
+ local oldDeactivateFunc = data.deactivateFunc
+ data.minor = minor
+ data.deactivateFunc = deactivateFunc
+ data.externalFunc = externalFunc
+ rawset(instance, 'GetLibraryVersion', function(self)
+ return major, minor
+ end)
+ if not rawget(instance, 'error') then
+ rawset(instance, 'error', error)
+ end
+ if not rawget(instance, 'assert') then
+ rawset(instance, 'assert', assert)
+ end
+ if not rawget(instance, 'argCheck') then
+ rawset(instance, 'argCheck', argCheck)
+ end
+ if not rawget(instance, 'pcall') then
+ rawset(instance, 'pcall', pcall)
+ end
+ if isAceLibrary then
+ for _,v in pairs(self.libs) do
+ local i = type(v) == "table" and v.instance
+ if type(i) == "table" then
+ if not rawget(i, 'error') or i.error == old_error then
+ rawset(i, 'error', error)
+ end
+ if not rawget(i, 'assert') or i.assert == old_assert then
+ rawset(i, 'assert', assert)
+ end
+ if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then
+ rawset(i, 'argCheck', argCheck)
+ end
+ if not rawget(i, 'pcall') or i.pcall == old_pcall then
+ rawset(i, 'pcall', pcall)
+ end
+ end
+ end
+ end
+ if activateFunc then
+ safecall(activateFunc, instance, oldInstance, oldDeactivateFunc)
+ else
+ safecall(oldDeactivateFunc, oldInstance)
+ end
+ del(oldInstance)
+
+ if externalFunc then
+ for k,data in pairs(self.libs) do
+ if k ~= major then
+ safecall(externalFunc, instance, k, data.instance)
+ end
+ end
+ end
+
+ return instance
+end
+
+local iter
+function AceLibrary:IterateLibraries()
+ if not iter then
+ local function iter(t, k)
+ k = next(t, k)
+ if not k then
+ return nil
+ else
+ return k, t[k].instance
+ end
+ end
+ end
+ return iter, self.libs, nil
+end
+
+-- @function Activate
+-- @brief The activateFunc for AceLibrary itself. Called when
+-- AceLibrary properly registers.
+-- @param self Reference to AceLibrary
+-- @param oldLib (optional) Reference to an old version of AceLibrary
+-- @param oldDeactivate (optional) Function to deactivate the old lib
+local function activate(self, oldLib, oldDeactivate)
+ if not self.libs then
+ if oldLib then
+ self.libs = oldLib.libs
+ self.scannedlibs = oldLib.scannedlibs
+ end
+ if not self.libs then
+ self.libs = {}
+ end
+ if not self.scannedlibs then
+ self.scannedlibs = {}
+ end
+ end
+ if not self.positions then
+ if oldLib then
+ self.positions = oldLib.positions
+ end
+ if not self.positions then
+ self.positions = setmetatable({}, { __mode = "k" })
+ end
+ end
+
+ -- Expose the library in the global environment
+ _G[ACELIBRARY_MAJOR] = self
+
+ if oldDeactivate then
+ oldDeactivate(oldLib)
+ end
+end
+
+if not previous then
+ previous = AceLibrary
+end
+if not previous.libs then
+ previous.libs = {}
+end
+AceLibrary.libs = previous.libs
+if not previous.positions then
+ previous.positions = setmetatable({}, { __mode = "k" })
+end
+AceLibrary.positions = previous.positions
+AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate)
diff --git a/Libs/Ace2/AceOO-2.0/AceOO-2.0.lua b/Libs/Ace2/AceOO-2.0/AceOO-2.0.lua
new file mode 100644
index 0000000..cd372b4
--- /dev/null
+++ b/Libs/Ace2/AceOO-2.0/AceOO-2.0.lua
@@ -0,0 +1,1006 @@
+--[[
+Name: AceOO-2.0
+Revision: $Rev: 17638 $
+Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
+Inspired By: Ace 1.x by Turan (turan@gryphon.com)
+Website: http://www.wowace.com/
+Documentation: http://www.wowace.com/index.php/AceOO-2.0
+SVN: http://svn.wowace.com/root/trunk/Ace2/AceOO-2.0
+Description: Library to provide an object-orientation framework.
+Dependencies: AceLibrary
+]]
+
+local MAJOR_VERSION = "AceOO-2.0"
+local MINOR_VERSION = "$Revision: 17639 $"
+
+-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version
+if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
+if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
+
+if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
+local table_setn
+do
+ local version = GetBuildInfo()
+ if string.find(version, "^2%.") then
+ -- 2.0.0
+ table_setn = function() end
+ else
+ table_setn = table.setn
+ end
+end
+
+local AceOO = {
+ error = AceLibrary.error,
+ argCheck = AceLibrary.argCheck
+}
+
+-- @function getuid
+-- @brief Obtain a unique string identifier for the object in question.
+-- @param t The object to obtain the uid for.
+-- @return The uid string.
+local function pad(cap)
+ return string.rep('0', 8 - string.len(cap)) .. cap
+end
+local function getuid(t)
+ local mt = getmetatable(t)
+ setmetatable(t, nil)
+ local str = tostring(t)
+ setmetatable(t, mt)
+ local _,_,cap = string.find(str, '[^:]*: 0x(.*)$')
+ if cap then return pad(cap) end
+ _,_,cap = string.find(str, '[^:]*: (.*)$')
+ if cap then return pad(cap) end
+end
+
+local function getlibrary(o)
+ if type(o) == "table" then
+ return o
+ elseif type(o) == "string" then
+ if not AceLibrary:HasInstance(o) then
+ AceOO:error("Library %q does not exist.", o)
+ end
+ return AceLibrary(o)
+ end
+end
+
+-- @function Factory
+-- @brief Construct a factory for the creation of objects.
+-- @param obj The object whose init method will be called on the new factory
+-- object.
+-- @param newobj The object whose init method will be called on the new
+-- objects that the Factory creates, to initialize them.
+-- @param (a1..a20) Arguments which will be passed to obj.init() in addition
+-- to the Factory object.
+-- @return The new factory which creates a newobj when its new method is called,
+-- or when it is called directly (__call metamethod).
+local Factory
+do
+ local function new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
+ a13, a14, a15, a16, a17, a18, a19, a20)
+ local t = {}
+ local uid = getuid(t)
+ local l = getlibrary
+ obj:init(t, l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7),
+ l(a8), l(a9), l(a10), l(a11), l(a12), l(a13),
+ l(a14), l(a15), l(a16), l(a17), l(a18), l(a19),
+ l(a20))
+ t.uid = uid
+ return t
+ end
+
+ local function createnew(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+ a11, a12, a13, a14, a15, a16, a17, a18,
+ a19, a20)
+ local o = self.prototype
+ local l = getlibrary
+ return new(o, l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7),
+ l(a8), l(a9), l(a10), l(a11), l(a12), l(a13),
+ l(a14), l(a15), l(a16), l(a17), l(a18), l(a19),
+ l(a20))
+ end
+
+ function Factory(obj, newobj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+ a11, a12, a13, a14, a15, a16, a17, a18,
+ a19, a20)
+ local t = new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
+ a13, a14, a15, a16, a17, a18, a19, a20)
+ t.prototype = newobj
+ t.new = createnew
+ getmetatable(t).__call = t.new
+ return t
+ end
+end
+
+
+local function objtostring(self)
+ if self.ToString then
+ return self:ToString()
+ elseif self.GetLibraryVersion then
+ return (self:GetLibraryVersion())
+ elseif self.super then
+ local s = "Sub-" .. tostring(self.super)
+ local first = true
+ if self.interfaces then
+ for interface in pairs(self.interfaces) do
+ if first then
+ s = s .. "(" .. tostring(interface)
+ first = false
+ else
+ s = s .. ", " .. tostring(interface)
+ end
+ end
+ end
+ if self.mixins then
+ for mixin in pairs(self.mixins) do
+ if first then
+ s = s .. tostring(mixin)
+ first = false
+ else
+ s = s .. ", " .. tostring(mixin)
+ end
+ end
+ end
+ if first then
+ if self.uid then
+ return s .. ":" .. self.uid
+ else
+ return s
+ end
+ else
+ return s .. ")"
+ end
+ else
+ return self.uid and 'Subclass:' .. self.uid or 'Subclass'
+ end
+end
+
+-- @table Object
+-- @brief Base of all objects, including Class.
+--
+-- @method init
+-- @brief Initialize a new object.
+-- @param newobject The object to initialize
+-- @param class The class to make newobject inherit from
+local Object
+do
+ Object = {}
+ function Object:init(newobject, class)
+ local parent = class or self
+ if not rawget(newobject, 'uid') then
+ newobject.uid = getuid(newobject)
+ end
+ local mt = {
+ __index = parent,
+ __tostring = objtostring,
+ }
+ setmetatable(newobject, mt)
+ end
+ Object.uid = getuid(Object)
+ setmetatable(Object, { __tostring = function() return 'Object' end })
+end
+
+local Interface
+
+local function validateInterface(object, interface)
+ if not object.class and object.prototype then
+ object = object.prototype
+ end
+ for k,v in pairs(interface.interface) do
+ if tostring(type(object[k])) ~= v then
+ return false
+ end
+ end
+ if interface.superinterfaces then
+ for superinterface in pairs(interface.superinterfaces) do
+ if not validateInterface(object, superinterface) then
+ return false
+ end
+ end
+ end
+ if type(object.class) == "table" and rawequal(object.class.prototype, object) then
+ if not object.class.interfaces then
+ rawset(object.class, 'interfaces', {})
+ end
+ object.class.interfaces[interface] = true
+ elseif type(object.class) == "table" and type(object.class.prototype) == "table" then
+ validateInterface(object.class.prototype, interface)
+ -- check if class is proper, thus preventing future checks.
+ end
+ return true
+end
+
+-- @function inherits
+-- @brief Return whether an Object or Class inherits from a given
+-- parent.
+-- @param object Object or Class to check
+-- @param parent Parent to test inheritance from
+-- @return whether an Object or Class inherits from a given
+-- parent.
+local function inherits(object, parent)
+ object = getlibrary(object)
+ if type(parent) == "string" then
+ if not AceLibrary:HasInstance(parent) then
+ return false
+ else
+ parent = AceLibrary(parent)
+ end
+ end
+ AceOO:argCheck(parent, 2, "table")
+ if type(object) ~= "table" then
+ return false
+ end
+ local current
+ if object.class then
+ current = object.class
+ else
+ current = object
+ end
+ if type(current) ~= "table" then
+ return false
+ end
+ if rawequal(current, parent) then
+ return true
+ end
+ if parent.class then
+ while true do
+ if rawequal(current, Object) then
+ break
+ end
+ if current.mixins then
+ for mixin in pairs(current.mixins) do
+ if rawequal(mixin, parent) then
+ return true
+ end
+ end
+ end
+ if current.interfaces then
+ for interface in pairs(current.interfaces) do
+ if rawequal(interface, parent) then
+ return true
+ end
+ end
+ end
+ current = current.super
+ if type(current) ~= "table" then
+ break
+ end
+ end
+
+ local isInterface = false
+ local curr = parent.class
+ while true do
+ if rawequal(curr, Object) then
+ break
+ elseif rawequal(curr, Interface) then
+ isInterface = true
+ break
+ end
+ curr = curr.super
+ if type(curr) ~= "table" then
+ break
+ end
+ end
+ return isInterface and validateInterface(object, parent)
+ else
+ while true do
+ if rawequal(current, parent) then
+ return true
+ elseif rawequal(current, Object) then
+ return false
+ end
+ current = current.super
+ if type(current) ~= "table" then
+ return false
+ end
+ end
+ end
+end
+
+-- @table Class
+-- @brief An object factory which sets up inheritence and supports
+-- 'mixins'.
+--
+-- @metamethod Class call
+-- @brief Call ClassFactory:new() to create a new class.
+--
+-- @method Class new
+-- @brief Construct a new object.
+-- @param (a1..a20) Arguments to pass to the object init function.
+-- @return The new object.
+--
+-- @method Class init
+-- @brief Initialize a new class.
+-- @param parent Superclass.
+-- @param (a1..a20) Mixins.
+--
+-- @method Class ToString
+-- @return A string representing the object, in this case 'Class'.
+local initStatus
+local Class
+local Mixin
+local autoEmbed = false
+local function traverseInterfaces(bit, total)
+ if bit.superinterfaces then
+ for interface in pairs(bit.superinterfaces) do
+ if not total[interface] then
+ total[interface] = true
+ traverseInterfaces(interface, total)
+ end
+ end
+ end
+end
+local class_new
+do
+ Class = Factory(Object, setmetatable({}, {__index = Object}), Object)
+ Class.super = Object
+
+ local function protostring(t)
+ return '<' .. tostring(t.class) .. ' prototype>'
+ end
+ local function classobjectstring(t)
+ if t.ToString then
+ return t:ToString()
+ elseif t.GetLibraryVersion then
+ return (t:GetLibraryVersion())
+ else
+ return '<' .. tostring(t.class) .. ' instance>'
+ end
+ end
+ local function classobjectequal(self, other)
+ if type(self) == "table" and self.Equals then
+ return self:Equals(other)
+ elseif type(other) == "table" and other.Equals then
+ return other:Equals(self)
+ elseif type(self) == "table" and self.CompareTo then
+ return self:CompareTo(other) == 0
+ elseif type(other) == "table" and other.CompareTo then
+ return other:CompareTo(self) == 0
+ else
+ return rawequal(self, other)
+ end
+ end
+ local function classobjectlessthan(self, other)
+ if type(self) == "table" and self.IsLessThan then
+ return self:IsLessThan(other)
+ elseif type(other) == "table" and other.IsLessThanOrEqualTo then
+ return not other:IsLessThanOrEqualTo(self)
+ elseif type(self) == "table" and self.CompareTo then
+ return self:CompareTo(other) < 0
+ elseif type(other) == "table" and other.CompareTo then
+ return other:CompareTo(self) > 0
+ elseif type(other) == "table" and other.IsLessThan and other.Equals then
+ return other:Equals(self) or other:IsLessThan(self)
+ else
+ AceOO:error("cannot compare two objects")
+ end
+ end
+ local function classobjectlessthanequal(self, other)
+ if type(self) == "table" and self.IsLessThanOrEqualTo then
+ return self:IsLessThanOrEqualTo(other)
+ elseif type(other) == "table" and other.IsLessThan then
+ return not other:IsLessThan(self)
+ elseif type(self) == "table" and self.CompareTo then
+ return self:CompareTo(other) <= 0
+ elseif type(other) == "table" and other.CompareTo then
+ return other:CompareTo(self) >= 0
+ elseif type(self) == "table" and self.IsLessThan and self.Equals then
+ return self:Equals(other) or self:IsLessThan(other)
+ else
+ AceOO:error("cannot compare two incompatible objects")
+ end
+ end
+ local function classobjectadd(self, other)
+ if type(self) == "table" and self.Add then
+ return self:Add(other)
+ else
+ AceOO:error("cannot add two incompatible objects")
+ end
+ end
+ local function classobjectsub(self, other)
+ if type(self) == "table" and self.Subtract then
+ return self:Subtract(other)
+ else
+ AceOO:error("cannot subtract two incompatible objects")
+ end
+ end
+ local function classobjectunm(self, other)
+ if type(self) == "table" and self.UnaryNegation then
+ return self:UnaryNegation(other)
+ else
+ AceOO:error("attempt to negate an incompatible object")
+ end
+ end
+ local function classobjectmul(self, other)
+ if type(self) == "table" and self.Multiply then
+ return self:Multiply(other)
+ else
+ AceOO:error("cannot multiply two incompatible objects")
+ end
+ end
+ local function classobjectdiv(self, other)
+ if type(self) == "table" and self.Divide then
+ return self:Divide(other)
+ else
+ AceOO:error("cannot divide two incompatible objects")
+ end
+ end
+ local function classobjectpow(self, other)
+ if type(self) == "table" and self.Exponent then
+ return self:Exponent(other)
+ else
+ AceOO:error("cannot exponentiate two incompatible objects")
+ end
+ end
+ local function classobjectconcat(self, other)
+ if type(self) == "table" and self.Concatenate then
+ return self:Concatenate(other)
+ else
+ AceOO:error("cannot concatenate two incompatible objects")
+ end
+ end
+ function class_new(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
+ a13, a14, a15, a16, a17, a18, a19, a20)
+ if self.virtual then
+ AceOO:error("Cannot instantiate a virtual class.")
+ end
+
+ local o = self.prototype
+ local newobj = {}
+ if o.class and o.class.instancemeta then
+ setmetatable(newobj, o.class.instancemeta)
+ else
+ Object:init(newobj, o)
+ end
+
+ if self.interfaces and not self.interfacesVerified then
+ -- Verify the interfaces
+
+ for interface in pairs(self.interfaces) do
+ for field,kind in pairs(interface.interface) do
+ if tostring(type(newobj[field])) ~= kind then
+ AceOO:error("Class did not satisfy all interfaces. %q is required to be a %s. It is a %s", field, kind, tostring(type(newobj[field])))
+ end
+ end
+ end
+ self.interfacesVerified = true
+ end
+ local tmp = initStatus
+ initStatus = newobj
+ newobj:init(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
+ a13, a14, a15, a16, a17, a18, a19, a20)
+ if initStatus then
+ initStatus = tmp
+ AceOO:error("Initialization not completed, be sure to call the superclass's init method.")
+ return
+ end
+ initStatus = tmp
+ return newobj
+ end
+ local classmeta = {
+ __tostring = objtostring,
+ __call = function(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
+ a13, a14, a15, a16, a17, a18, a19, a20)
+ return self:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
+ a13, a14, a15, a16, a17, a18, a19, a20)
+ end,
+ }
+ function Class:init(newclass, parent, a1, a2, a3, a4, a5, a6, a7, a8, a9,
+ a10, a11, a12, a13, a14, a15, a16,
+ a17, a18, a19, a20)
+ parent = parent or self
+
+ local total
+
+ if parent.class then
+ total = {
+ parent, a1, a2, a3, a4, a5, a6, a7, a8, a9,
+ a10, a11, a12, a13, a14, a15, a16,
+ a17, a18, a19, a20
+ }
+ parent = self
+ else
+ total = {
+ a1, a2, a3, a4, a5, a6, a7, a8, a9,
+ a10, a11, a12, a13, a14, a15, a16,
+ a17, a18, a19, a20
+ }
+ end
+ if not inherits(parent, Class) then
+ AceOO:error("Classes must inherit from a proper class")
+ end
+ if parent.sealed then
+ AceOO:error("Cannot inherit from a sealed class")
+ end
+ for i,v in ipairs(total) do
+ if inherits(v, Mixin) and v.class then
+ if not newclass.mixins then
+ newclass.mixins = {}
+ end
+ if newclass.mixins[v] then
+ AceOO:error("Cannot explicitly inherit from the same mixin twice")
+ end
+ newclass.mixins[v] = true
+ elseif inherits(v, Interface) and v.class then
+ if not newclass.interfaces then
+ newclass.interfaces = {}
+ end
+ if newclass.interfaces[v] then
+ AceOO:error("Cannot explicitly inherit from the same interface twice")
+ end
+ newclass.interfaces[v] = true
+ else
+ AceOO:error("Classes can only inherit from one or zero classes and any number of mixins or interfaces")
+ end
+ end
+ if parent.interfaces then
+ if newclass.interfaces then
+ for interface in pairs(parent.interfaces) do
+ newclass.interfaces[interface] = true
+ end
+ else
+ newclass.interfaces = parent.interfaces
+ end
+ end
+ for k in pairs(total) do
+ total[k] = nil
+ end
+ table_setn(total, 0)
+
+ newclass.super = parent
+
+ newclass.prototype = setmetatable(total, {
+ __index = parent.prototype,
+ __tostring = protostring,
+ })
+ total = nil
+
+ newclass.instancemeta = {
+ __index = newclass.prototype,
+ __tostring = classobjectstring,
+ __eq = classobjectequal,
+ __lt = classobjectlessthan,
+ __le = classobjectlessthanequal,
+ __add = classobjectadd,
+ __sub = classobjectsub,
+ __unm = classobjectunm,
+ __mul = classobjectmul,
+ __div = classobjectdiv,
+ __pow = classobjectpow,
+ __concat = classobjectconcat,
+ }
+
+ setmetatable(newclass, classmeta)
+
+ newclass.new = class_new
+
+ if newclass.mixins then
+ -- Fold in the mixins
+ local err, msg
+ for mixin in pairs(newclass.mixins) do
+ local ret
+ autoEmbed = true
+ ret, msg = pcall(mixin.embed, mixin, newclass.prototype)
+ autoEmbed = false
+ if not ret then
+ err = true
+ break
+ end
+ end
+
+ if err then
+ local pt = newclass.prototype
+ for k,v in pairs(pt) do
+ pt[k] = nil
+ end
+
+ -- method conflict
+ AceOO:error(msg)
+ end
+ end
+
+ newclass.prototype.class = newclass
+
+ if newclass.interfaces then
+ for interface in pairs(newclass.interfaces) do
+ traverseInterfaces(interface, newclass.interfaces)
+ end
+ end
+ if newclass.mixins then
+ for mixin in pairs(newclass.mixins) do
+ if mixin.interfaces then
+ if not newclass.interfaces then
+ newclass.interfaces = {}
+ end
+ for interface in pairs(mixin.interfaces) do
+ newclass.interfaces[interface] = true
+ end
+ end
+ end
+ end
+ end
+ function Class:ToString()
+ if type(self.GetLibraryVersion) == "function" then
+ return (self:GetLibraryVersion())
+ else
+ return "Class"
+ end
+ end
+
+ local tmp
+ function Class.prototype:init()
+ if rawequal(self, initStatus) then
+ initStatus = nil
+ else
+ AceOO:error("Improper self passed to init. You must do MyClass.super.prototype.init(self, ...)", 2)
+ end
+ self.uid = getuid(self)
+ local current = self.class
+ while true do
+ if current == Class then
+ break
+ end
+ if current.mixins then
+ for mixin in pairs(current.mixins) do
+ if type(mixin.OnInstanceInit) == "function" then
+ mixin:OnInstanceInit(self)
+ end
+ end
+ end
+ current = current.super
+ end
+ end
+end
+
+
+-- @object ClassFactory
+-- @brief A factory for creating classes. Rarely used directly.
+local ClassFactory = Factory(Object, Class, Object)
+
+function Class:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
+ a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ local x = ClassFactory:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
+ a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ if AceOO.classes then
+ AceOO.classes[x] = true
+ end
+ return x
+end
+getmetatable(Class).__call = Class.new
+
+-- @class Mixin
+-- @brief A class to create mixin objects, which contain methods that get
+-- "mixed in" to class prototypes.
+--
+-- @object Mixin prototype
+-- @brief The prototype that mixin objects inherit their methods from.
+--
+-- @method Mixin prototype embed
+-- @brief Mix in the methods of our object which are listed in our interface
+-- to the supplied target table.
+--
+-- @method Mixin prototype init
+-- @brief Initialize the mixin object.
+-- @param newobj The new object we're initializing.
+-- @param interface The interface we implement (the list of methods our
+-- prototype provides which should be mixed into the target
+-- table by embed).
+do
+ Mixin = Class()
+ function Mixin:ToString()
+ if self.GetLibraryVersion then
+ return (self:GetLibraryVersion())
+ else
+ return 'Mixin'
+ end
+ end
+ local function _Embed(state, field, target)
+ field = next(state.export, field)
+ if field == nil then
+ return
+ end
+
+ if rawget(target, field) or (target[field] and target[field] ~= state[field]) then
+ AceOO:error("Method conflict in attempt to mixin. Field %q", field)
+ end
+
+ target[field] = state[field]
+
+ local ret,msg = pcall(_Embed, state, field, target)
+ if not ret then
+ -- Mix in the next method according to the defined interface. If that
+ -- fails due to a conflict, re-raise to back out the previous mixed
+ -- methods.
+
+ target[field] = nil
+ AceOO:error(msg)
+ end
+ end
+ function Mixin.prototype:embed(target)
+ local mt = getmetatable(target)
+ setmetatable(target, nil)
+ local err, msg = pcall(_Embed, self, nil, target)
+ if not err then
+ setmetatable(target, mt)
+ AceOO:error(msg)
+ return
+ end
+ if type(self.embedList) == "table" then
+ self.embedList[target] = true
+ end
+ if type(target.class) ~= "table" then
+ target[self] = true
+ end
+ if not autoEmbed and type(self.OnManualEmbed) == "function" then
+ if not target.modules then
+ self:OnManualEmbed(target)
+ end
+ end
+ setmetatable(target, mt)
+ end
+
+ function Mixin.prototype:activate(oldLib, oldDeactivate)
+ if oldLib and oldLib.embedList then
+ for target in pairs(oldLib.embedList) do
+ local mt = getmetatable(target)
+ setmetatable(target, nil)
+ for field in pairs(oldLib.export) do
+ target[field] = nil
+ end
+ setmetatable(target, mt)
+ end
+ self.embedList = oldLib.embedList
+ for target in pairs(self.embedList) do
+ self:embed(target)
+ end
+ else
+ self.embedList = setmetatable({}, {__mode="k"})
+ end
+ end
+
+ function Mixin.prototype:init(export, a1, a2, a3, a4, a5, a6, a7, a8, a9,
+ a10, a11, a12, a13, a14, a15, a16,
+ a17, a18, a19, a20)
+ AceOO:argCheck(export, 2, "table")
+ for k,v in pairs(export) do
+ if type(k) ~= "number" then
+ AceOO:error("All keys to argument #2 must be numbers.")
+ elseif type(v) ~= "string" then
+ AceOO:error("All values to argument #2 must be strings.")
+ end
+ end
+ local num = table.getn(export)
+ for i = 1, num do
+ local v = export[i]
+ export[i] = nil
+ export[v] = true
+ end
+ table_setn(export, 0)
+ local interfaces
+ if a1 then
+ local l = getlibrary
+ interfaces = { l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), l(a8),
+ l(a9), l(a10), l(a11), l(a12), l(a13), l(a14), l(a15), l(a16),
+ l(a17), l(a18), l(a19), l(a20) }
+ for _,v in ipairs(interfaces) do
+ if not v.class or not inherits(v, Interface) then
+ AceOO:error("Mixins can inherit only from interfaces")
+ end
+ end
+ local num = table.getn(interfaces)
+ for i = 1, num do
+ local v = interfaces[i]
+ interfaces[i] = nil
+ interfaces[v] = true
+ end
+ table_setn(interfaces, 0)
+ for interface in pairs(interfaces) do
+ traverseInterfaces(interface, interfaces)
+ end
+ for interface in pairs(interfaces) do
+ for field,kind in pairs(interface.interface) do
+ if kind ~= "nil" then
+ local good = false
+ for bit in pairs(export) do
+ if bit == field then
+ good = true
+ break
+ end
+ end
+ if not good then
+ AceOO:error("Mixin does not fully accommodate field %q", field)
+ end
+ end
+ end
+ end
+ end
+ self.super = Mixin.prototype
+ Mixin.super.prototype.init(self)
+ self.export = export
+ self.interfaces = interfaces
+ end
+end
+
+-- @class Interface
+-- @brief A class to create interfaces, which contain contracts that classes
+-- which inherit from this must comply with.
+--
+-- @object Interface prototype
+-- @brief The prototype that interface objects must adhere to.
+--
+-- @method Interface prototype init
+-- @brief Initialize the mixin object.
+-- @param interface The interface we contract (the hash of fields forced).
+-- @param (a1..a20) Superinterfaces
+do
+ Interface = Class()
+ function Interface:ToString()
+ if self.GetLibraryVersion then
+ return (self:GetLibraryVersion())
+ else
+ return 'Instance'
+ end
+ end
+ function Interface.prototype:init(interface, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ Interface.super.prototype.init(self)
+ AceOO:argCheck(interface, 2, "table")
+ for k,v in pairs(interface) do
+ if type(k) ~= "string" then
+ AceOO:error("All keys to argument #2 must be numbers.")
+ elseif type(v) ~= "string" then
+ AceOO:error("All values to argument #2 must be strings.")
+ elseif v ~= "nil" and v ~= "string" and v ~= "number" and v ~= "table" and v ~= "function" then
+ AceOO:error('All values to argument #2 must either be "nil", "string", "number", "table", or "function".')
+ end
+ end
+ local l = getlibrary
+ a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), l(a8), l(a9), l(a10), l(a11), l(a12), l(a13), l(a14), l(a15), l(a16), l(a17), l(a18), l(a19), l(a20)
+ if a1 then
+ self.superinterfaces = {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20}
+ for k,v in ipairs(self.superinterfaces) do
+ if not inherits(v, Interface) or not v.class then
+ AceOO:error('Cannot provide a non-Interface to inherit from')
+ end
+ end
+ local num = table.getn(self.superinterfaces)
+ for i = 1, num do
+ local v = self.superinterfaces[i]
+ self.superinterfaces[i] = nil
+ self.superinterfaces[v] = true
+ end
+ table_setn(self.superinterfaces, 0)
+ end
+ self.interface = interface
+ end
+end
+
+-- @function Classpool
+-- @brief Obtain a read only class from our pool of classes, indexed by the
+-- superclass and mixins.
+-- @param sc The superclass of the class we want.
+-- @param (m1..m20) Mixins of the class we want's objects.
+-- @return A read only class from the class pool.
+local Classpool
+do
+ local pool = setmetatable({}, {__mode = 'v'})
+ local function newindex(k, v)
+ AceOO:error('Attempt to modify a read-only class.')
+ end
+ local function protonewindex(k, v)
+ AceOO:error('Attempt to modify a read-only class prototype.')
+ end
+ local function ts(bit)
+ if type(bit) ~= "table" then
+ return tostring(bit)
+ elseif getmetatable(bit) and bit.__tostring then
+ return tostring(bit)
+ elseif type(bit.GetLibraryVersion) == "function" then
+ return bit:GetLibraryVersion()
+ else
+ return tostring(bit)
+ end
+ end
+ local t
+ local function getcomplexuid(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9,
+ m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20)
+ if not t then t = {} end
+ if sc then if sc.uid then table.insert(t, sc.uid) else AceOO:error("%s is not an appropriate class/mixin", ts(sc)) end
+ if m1 then if m1.uid then table.insert(t, m1.uid) else AceOO:error("%s is not an appropriate mixin", ts(m1)) end
+ if m2 then if m2.uid then table.insert(t, m2.uid) else AceOO:error("%s is not an appropriate mixin", ts(m2)) end
+ if m3 then if m3.uid then table.insert(t, m3.uid) else AceOO:error("%s is not an appropriate mixin", ts(m3)) end
+ if m4 then if m4.uid then table.insert(t, m4.uid) else AceOO:error("%s is not an appropriate mixin", ts(m4)) end
+ if m5 then if m5.uid then table.insert(t, m5.uid) else AceOO:error("%s is not an appropriate mixin", ts(m5)) end
+ if m6 then if m6.uid then table.insert(t, m6.uid) else AceOO:error("%s is not an appropriate mixin", ts(m6)) end
+ if m7 then if m7.uid then table.insert(t, m7.uid) else AceOO:error("%s is not an appropriate mixin", ts(m7)) end
+ if m8 then if m8.uid then table.insert(t, m8.uid) else AceOO:error("%s is not an appropriate mixin", ts(m8)) end
+ if m9 then if m9.uid then table.insert(t, m9.uid) else AceOO:error("%s is not an appropriate mixin", ts(m9)) end
+ if m10 then if m10.uid then table.insert(t, m10.uid) else AceOO:error("%s is not an appropriate mixin", ts(m10)) end
+ if m11 then if m11.uid then table.insert(t, m11.uid) else AceOO:error("%s is not an appropriate mixin", ts(m11)) end
+ if m12 then if m12.uid then table.insert(t, m12.uid) else AceOO:error("%s is not an appropriate mixin", ts(m12)) end
+ if m13 then if m13.uid then table.insert(t, m13.uid) else AceOO:error("%s is not an appropriate mixin", ts(m13)) end
+ if m14 then if m14.uid then table.insert(t, m14.uid) else AceOO:error("%s is not an appropriate mixin", ts(m14)) end
+ if m15 then if m15.uid then table.insert(t, m15.uid) else AceOO:error("%s is not an appropriate mixin", ts(m15)) end
+ if m16 then if m16.uid then table.insert(t, m16.uid) else AceOO:error("%s is not an appropriate mixin", ts(m16)) end
+ if m17 then if m17.uid then table.insert(t, m17.uid) else AceOO:error("%s is not an appropriate mixin", ts(m17)) end
+ if m18 then if m18.uid then table.insert(t, m18.uid) else AceOO:error("%s is not an appropriate mixin", ts(m18)) end
+ if m19 then if m19.uid then table.insert(t, m19.uid) else AceOO:error("%s is not an appropriate mixin", ts(m19)) end
+ if m20 then if m20.uid then table.insert(t, m20.uid) else AceOO:error("%s is not an appropriate mixin", ts(m20)) end
+ end end end end end end end end end end end end end end end end end end end end end
+ table.sort(t)
+ local uid = table.concat(t, '')
+ for k in pairs(t) do t[k] = nil end
+ table_setn(t, 0)
+ return uid
+ end
+ local classmeta
+ function Classpool(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9,
+ m10, m11, m12, m13, m14, m15, m16,
+ m17, m18, m19, m20)
+ local l = getlibrary
+ sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20 = l(sc), l(m1), l(m2), l(m3), l(m4), l(m5), l(m6), l(m7), l(m8), l(m9), l(m10), l(m11), l(m12), l(m13), l(m14), l(m15), l(m16), l(m17), l(m18), l(m19), l(m20)
+ if sc and sc.class then
+ sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20 = Class, sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19
+ end
+ sc = sc or Class
+ local key = getcomplexuid(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20)
+ if not pool[key] then
+ local class = Class(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9,
+ m10, m11, m12, m13, m14, m15, m16, m17,
+ m18, m19, m20)
+ if not classmeta then
+ classmeta = {}
+ local mt = getmetatable(class)
+ for k,v in pairs(mt) do
+ classmeta[k] = v
+ end
+ classmeta.__newindex = newindex
+ end
+ -- Prevent the user from adding methods to this class.
+ -- NOTE: I'm not preventing modifications of existing class members,
+ -- but it's likely that only a truly malicious user will be doing so.
+ class.sealed = true
+ setmetatable(class, classmeta)
+ getmetatable(class.prototype).__newindex = protonewindex
+ pool[key] = class
+ end
+ return pool[key]
+ end
+end
+
+AceOO.Factory = Factory
+AceOO.Object = Object
+AceOO.Class = Class
+AceOO.Mixin = Mixin
+AceOO.Interface = Interface
+AceOO.Classpool = Classpool
+AceOO.inherits = inherits
+
+-- Library handling bits
+
+local function activate(self, oldLib, oldDeactivate)
+ AceOO = self
+ Factory = self.Factory
+ Object = self.Object
+ Class = self.Class
+ ClassFactory.prototype = Class
+ Mixin = self.Mixin
+ Interface = self.Interface
+ Classpool = self.Classpool
+
+ if oldLib then
+ self.classes = oldLib.classes
+ end
+ if not self.classes then
+ self.classes = setmetatable({}, {__mode="k"})
+ else
+ for class in pairs(self.classes) do
+ class.new = class_new
+ end
+ end
+
+ if oldDeactivate then
+ oldDeactivate(oldLib)
+ end
+end
+
+AceLibrary:Register(AceOO, MAJOR_VERSION, MINOR_VERSION, activate)
+AceOO = AceLibrary(MAJOR_VERSION)
diff --git a/Localization.lua b/Localization.lua
index 115faf7..ed1d67c 100644
--- a/Localization.lua
+++ b/Localization.lua
@@ -1,172 +1,246 @@
-BCS = BCS or {}
-
-BCS["L"] = {
-
- ["([%d.]+)%% chance to crit"] = "([%d.]+)%% chance to crit",
-
- ["^Set: Improves your chance to hit by (%d)%%."] = "^Set: Improves your chance to hit by (%d)%%.",
- ["^Set: Improves your chance to get a critical strike with spells by (%d)%%."] = "^Set: Improves your chance to get a critical strike with spells by (%d)%%.",
- ["^Set: Improves your chance to hit with spells by (%d)%%."] = "^Set: Improves your chance to hit with spells by (%d)%%.",
- ["^Set: Increases damage and healing done by magical spells and effects by up to (%d+)%."] = "^Set: Increases damage and healing done by magical spells and effects by up to (%d+)%.",
- ["^Set: Increases healing done by spells and effects by up to (%d+)%."] = "^Set: Increases healing done by spells and effects by up to (%d+)%.",
- ["^Set: Allows (%d+)%% of your Mana regeneration to continue while casting."] = "^Set: Allows (%d+)%% of your Mana regeneration to continue while casting.",
-
- ["Equip: Improves your chance to hit by (%d)%%."] = "Equip: Improves your chance to hit by (%d)%%.",
- ["Equip: Improves your chance to get a critical strike with spells by (%d)%%."] = "Equip: Improves your chance to get a critical strike with spells by (%d)%%.",
- ["Equip: Improves your chance to hit with spells by (%d)%%."] = "Equip: Improves your chance to hit with spells by (%d)%%.",
-
- ["Increases your chance to hit with melee weapons by (%d)%%."] = "Increases your chance to hit with melee weapons by (%d)%%.",
- ["Increases your critical strike chance with ranged weapons by (%d)%%."] = "Increases your critical strike chance with ranged weapons by (%d)%%.",
- ["Increases hit chance by (%d)%% and increases the chance movement impairing effects will be resisted by an additional %d+%%."] = "Increases hit chance by (%d)%% and increases the chance movement impairing effects will be resisted by an additional %d+%%.",
- ["Increases your critical strike chance with all attacks by (%d)%%."] = "Increases your critical strike chance with all attacks by (%d)%%.",
- ["Increases spell damage and healing by up to (%d+)%% of your total Spirit."] = "Increases spell damage and healing by up to (%d+)%% of your total Spirit.",
- ["Allows (%d+)%% of your Mana regeneration to continue while casting."] = "Allows (%d+)%% of your Mana regeneration to continue while casting.",
- ["Reduces the chance that the opponent can resist your Frost and Fire spells by (%d)%%."] = "Reduces the chance that the opponent can resist your Frost and Fire spells by (%d)%%.",
- ["Reduces the chance that the opponent can resist your Arcane spells by (%d+)%%."] = "Reduces the chance that the opponent can resist your Arcane spells by (%d+)%%.",
- ["Reduces your target's chance to resist your Shadow spells by (%d+)%%."] = "Reduces your target's chance to resist your Shadow spells by (%d+)%%.",
-
- ["Equip: Increases damage done by Arcane spells and effects by up to (%d+)."] = "Equip: Increases damage done by Arcane spells and effects by up to (%d+).",
- ["Equip: Increases damage done by Fire spells and effects by up to (%d+)."] = "Equip: Increases damage done by Fire spells and effects by up to (%d+).",
- ["Equip: Increases damage done by Frost spells and effects by up to (%d+)."] = "Equip: Increases damage done by Frost spells and effects by up to (%d+).",
- ["Equip: Increases damage done by Holy spells and effects by up to (%d+)."] = "Equip: Increases damage done by Holy spells and effects by up to (%d+).",
- ["Equip: Increases damage done by Nature spells and effects by up to (%d+)."] = "Equip: Increases damage done by Nature spells and effects by up to (%d+).",
- ["Equip: Increases damage done by Shadow spells and effects by up to (%d+)."] = "Equip: Increases damage done by Shadow spells and effects by up to (%d+).",
-
- ["Shadow Damage %+(%d+)"] = "Shadow Damage %+(%d+)",
- ["Spell Damage %+(%d+)"] = "Spell Damage %+(%d+)",
- ["Fire Damage %+(%d+)"] = "Fire Damage %+(%d+)",
- ["Frost Damage %+(%d+)"] = "Frost Damage %+(%d+)",
- ["Healing Spells %+(%d+)"] = "Healing Spells %+(%d+)",
- ["^Healing %+(%d+) and %d+ mana per 5 sec."] = "^Healing %+(%d+) and %d+ mana per 5 sec.",
-
- ["Equip: Restores (%d+) mana per 5 sec."] = "Equip: Restores (%d+) mana per 5 sec.",
- ["+(%d)%% Hit"] = "+(%d)%% Hit",
-
- -- Random Bonuses // https://wow.gamepedia.com/index.php?title=SuffixId&oldid=204406
- ["^%+(%d+) Damage and Healing Spells"] = "^%+(%d+) Damage and Healing Spells",
- ["^%+(%d+) Arcane Spell Damage"] = "^%+(%d+) Arcane Spell Damage",
- ["^%+(%d+) Fire Spell Damage"] = "^%+(%d+) Fire Spell Damage",
- ["^%+(%d+) Frost Spell Damage"] = "^%+(%d+) Frost Spell Damage",
- ["^%+(%d+) Holy Spell Damage"] = "^%+(%d+) Holy Spell Damage",
- ["^%+(%d+) Nature Spell Damage"] = "^%+(%d+) Nature Spell Damage",
- ["^%+(%d+) Shadow Spell Damage"] = "^%+(%d+) Shadow Spell Damage",
- ["^%+(%d+) mana every 5 sec."] = "^%+(%d+) mana every 5 sec.",
- ["Restores (%d+) mana every 1 sec."] = "Restores (%d+) mana every 1 sec.",
- ["(%d+)%% of your Mana regeneration continuing while casting."] = "(%d+)%% of your Mana regeneration continuing while casting.",
-
-
- -- Mana Oils
- ["^Brilliant Mana Oil %((%d+) min%"] = "^Brilliant Mana Oil %((%d+) min%",
- ["^Lesser Mana Oil ((%d+) min)"] = "^Lesser Mana Oil ((%d+) min)",
- ["^Minor Mana Oil ((%d+) min)"] = "^Minor Mana Oil ((%d+) min)",
-
- -- snowflakes ZG enchants
- ["/Hit %+(%d+)"] = "/Hit %+(%d+)",
- ["/Spell Hit %+(%d+)"] = "/Spell Hit %+(%d+)",
- ["^Mana Regen %+(%d+)"] = "^Mana Regen %+(%d+)",
- ["^Healing %+%d+ and (%d+) mana per 5 sec."] = "^Healing %+%d+ and (%d+) mana per 5 sec.",
- ["^%+(%d+) Healing Spells"] = "^%+(%d+) Healing Spells",
- ["^%+(%d+) Spell Damage and Healing"] = "^%+(%d+) Spell Damage and Healing",
-
- -- Mana Oils
- ["^Brilliant Mana Oil %((%d+) min%"] = "^Brilliant Mana Oil %((%d+) min%",
-
- ["Equip: Increases damage and healing done by magical spells and effects by up to (%d+)."] = "Equip: Increases damage and healing done by magical spells and effects by up to (%d+).",
- ["Equip: Increases healing done by spells and effects by up to (%d+)."] = "Equip: Increases healing done by spells and effects by up to (%d+).",
-
- -- auras
- ["Chance to hit increased by (%d)%%."] = "Chance to hit increased by (%d)%%.",
- ["Magical damage dealt is increased by up to (%d+)."] = "Magical damage dealt is increased by up to (%d+).",
- ["Healing done by magical spells is increased by up to (%d+)."] = "Healing done by magical spells is increased by up to (%d+).",
- ["Increases healing done by magical spells by up to (%d+) for 3600 sec."] = "Increases healing done by magical spells by up to (%d+) for 3600 sec.",
- ["Healing increased by up to (%d+)."] = "Healing increased by up to (%d+).",
- ["Healing spells increased by up to (%d+)."] = "Healing spells increased by up to (%d+).",
- ["Chance to hit reduced by (%d+)%%."] = "Chance to hit reduced by (%d+)%%.",
- ["Chance to hit decreased by (%d+)%% and %d+ Nature damage every %d+ sec."] = "Chance to hit decreased by (%d+)%% and %d+ Nature damage every %d+ sec.",
- ["Lowered chance to hit."] = "Lowered chance to hit.", -- 5917 Fumble (25%)
- ["Increases hitpoints by 300. 15%% haste to melee attacks. (%d+) mana regen every 5 seconds."] = "Increases hitpoints by 300. 15%% haste to melee attacks. (%d+) mana regen every 5 seconds.",
- ["Restores (%d+) mana per 5 sec."] = "Restores (%d+) mana per 5 sec.",
- ["Regenerating (%d+) Mana every 5 seconds."] = "Regenerating (%d+) Mana every 5 seconds.",
- ["Regenerate (%d+) mana per 5 sec."] = "Regenerate (%d+) mana per 5 sec.",
- ["Mana Regeneration increased by (%d+) every 5 seconds."] = "Mana Regeneration increased by (%d+) every 5 seconds.",
- ["Improves your chance to hit by (%d+)%%."] = "Improves your chance to hit by (%d+)%%.",
- ["Chance for a critical hit with a spell increased by (%d+)%%."] = "Chance for a critical hit with a spell increased by (%d+)%%.",
- ["While active, target's critical hit chance with spells and attacks increases by 10%%."] = "While active, target's critical hit chance with spells and attacks increases by 10%%.",
- ["Increases attack power by %d+ and chance to hit by (%d+)%%."] = "Increases attack power by %d+ and chance to hit by (%d+)%%.",
- ["Holy spell critical hit chance increased by (%d+)%%."] = "Holy spell critical hit chance increased by (%d+)%%.",
- ["Destruction spell critical hit chance increased by (%d+)%%."] = "Destruction spell critical hit chance increased by (%d+)%%.",
- ["Arcane spell critical hit chance increased by (%d+)%%.\r\nArcane spell critical hit damage increased by (%d+)%%."] = "Arcane spell critical hit chance increased by (%d+)%%.\r\nArcane spell critical hit damage increased by (%d+)%%.",
- ["Spell hit chance increased by (%d+)%%."] = "Spell hit chance increased by (%d+)%%.",
-
- ["Increases chance for a melee, ranged, or spell critical by (%d+)%% and all attributes by %d+."] = "Increases chance for a melee, ranged, or spell critical by (%d+)%% and all attributes by %d+.",
- ["Melee critical-hit chance reduced by (%d+)%%.\r\nSpell critical-hit chance reduced by (%d+)%%."] = "Melee critical-hit chance reduced by (%d+)%%.\r\nSpell critical-hit chance reduced by (%d+)%%.",
- ["Increases critical chance of spells by 10%%, melee and ranged by 5%% and grants 140 attack power. 120 minute duration."] = "Increases critical chance of spells by 10%%, melee and ranged by 5%% and grants 140 attack power. 120 minute duration.",
- ["Holy spell critical hit chance increased by (%d+)%%."] = "Holy spell critical hit chance increased by (%d+)%%.",
- ["Destruction spell critical hit chance increased by (%d+)%%."] = "Destruction spell critical hit chance increased by (%d+)%%.",
- ["Critical strike chance with spells and melee attacks increased by (%d+)%%."] = "Critical strike chance with spells and melee attacks increased by (%d+)%%.",
-
-
- ["MELEE_HIT_TOOLTIP"] = [[|cffffffffHit|r
- Result of an attack made with
- melee or ranged weapons.]],
-
- ["SPELL_HIT_TOOLTIP"] = [[|cffffffffHit|r
- Result of an attack made with
- spells.]],
-
- ["SPELL_HIT_SECONDARY_TOOLTIP"] = [[|cffffffffHit %d%% (%d%%|cff20ff20+%d%% %s|r|cffffffff)|r
- Result of an attack made with
- spells.]],
-
- ["SPELL_POWER_TOOLTIP"] = [[|cffffffffSpell Power %d|r
- Increases damage done by spells and effects.]],
-
- ["SPELL_POWER_SECONDARY_TOOLTIP"] = [[|cffffffffSpell Power %d (%d|cff20ff20+%d %s|r|cffffffff)|r
- Increases damage done by spells and effects.]],
-
- ["SPELL_HEALING_POWER_TOOLTIP"] = [[|cffffffffHealing Power %d (%d|cff20ff20+%d|r|cffffffff)|r
- Increases healing done by spells and effects.]],
-
- ["SPELL_MANA_REGEN_TOOLTIP"] = [[|cffffffffMana regen: %d |cffBF40BF(%d)|r
- Mana regen when not casting and |cffBF40BF(while casting)|r.
- Mana regenerates every 2 seconds and the amount
- is dependent on your total spirit and MP5.
- Spirit Regen: |cff7DF9FF%d|r
- %%Regen while casting: |cffBF40BF%d%%|r
- MP5 Regen: |cff20ff20%d|r
- MP5 Regen (2s): |cff20ff20%d|r]],
-
- ["ROGUE_MELEE_HIT_TOOLTIP"] = [[
-+5% hit to always hit enemy players.
-+8% hit to always hit with your special abilities against a raid boss.
-+24.6% hit to always hit a raid boss.]],
-
- PLAYERSTAT_BASE_STATS = "Base Stats",
- PLAYERSTAT_DEFENSES = "Defenses",
- PLAYERSTAT_MELEE_COMBAT = "Melee",
- PLAYERSTAT_RANGED_COMBAT = "Ranged",
- PLAYERSTAT_SPELL_COMBAT = "Spell",
- PLAYERSTAT_SPELL_SCHOOLS = "Schools",
-
- MELEE_HIT_RATING_COLON = "Hit Rating:",
- RANGED_HIT_RATING_COLON = "Hit Rating:",
- SPELL_HIT_RATING_COLON = "Hit Rating:",
- MELEE_CRIT_COLON = "Crit Chance:",
- RANGED_CRIT_COLON = "Crit Chance:",
- SPELL_CRIT_COLON = "Crit Chance:",
- MANA_REGEN_COLON = "Mana regen:",
- HEAL_POWER_COLON = "Healing:",
- DODGE_COLON = DODGE .. ":",
- PARRY_COLON = PARRY .. ":",
- BLOCK_COLON = BLOCK .. ":",
-
- SPELL_POWER_COLON = "Power:",
-
- SPELL_SCHOOL_ARCANE = "Arcane",
- SPELL_SCHOOL_FIRE = "Fire",
- SPELL_SCHOOL_FROST = "Frost",
- SPELL_SCHOOL_HOLY = "Holy",
- SPELL_SCHOOL_NATURE = "Nature",
- SPELL_SCHOOL_SHADOW = "Shadow",
-
-}
\ No newline at end of file
+BCS = BCS or {}
+
+BCS["L"] = {
+ ["%+(%d+)%% Critical Strike"] = "%+(%d+)%% Critical Strike",
+ ["([%d.]+)%% chance to crit"] = "([%d.]+)%% chance to crit",
+
+ ["^Set: Improves your chance to hit by (%d)%%."] = "^Set: Improves your chance to hit by (%d)%%.",
+ ["^Set: Improves your chance to get a critical strike with spells by (%d)%%."] = "^Set: Improves your chance to get a critical strike with spells by (%d)%%.",
+ ["^Set: Improves your chance to hit with spells by (%d)%%."] = "^Set: Improves your chance to hit with spells by (%d)%%.",
+ ["^Set: Increases damage and healing done by magical spells and effects by up to (%d+)%."] = "^Set: Increases damage and healing done by magical spells and effects by up to (%d+)%.",
+ ["^Set: Increases healing done by spells and effects by up to (%d+)%."] = "^Set: Increases healing done by spells and effects by up to (%d+)%.",
+ ["^Set: Allows (%d+)%% of your Mana regeneration to continue while casting."] = "^Set: Allows (%d+)%% of your Mana regeneration to continue while casting.",
+ ["^Set: Improves your chance to get a critical strike by (%d)%%."] = "^Set: Improves your chance to get a critical strike by (%d)%%.",
+
+ ["Equip: Improves your chance to hit by (%d)%%."] = "Equip: Improves your chance to hit by (%d)%%.",
+ ["Equip: Improves your chance to get a critical strike with spells by (%d)%%."] = "Equip: Improves your chance to get a critical strike with spells by (%d)%%.",
+ ["Equip: Improves your chance to hit with spells by (%d)%%."] = "Equip: Improves your chance to hit with spells by (%d)%%.",
+ ["Equip: Improves your chance to get a critical strike by (%d)%%."] = "Equip: Improves your chance to get a critical strike by (%d)%%.",
+ ["Increases your chance to hit with melee weapons by (%d)%%."] = "Increases your chance to hit with melee weapons by (%d)%%.",
+ ["Increases your critical strike chance with ranged weapons by (%d)%%."] = "Increases your critical strike chance with ranged weapons by (%d)%%.",
+ ["Increases hit chance by (%d)%% and increases the chance movement impairing effects will be resisted by an additional %d+%%."] = "Increases hit chance by (%d)%% and increases the chance movement impairing effects will be resisted by an additional %d+%%.",
+ ["Increases your critical strike chance with all attacks by (%d)%%."] = "Increases your critical strike chance with all attacks by (%d)%%.",
+ ["Increases spell damage and healing by up to (%d+)%% of your total Spirit."] = "Increases spell damage and healing by up to (%d+)%% of your total Spirit.",
+ ["Allows (%d+)%% of your Mana regeneration to continue while casting."] = "Allows (%d+)%% of your Mana regeneration to continue while casting.",
+ ["Reduces the chance that the opponent can resist your Frost and Fire spells by (%d)%%."] = "Reduces the chance that the opponent can resist your Frost and Fire spells by (%d)%%.",
+ ["Reduces the chance that the opponent can resist your Arcane spells by (%d+)%%."] = "Reduces the chance that the opponent can resist your Arcane spells by (%d+)%%.",
+ ["Reduces your target's chance to resist your Shadow spells by (%d+)%%."] = "Reduces your target's chance to resist your Shadow spells by (%d+)%%.",
+
+ ["Equip: Increases damage done by Arcane spells and effects by up to (%d+)."] = "Equip: Increases damage done by Arcane spells and effects by up to (%d+).",
+ ["Equip: Increases damage done by Fire spells and effects by up to (%d+)."] = "Equip: Increases damage done by Fire spells and effects by up to (%d+).",
+ ["Equip: Increases damage done by Frost spells and effects by up to (%d+)."] = "Equip: Increases damage done by Frost spells and effects by up to (%d+).",
+ ["Equip: Increases damage done by Holy spells and effects by up to (%d+)."] = "Equip: Increases damage done by Holy spells and effects by up to (%d+).",
+ ["Equip: Increases damage done by Nature spells and effects by up to (%d+)."] = "Equip: Increases damage done by Nature spells and effects by up to (%d+).",
+ ["Equip: Increases damage done by Shadow spells and effects by up to (%d+)."] = "Equip: Increases damage done by Shadow spells and effects by up to (%d+).",
+
+ ["Shadow Damage %+(%d+)"] = "Shadow Damage %+(%d+)",
+ ["Spell Damage %+(%d+)"] = "Spell Damage %+(%d+)",
+ ["Arcane Damage %+(%d+)"] = "Arcane Damage %+(%d+)",
+ ["Fire Damage %+(%d+)"] = "Fire Damage %+(%d+)",
+ ["Frost Damage %+(%d+)"] = "Frost Damage %+(%d+)",
+ ["Healing Spells %+(%d+)"] = "Healing Spells %+(%d+)",
+ ["^Healing %+(%d+) and %d+ mana per 5 sec."] = "^Healing %+(%d+) and %d+ mana per 5 sec.",
+
+ ["Equip: Restores (%d+) mana per 5 sec."] = "Equip: Restores (%d+) mana per 5 sec.",
+ ["+(%d)%% Ranged Hit"] = "+(%d)%% Ranged Hit",
+
+ -- Random Bonuses // https://wow.gamepedia.com/index.php?title=SuffixId&oldid=204406
+ ["^%+(%d+) Damage and Healing Spells"] = "^%+(%d+) Damage and Healing Spells",
+ ["^%+(%d+) Arcane Spell Damage"] = "^%+(%d+) Arcane Spell Damage",
+ ["^%+(%d+) Fire Spell Damage"] = "^%+(%d+) Fire Spell Damage",
+ ["^%+(%d+) Frost Spell Damage"] = "^%+(%d+) Frost Spell Damage",
+ ["^%+(%d+) Holy Spell Damage"] = "^%+(%d+) Holy Spell Damage",
+ ["^%+(%d+) Nature Spell Damage"] = "^%+(%d+) Nature Spell Damage",
+ ["^%+(%d+) Shadow Spell Damage"] = "^%+(%d+) Shadow Spell Damage",
+ ["^%+(%d+) mana every 5 sec."] = "^%+(%d+) mana every 5 sec.",
+ ["Restores (%d+) mana every 1 sec."] = "Restores (%d+) mana every 1 sec.",
+ ["(%d+)%% of your Mana regeneration continuing while casting."] = "(%d+)%% of your Mana regeneration continuing while casting.",
+ ["(%d+)%% of your mana regeneration to continue while casting."] = "(%d+)%% of your mana regeneration to continue while casting.",
+
+ -- Mana Oils
+ ["^Brilliant Mana Oil %((%d+) min%"] = "^Brilliant Mana Oil %((%d+) min%",
+ ["^Lesser Mana Oil ((%d+) min)"] = "^Lesser Mana Oil ((%d+) min)",
+ ["^Minor Mana Oil ((%d+) min)"] = "^Minor Mana Oil ((%d+) min)",
+
+ -- snowflakes ZG enchants
+ ["/Hit %+(%d+)"] = "/Hit %+(%d+)",
+ ["/Spell Hit %+(%d+)"] = "/Spell Hit %+(%d+)",
+ ["^Mana Regen %+(%d+)"] = "^Mana Regen %+(%d+)",
+ ["^Healing %+%d+ and (%d+) mana per 5 sec."] = "^Healing %+%d+ and (%d+) mana per 5 sec.",
+ ["^%+(%d+) Healing Spells"] = "^%+(%d+) Healing Spells",
+ ["^%+(%d+) Spell Damage and Healing"] = "^%+(%d+) Spell Damage and Healing",
+
+ -- sapphiron shoulder enchants
+ ["%+(%d+)%% Critical Strike"] = "%+(%d+)%% Critical Strike",
+
+ ["Equip: Increases damage and healing done by magical spells and effects by up to (%d+)."] = "Equip: Increases damage and healing done by magical spells and effects by up to (%d+).",
+ ["Equip: Increases healing done by spells and effects by up to (%d+)."] = "Equip: Increases healing done by spells and effects by up to (%d+).",
+
+ -- auras
+ ["Chance to hit increased by (%d)%%."] = "Chance to hit increased by (%d)%%.",
+ ["Magical damage dealt is increased by up to (%d+)."] = "Magical damage dealt is increased by up to (%d+).",
+ ["Healing done by magical spells is increased by up to (%d+)."] = "Healing done by magical spells is increased by up to (%d+).",
+ ["Increases healing done by magical spells by up to (%d+) for 3600 sec."] = "Increases healing done by magical spells by up to (%d+) for 3600 sec.",
+ ["Healing increased by up to (%d+)."] = "Healing increased by up to (%d+).",
+ ["Healing spells increased by up to (%d+)."] = "Healing spells increased by up to (%d+).",
+ ["Chance to hit reduced by (%d+)%%."] = "Chance to hit reduced by (%d+)%%.",
+ ["Chance to hit decreased by (%d+)%% and %d+ Nature damage every %d+ sec."] = "Chance to hit decreased by (%d+)%% and %d+ Nature damage every %d+ sec.",
+ ["Lowered chance to hit."] = "Lowered chance to hit.", -- 5917 Fumble (25%)
+ ["Increases hitpoints by 300. 15%% haste to melee attacks. (%d+) mana regen every 5 seconds."] = "Increases hitpoints by 300. 15%% haste to melee attacks. (%d+) mana regen every 5 seconds.",
+ ["Restores (%d+) mana per 5 sec."] = "Restores (%d+) mana per 5 sec.",
+ ["Regenerating (%d+) Mana every 5 seconds."] = "Regenerating (%d+) Mana every 5 seconds.",
+ ["Regenerate (%d+) mana per 5 sec."] = "Regenerate (%d+) mana per 5 sec.",
+ ["Mana Regeneration increased by (%d+) every 5 seconds."] = "Mana Regeneration increased by (%d+) every 5 seconds.",
+ ["Improves your chance to hit by (%d+)%%."] = "Improves your chance to hit by (%d+)%%.",
+ ["Chance for a critical hit with a spell increased by (%d+)%%."] = "Chance for a critical hit with a spell increased by (%d+)%%.",
+ ["While active, target's critical hit chance with spells and attacks increases by 10%%."] = "While active, target's critical hit chance with spells and attacks increases by 10%%.", --??
+ ["Increases attack power by %d+ and chance to hit by (%d+)%%."] = "Increases attack power by %d+ and chance to hit by (%d+)%%.",
+ ["Holy spell critical hit chance increased by (%d+)%%."] = "Holy spell critical hit chance increased by (%d+)%%.",
+ ["Destruction spell critical hit chance increased by (%d+)%%."] = "Destruction spell critical hit chance increased by (%d+)%%.",
+ ["Arcane spell critical hit chance increased by (%d+)%%.\r\nArcane spell critical hit damage increased by (%d+)%%."] = "Arcane spell critical hit chance increased by (%d+)%%.\r\nArcane spell critical hit damage increased by (%d+)%%.",
+ ["Spell hit chance increased by (%d+)%%."] = "Spell hit chance increased by (%d+)%%.",
+ ["Agility increased by 25, Critical hit chance increases by (%d)%%."] = "Agility increased by 25, Critical hit chance increases by (%d)%%.",
+ ["Increases chance for a melee, ranged, or spell critical by (%d+)%% and all attributes by %d+."] = "Increases chance for a melee, ranged, or spell critical by (%d+)%% and all attributes by %d+.",
+ ["Melee critical-hit chance reduced by (%d+)%%.\r\nSpell critical-hit chance reduced by (%d+)%%."] = "Melee critical-hit chance reduced by (%d+)%%.\r\nSpell critical-hit chance reduced by (%d+)%%.",
+ ["Increases critical chance of spells by 10%%, melee and ranged by 5%% and grants 140 attack power. 120 minute duration."] = "Increases critical chance of spells by 10%%, melee and ranged by 5%% and grants 140 attack power. 120 minute duration.",
+ ["Critical strike chance with spells and melee attacks increased by (%d+)%%."] = "Critical strike chance with spells and melee attacks increased by (%d+)%%.",
+ ["Increases ranged and melee critical chance by (%d+)%%."] = "Increases ranged and melee critical chance by (%d+)%%.",
+ ["Equip: Increases the critical chance provided by Leader of the Pack and Moonkin Aura by (%d)%%."] = "Equip: Increases the critical chance provided by Leader of the Pack and Moonkin Aura by (%d)%%.",
+ -- druid
+ ["Increases the damage and critical strike chance of your Moonfire spell by (%d+)%%."] = "Increases the damage and critical strike chance of your Moonfire spell by (%d+)%%.",
+ ["Increases the critical effect chance of your Regrowth spell by (%d+)%%."] = "Increases the critical effect chance of your Regrowth spell by (%d+)%%.",
+ -- paladin
+ ["Increases your healing power by (%d+)%% of your Armor."] = "Increases your healing power by (%d+)%% of your Armor.",
+ ["Increases the critical effect chance of your Holy Light and Flash of Light by (%d+)%%."] = "Increases the critical effect chance of your Holy Light and Flash of Light by (%d+)%%.",
+ ["Improves your chance to get a critical strike with Holy Shock by (%d+)%%."] = "Improves your chance to get a critical strike with Holy Shock by (%d+)%%.",
+ -- shaman
+ ["Increases the critical strike chance of your Lightning Bolt and Chain Lightning spells by an additional (%d+)%%."] = "Increases the critical strike chance of your Lightning Bolt and Chain Lightning spells by an additional (%d+)%%.",
+ ["Increases the critical effect chance of your healing and lightning spells by (%d+)%%."] = "Increases the critical effect chance of your healing and lightning spells by (%d+)%%.",
+ -- warlock
+ ["Increases the critical strike chance of your Destruction spells by (%d+)%%."] = "Increases the critical strike chance of your Destruction spells by (%d+)%%.",
+ ["Increases the critical strike chance of your Searing Pain spell by (%d+)%%."] = "Increases the critical strike chance of your Searing Pain spell by (%d+)%%.",
+ ["Reduces the chance for enemies to resist your Affliction spells by (%d+)%%."] = "Reduces the chance for enemies to resist your Affliction spells by (%d+)%%.",
+ -- mage
+ ["Increases the critical strike chance of your Arcane Explosion and Arcane Missiles spells by an additional (%d+)%%."] = "Increases the critical strike chance of your Arcane Explosion and Arcane Missiles spells by an additional (%d+)%%.",
+ ["Increases the critical strike chance of your Fire Blast and Scorch spells by (%d+)%%."] = "Increases the critical strike chance of your Fire Blast and Scorch spells by (%d+)%%.",
+ ["Increases the critical strike chance of your Flamestrike spell by (%d+)%%."] = "Increases the critical strike chance of your Flamestrike spell by (%d+)%%.",
+ ["Increases the critical strike chance of your Fire spells by (%d+)%%."] = "Increases the critical strike chance of your Fire spells by (%d+)%%.",
+ ["Increases the critical strike chance of all your spells against frozen targets by (%d+)%%."] = "Increases the critical strike chance of all your spells against frozen targets by (%d+)%%.",
+ ["Increases your spell damage and critical srike chance by (%d+)%%."] = "Increases your spell damage and critical srike chance by (%d+)%%.",
+ ["Increases critical strike chance from Fire damage spells by (%d+)%%."] = "Increases critical strike chance from Fire damage spells by (%d+)%%.",
+ -- priest
+ ["Reduces the chance for enemies to resist your Holy and Discipline spells by (%d+)%%."] = "Reduces the chance for enemies to resist your Holy and Discipline spells by (%d+)%%.",
+ ["Increases the critical effect chance of your Holy and Discipline spells by (%d+)%%."] = "Increases the critical effect chance of your Holy and Discipline spells by (%d+)%%.",
+ ["Increases your spell damage by %d+%% and the critical strike chance of your offensive spells by (%d)%%"] = "Increases your spell damage by %d+%% and the critical strike chance of your offensive spells by (%d)%%",
+ ["^Set: Improves your chance to get a critical strike with Holy spells by (%d)%%."] = "^Set: Improves your chance to get a critical strike with Holy spells by (%d)%%.",
+ ["^Set: Increases your chance of a critical hit with Prayer of Healing by (%d+)%%."] = "^Set: Increases your chance of a critical hit with Prayer of Healing by (%d+)%%.",
+ --defense
+ ["DEFENSE_TOOLTIP"] = [[|cffffffffDefense Skill|r]],
+ ["DEFENSE_TOOLTIP_SUB"] = [[Higher defense makes you harder to hit and makes monsters less likely to land a crushing blow.]],
+
+ ["PLAYER_DODGE_TOOLTIP"] = [[|cffffffffDodge|r]],
+ ["PLAYER_DODGE_TOOLTIP_SUB"] = [[Your chance to dodge enemy melee attacks.
+ Players can not dodge attacks from behind.]],
+
+ ["PLAYER_PARRY_TOOLTIP"] = [[|cffffffffParry|r]],
+ ["PLAYER_PARRY_TOOLTIP_SUB"] = [[Your chance to parry enemy melee attacks.
+ Players and monsters can not parry attacks from behind.]],
+
+ ["PLAYER_BLOCK_TOOLTIP"] = [[|cffffffffBlock|r]],
+ ["PLAYER_BLOCK_TOOLTIP_SUB"] = [[Your chance to block enemy physical attacks with a shield.
+ Players and monsters can not block attacks from behind.]],
+
+ ["TOTAL_AVOIDANCE_TOOLTIP"] = [[|cffffffffAvoidance|r]],
+ ["TOTAL_AVOIDANCE_TOOLTIP_SUB"] = [[Your total chance to avoid enemy physical attacks. Subtract 2.4 vs bosses.]],
+
+ --melee
+ ["MELEE_HIT_TOOLTIP"] = [[|cffffffffMelee Hit|r]],
+ ["MELEE_HIT_TOOLTIP_SUB"] = [[Increases chance to hit with melee attacks.]],
+ ["MELEE_CRIT_TOOLTIP"] = [[|cffffffffMelee Crit|r]],
+ ["MELEE_CRIT_TOOLTIP_SUB"] = [[Your chance to land a critical strike with melee attacks.]],
+ ["MELEE_WEAPON_SKILL_TOOLTIP"] = [[|cffffffffMelee Weapon Skill|r]],
+ ["MELEE_WEAPON_SKILL_TOOLTIP_SUB"] = [[Higher weapon skill reduces your chance to miss and increases damage of your glancing blows, while using melee weapons.]],
+
+ --boss
+ ["MELEE_MISS_VS_BOSS_TOOLTIP"] = [[|cffffffffChance To Miss Boss(lvl 63)|r]],
+ ["MELEE_MISS_VS_BOSS_TOOLTIP_SUB"] = [[Melee attack miss chance due to mob being higher level than you.]],
+ ["MELEE_DODGE_VS_BOSS_TOOLTIP"] = [[|cffffffffChance Boss(lvl 63) Dodges|r]],
+ ["MELEE_DODGE_VS_BOSS_TOOLTIP_SUB"] = [[The formula is 5%% dodge chance + (your weapon skill - 315) * 0.1%%.]],
+ ["MELEE_GLANCE_VS_BOSS_TOOLTIP"] = [[|cffffffffPercent Glancing Damage vs Boss(lvl 63)|r]],
+ ["MELEE_GLANCE_VS_BOSS_TOOLTIP_SUB"] = [[Against lvl 63 Boss you have a 40%% chance to do a glancing blow that does reduced damage. The amount of damage reduction is based on your weapon skill but the formula is too complicated to show here.]],
+ ["MELEE_CRIT_CAP_VS_BOSS_TOOLTIP"] = [[|cffffffffCritical Chance Cap|r]],
+ ["MELEE_CRIT_CAP_VS_BOSS_TOOLTIP_SUB"] = [[A critical hit can only happen if an attack is not already a miss, dodged, or glancing. This means your crit cap is (100%% - miss chance%% - dodge chance%% - glance chance%%). Any crit chance you have over your crit cap is wasted.]],
+ ["MELEE_EFF_CRIT_VS_BOSS_TOOLTIP"] = [[|cffffffffEffective Critical Chance|r]],
+ ["MELEE_EFF_CRIT_VS_BOSS_TOOLTIP_SUB"] = [[If you are under your crit cap, this will match your normal crit chance. If you are over your crit cap, this will be your crit cap.]],
+
+ --ranged
+ ["RANGED_WEAPON_SKILL_TOOLTIP"] = [[|cffffffffRanged Weapon Skill|r]],
+ ["RANGED_WEAPON_SKILL_TOOLTIP_SUB"] = [[Higher weapon skill reduces your chance to miss with a ranged weapon.]],
+ ["RANGED_CRIT_TOOLTIP"] = [[|cffffffffRanged Crit|r]],
+ ["RANGED_CRIT_TOOLTIP_SUB"] = [[Your chance to land a critical strike with ranged weapons.]],
+ ["RANGED_HIT_TOOLTIP"] = [[|cffffffffRanged Hit|r]],
+ ["RANGED_HIT_TOOLTIP_SUB"] = [[Increases chance to hit with ranged weapons.]],
+
+ --spells
+ ["SPELL_HIT_TOOLTIP"] = [[|cffffffffSpell Hit|r]],
+ ["SPELL_HIT_SECONDARY_TOOLTIP"] = [[|cffffffffSpell Hit (%d%%|cff20ff20+%d%% %s|r|cffffffff)|r]],
+ ["SPELL_HIT_TOOLTIP_SUB"] = [[Increases chance to land a harmful spell.]],
+
+ ["SPELL_CRIT_TOOLTIP"] = [[|cffffffffSpell Crit|r]],
+ ["SPELL_CRIT_TOOLTIP_SUB"] = [[Your chance to land a critical strike with spells.]],
+
+ ["SPELL_POWER_TOOLTIP"] = [[|cffffffffSpell Power %d|r]],
+ ["SPELL_POWER_TOOLTIP_SUB"] = [[Increases damage done by spells and effects.]],
+ ["SPELL_POWER_SECONDARY_TOOLTIP"] = [[|cffffffffSpell Power %d (%d|cff20ff20+%d %s|r|cffffffff)|r]],
+ ["SPELL_POWER_SECONDARY_TOOLTIP_SUB"] = [[Increases damage done by spells and effects.]],
+
+ ["SPELL_SCHOOL_TOOLTIP"] = [[|cffffffff%s Spell Power %s|r]],
+ ["SPELL_SCHOOL_SECONDARY_TOOLTIP"] = [[|cffffffff%s Spell Power %d (%d|cff20ff20+%d|r|cffffffff)|r]],
+ ["SPELL_SCHOOL_TOOLTIP_SUB"] = [[Increases damage done by %s spells and effects.]],
+
+ ["SPELL_HEALING_POWER_TOOLTIP"] = [[|cffffffffHealing Power %d|r]],
+ ["SPELL_HEALING_POWER_SECONDARY_TOOLTIP"] = [[|cffffffffHealing Power %d (%d|cff20ff20+%d|r|cffffffff)|r]],
+ ["SPELL_HEALING_POWER_TOOLTIP_SUB"] = [[Increases healing done by spells and effects.]],
+
+ ["SPELL_MANA_REGEN_TOOLTIP"] = [[|cffffffffMana regen: %d |cffffffff(%d)|r]],
+ ["SPELL_MANA_REGEN_TOOLTIP_SUB"] = [[Mana regen when not casting and (while casting).
+ Mana regenerates every 2 seconds and the amount is dependent on your total spirit and MP5.
+ Spirit Regen: %d
+ Regen while casting: %d%%
+ MP5 Regen: %d
+ MP5 Regen (2s): %d]],
+
+ PLAYERSTAT_BASE_STATS = "Base Stats",
+ PLAYERSTAT_DEFENSES = "Defenses",
+ PLAYERSTAT_DEFENSES_BOSS = "Defenses vs Boss",
+ PLAYERSTAT_MELEE_COMBAT = "Melee",
+ PLAYERSTAT_MELEE_BOSS = "Melee vs Boss",
+ PLAYERSTAT_RANGED_COMBAT = "Ranged",
+ PLAYERSTAT_SPELL_COMBAT = "Spell",
+ PLAYERSTAT_SPELL_SCHOOLS = "Schools",
+ WEAPON_SKILL_COLON = "Wep Skill:",
+ MISS_CHANCE_COLON = "Miss %:",
+ DODGE_CHANCE_COLON = "Dodge %:",
+ GLANCE_REDUCTION_COLON = "Glance Dmg:",
+ CRIT_CAP_COLON = "Crit Cap:",
+ BOSS_CRIT_COLON = "Eff. Crit:",
+ MELEE_HIT_RATING_COLON = "Hit Rating:",
+ RANGED_HIT_RATING_COLON = "Hit Rating:",
+ SPELL_HIT_RATING_COLON = "Hit Rating:",
+ MELEE_CRIT_COLON = "Crit Chance:",
+ RANGED_CRIT_COLON = "Crit Chance:",
+ SPELL_CRIT_COLON = "Crit Chance:",
+ MANA_REGEN_COLON = "Mana regen:",
+ HEAL_POWER_COLON = "Healing:",
+ DODGE_COLON = DODGE .. ":",
+ PARRY_COLON = PARRY .. ":",
+ BLOCK_COLON = BLOCK .. ":",
+ TOTAL_COLON = "Total:",
+ SPELL_POWER_COLON = "Power:",
+ SPELL_SCHOOL_ARCANE = "Arcane",
+ SPELL_SCHOOL_FIRE = "Fire",
+ SPELL_SCHOOL_FROST = "Frost",
+ SPELL_SCHOOL_HOLY = "Holy",
+ SPELL_SCHOOL_NATURE = "Nature",
+ SPELL_SCHOOL_SHADOW = "Shadow",
+}
diff --git a/README.md b/README.md
index 8e36f63..ebcce4e 100644
--- a/README.md
+++ b/README.md
@@ -1,31 +1,20 @@
-BetterCharacterStats - a World of Warcraft (1.12.1) AddOn
-===================================================
-
-![preview](https://raw.githubusercontent.com/yutsuku/BetterCharacterStats/gh-pages/images/BetterCharacterStats.png)
-
-Installation:
-
-Put "BetterCharacterStats" folder into ".../World of Warcraft/Interface/AddOns/".
-Create AddOns folder if necessary
-
-After Installation directory tree should look like the following
-
- World of Warcraft
- `- Interface
- `- AddOns
- `- BetterCharacterStats
- |- README.md
- |- BetterCharacterStats.lua
- |- BetterCharacterStats.toc
- |- BetterCharacterStats.xml
- |- helper.lua
- `- Localization.lua
-
-Features:
-- Displays character statistics in one place (just like the character tab in Burning Crusade).
-
-Known Issues:
-- May be lacking things related to spell hit/spell crit.
-
-Thanks to:
-- All people who keeps reporting to me that some things are missing or are broken.
\ No newline at end of file
+# BetterCharacterStats for Turtle WoW
+This addon shows your character stats that are not present in default UI.
+This version is designed specifically for TurtleWoW and its custom changes.
+## Features
+ - Base stats: strength, agility, stamina, intellect, spirit, armor
+ - Melee/Ranged: weapon skill, damage, attack speed, attack power, hit, crit
+ - Spells: spell power, spell hit, crit, healing power, mana regeneration
+ - Schools: your spell power for each school of magic
+ - Defenses: armor, defense skill, dodge, parry, block, avoidance
+## Preview
+![preview1](https://github.com/user-attachments/assets/d342aed0-812c-40f9-a4d4-9b33eb48caa3)
+
+## Installation
+1. Click Code -> Download ZIP
+2. Extract ZIP file into your Interface/AddOnns folder, remove ``-main``
+3. Restart the game
+## Feedback
+If you find any bugs, inaccuracies, typos or just want to suggest something, open an [issue](https://github.com/Otari98/BetterCharacterStats/issues) on GitHub
+## Thanks to original author and all contributors
+Moh, Bennylava, Lexie, Spit, Pepopo
diff --git a/helper.lua b/helper.lua
index 2bb87c0..bb71f0c 100644
--- a/helper.lua
+++ b/helper.lua
@@ -21,6 +21,65 @@ local function tContains(table, item)
return nil
end
+BCScache = BCScache or {
+ ["gear"] = {
+ damage_and_healing = 0,
+ arcane = 0,
+ fire = 0,
+ frost = 0,
+ holy = 0,
+ nature = 0,
+ shadow = 0,
+ healing = 0,
+ mp5 = 0,
+ casting = 0,
+ spell_hit = 0,
+ spell_crit = 0,
+ hit = 0,
+ ranged_hit = 0,
+ ranged_crit = 0
+ },
+ ["talents"] = {
+ damage_and_healing = 0,
+ healing = 0,
+ spell_hit = 0,
+ spell_hit_fire = 0,
+ spell_hit_frost = 0,
+ spell_hit_arcane = 0,
+ spell_hit_shadow = 0,
+ spell_hit_holy = 0,
+ spell_crit = 0,
+ casting = 0,
+ mp5 = 0,
+ hit = 0,
+ ranged_hit = 0,
+ ranged_crit = 0
+ },
+ ["auras"] = {
+ damage_and_healing = 0,
+ only_damage = 0, -- +dmg to all schools, needed to calculate healing
+ arcane = 0,
+ fire = 0,
+ frost = 0,
+ holy = 0,
+ nature = 0,
+ shadow = 0,
+ healing = 0,
+ mp5 = 0,
+ casting = 0,
+ spell_hit = 0,
+ spell_crit = 0,
+ hit = 0,
+ ranged_hit = 0,
+ ranged_crit = 0,
+ hit_debuff = 0
+ },
+ ["skills"] = {
+ mh = 0,
+ oh = 0,
+ ranged = 0
+ }
+}
function BCS:GetPlayerAura(searchText, auraType)
if not auraType then
-- buffs
@@ -30,9 +89,7 @@ function BCS:GetPlayerAura(searchText, auraType)
local index = GetPlayerBuff(i, 'HELPFUL')
if index > -1 then
BCS_Tooltip:SetPlayerBuff(index)
- local MAX_LINES = BCS_Tooltip:NumLines()
-
- for line=1, MAX_LINES do
+ for line=1, BCS_Tooltip:NumLines() do
local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
if left:GetText() then
if left:GetText() == "Power of the Guardian" and searchText == "Power of the Guardian Crit" then
@@ -52,9 +109,7 @@ function BCS:GetPlayerAura(searchText, auraType)
local index = GetPlayerBuff(i, auraType)
if index > -1 then
BCS_Tooltip:SetPlayerBuff(index)
- local MAX_LINES = BCS_Tooltip:NumLines()
-
- for line=1, MAX_LINES do
+ for line=1, BCS_Tooltip:NumLines() do
local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
if left:GetText() then
local value = {strfind(left:GetText(), searchText)}
@@ -68,174 +123,128 @@ function BCS:GetPlayerAura(searchText, auraType)
end
end
-local Cache_GetHitRating_Tab, Cache_GetHitRating_Talent
-local hit_debuff = 0
function BCS:GetHitRating(hitOnly)
local Hit_Set_Bonus = {}
- local hit = 0;
- local MAX_INVENTORY_SLOTS = 19;
- hit_debuff = 0;
-
- for slot=0, MAX_INVENTORY_SLOTS do
- local hasItem = BCS_Tooltip:SetInventoryItem("player", slot)
- if hasItem then
- local MAX_LINES = BCS_Tooltip:NumLines()
- local SET_NAME = nil
-
- for line=1, MAX_LINES do
- local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
- if left:GetText() then
- local _,_, value = strfind(left:GetText(), L["Equip: Improves your chance to hit by (%d)%%."])
- if value then
- hit = hit + tonumber(value)
- end
- _,_, value = strfind(left:GetText(), L["/Hit %+(%d+)"])
- if value then
- hit = hit + tonumber(value)
- end
- _,_, value = strfind(left:GetText(), "(.+) %(%d/%d%)")
- if value then
- SET_NAME = value
- end
- _,_, value = strfind(left:GetText(), L["^Set: Improves your chance to hit by (%d)%%."])
- if value and SET_NAME and not tContains(Hit_Set_Bonus, SET_NAME) then
- tinsert(Hit_Set_Bonus, SET_NAME)
- hit = hit + tonumber(value)
- line = MAX_LINES
+ local hit = 0
+
+ if BCS.needScanGear then
+ BCScache["gear"].hit = 0
+ --scan gear
+ for slot=1, 19 do
+ if BCS_Tooltip:SetInventoryItem('player', slot) then
+ local _, _, eqItemLink = strfind(GetInventoryItemLink('player', slot), "(item:%d+:%d+:%d+:%d+)")
+ if eqItemLink then BCS_Tooltip:ClearLines() BCS_Tooltip:SetHyperlink(eqItemLink) end
+ local SET_NAME = nil
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _,_, value = strfind(left:GetText(), L["Equip: Improves your chance to hit by (%d)%%."])
+ if value then
+ BCScache["gear"].hit = BCScache["gear"].hit + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["/Hit %+(%d+)"])
+ if value then
+ BCScache["gear"].hit = BCScache["gear"].hit + tonumber(value)
+ end
+
+ _,_, value = strfind(left:GetText(), "(.+) %(%d/%d%)")
+ if value then
+ SET_NAME = value
+ end
+ _,_, value = strfind(left:GetText(), L["^Set: Improves your chance to hit by (%d)%%."])
+ if value and SET_NAME and not tContains(Hit_Set_Bonus, SET_NAME) then
+ tinsert(Hit_Set_Bonus, SET_NAME)
+ BCScache["gear"].hit = BCScache["gear"].hit + tonumber(value)
+ break
+ end
end
end
end
-
end
end
- -- buffs
- local _, _, hitFromAura = BCS:GetPlayerAura(L["Chance to hit increased by (%d)%%."])
- if hitFromAura then
- hit = hit + tonumber(hitFromAura)
- end
- _, _, hitFromAura = BCS:GetPlayerAura(L["Improves your chance to hit by (%d+)%%."])
- if hitFromAura then
- hit = hit + tonumber(hitFromAura)
- end
- _, _, hitFromAura = BCS:GetPlayerAura(L["Increases attack power by %d+ and chance to hit by (%d+)%%."])
- if hitFromAura then
- hit = hit + tonumber(hitFromAura)
- end
- -- debuffs
- _, _, hitFromAura = BCS:GetPlayerAura(L["Chance to hit reduced by (%d+)%%."], 'HARMFUL')
- if hitFromAura then
- hit_debuff = hit_debuff + tonumber(hitFromAura)
- end
- _, _, hitFromAura = BCS:GetPlayerAura(L["Chance to hit decreased by (%d+)%% and %d+ Nature damage every %d+ sec."], 'HARMFUL')
- if hitFromAura then
- hit_debuff = hit_debuff + tonumber(hitFromAura)
- end
- hitFromAura = BCS:GetPlayerAura(L["Lowered chance to hit."], 'HARMFUL')
- if hitFromAura then
- hit_debuff = hit_debuff + 25
- end
-
- local MAX_TABS = GetNumTalentTabs()
- -- speedup
- if Cache_GetHitRating_Tab and Cache_GetHitRating_Talent then
- BCS_Tooltip:SetTalent(Cache_GetHitRating_Tab, Cache_GetHitRating_Talent)
- local MAX_LINES = BCS_Tooltip:NumLines()
-
- for line=1, MAX_LINES do
- local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
- if left:GetText() then
- -- rogues
- local _,_, value = strfind(left:GetText(), L["Increases your chance to hit with melee weapons by (%d)%%."])
- local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(Cache_GetHitRating_Tab, Cache_GetHitRating_Talent)
- if value and rank > 0 then
- hit = hit + tonumber(value)
- line = MAX_LINES
- end
-
- -- hunters
- _,_, value = strfind(left:GetText(), L["Increases hit chance by (%d)%% and increases the chance movement impairing effects will be resisted by an additional %d+%%."])
- name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(Cache_GetHitRating_Tab, Cache_GetHitRating_Talent)
- if value and rank > 0 then
- hit = hit + tonumber(value)
- line = MAX_LINES
- end
- end
+ if BCS.needScanAuras then
+ BCScache["auras"].hit = 0
+ BCScache["auras"].hit_debuff = 0
+ -- buffs
+ local _, _, hitFromAura = BCS:GetPlayerAura(L["Chance to hit increased by (%d)%%."])
+ if hitFromAura then
+ BCScache["auras"].hit = BCScache["auras"].hit + tonumber(hitFromAura)
end
-
- if not hitOnly then
- hit = hit - hit_debuff
- if hit < 0 then hit = 0 end
- return hit
- else
- return hit
+ _, _, hitFromAura = BCS:GetPlayerAura(L["Improves your chance to hit by (%d+)%%."])
+ if hitFromAura then
+ BCScache["auras"].hit = BCScache["auras"].hit + tonumber(hitFromAura)
+ end
+ _, _, hitFromAura = BCS:GetPlayerAura(L["Increases attack power by %d+ and chance to hit by (%d+)%%."])
+ if hitFromAura then
+ BCScache["auras"].hit = BCScache["auras"].hit + tonumber(hitFromAura)
+ end
+ -- debuffs
+ _, _, hitFromAura = BCS:GetPlayerAura(L["Chance to hit reduced by (%d+)%%."], 'HARMFUL')
+ if hitFromAura then
+ BCScache["auras"].hit_debuff = BCScache["auras"].hit_debuff + tonumber(hitFromAura)
+ end
+ _, _, hitFromAura = BCS:GetPlayerAura(L["Chance to hit decreased by (%d+)%% and %d+ Nature damage every %d+ sec."], 'HARMFUL')
+ if hitFromAura then
+ BCScache["auras"].hit_debuff = BCScache["auras"].hit_debuff + tonumber(hitFromAura)
+ end
+ hitFromAura = BCS:GetPlayerAura(L["Lowered chance to hit."], 'HARMFUL')
+ if hitFromAura then
+ BCScache["auras"].hit_debuff = BCScache["auras"].hit_debuff + 25
end
end
-
- for tab=1, MAX_TABS do
- local MAX_TALENTS = GetNumTalents(tab)
-
- for talent=1, MAX_TALENTS do
- BCS_Tooltip:SetTalent(tab, talent);
- local MAX_LINES = BCS_Tooltip:NumLines()
-
- for line=1, MAX_LINES do
- local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
- if left:GetText() then
- -- rogues
- local _,_, value = strfind(left:GetText(), L["Increases your chance to hit with melee weapons by (%d)%%."])
- local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tab, talent)
- if value and rank > 0 then
- hit = hit + tonumber(value)
-
- Cache_GetHitRating_Tab = tab
- Cache_GetHitRating_Talent = talent
-
- line = MAX_LINES
- talent = MAX_TALENTS
- tab = MAX_TABS
- end
-
- -- hunters
- _,_, value = strfind(left:GetText(), L["Increases hit chance by (%d)%% and increases the chance movement impairing effects will be resisted by an additional %d+%%."])
- name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tab, talent)
- if value and rank > 0 then
- hit = hit + tonumber(value)
-
- Cache_GetHitRating_Tab = tab
- Cache_GetHitRating_Talent = talent
-
- line = MAX_LINES
- talent = MAX_TALENTS
- tab = MAX_TABS
- end
-
- -- Druid
- -- Natural Weapons
- _,_, value = strfind(left:GetText(), "Also increases chance to hit with melee attacks and spells by (%d+)%%.")
- local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tab, talent)
- if value and rank > 0 then
- hit = hit + tonumber(value)
- line = MAX_LINES
- end
-
- -- Paladin & Shaman
- -- Precision & Nature's Guidance
- _,_, value = strfind(left:GetText(), "Increases your chance to hit with melee attacks and spells by (%d+)%%.")
- local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tab, talent)
- if value and rank > 0 then
- hit = hit + tonumber(value)
- line = MAX_LINES
+
+ if BCS.needScanTalents then
+ BCScache["talents"].hit = 0
+ --scan talents
+ for tab=1, GetNumTalentTabs() do
+ for talent=1, GetNumTalents(tab) do
+ BCS_Tooltip:SetTalent(tab, talent)
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _, _, _, _, rank = GetTalentInfo(tab, talent)
+ -- Rogue
+ local _,_, value = strfind(left:GetText(), L["Increases your chance to hit with melee weapons by (%d)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].hit = BCScache["talents"].hit + tonumber(value)
+ break
+ end
+ -- Hunter
+ _,_, value = strfind(left:GetText(), L["Increases hit chance by (%d)%% and increases the chance movement impairing effects will be resisted by an additional %d+%%."])
+ if value and rank > 0 then
+ BCScache["talents"].hit = BCScache["talents"].hit + tonumber(value)
+ break
+ end
+ -- Druid
+ -- Natural Weapons
+ _,_, value = strfind(left:GetText(), "Also increases chance to hit with melee attacks and spells by (%d+)%%.")
+ if value and rank > 0 then
+ BCScache["talents"].hit = BCScache["talents"].hit + tonumber(value)
+ break
+ end
+ -- Paladin
+ -- Precision
+ _,_, value = strfind(left:GetText(), "Increases your chance to hit with melee attacks and spells by (%d+)%%.")
+ if value and rank > 0 then
+ BCScache["talents"].hit = BCScache["talents"].hit + tonumber(value)
+ break
+ end
+ -- Shaman
+ -- Elemental Devastation
+ _,_, value = strfind(left:GetText(), "Increases your chance to hit with spells and melee attacks by (%d+)%%")
+ if value and rank > 0 then
+ BCScache["talents"].hit = BCScache["talents"].hit + tonumber(value)
+ break
+ end
end
-
- end
+ end
end
-
end
end
-
+ hit = BCScache["talents"].hit + BCScache["gear"].hit + BCScache["auras"].hit
if not hitOnly then
- hit = hit - hit_debuff
+ hit = hit - BCScache["auras"].hit_debuff
if hit < 0 then hit = 0 end -- Dust Cloud OP
return hit
else
@@ -244,26 +253,22 @@ function BCS:GetHitRating(hitOnly)
end
function BCS:GetRangedHitRating()
- local melee_hit = BCS:GetHitRating(true)
- local ranged_hit = melee_hit
- local debuff = hit_debuff
-
- local hasItem = BCS_Tooltip:SetInventoryItem("player", 18) -- ranged enchant
- if hasItem then
- local MAX_LINES = BCS_Tooltip:NumLines()
- for line=1, MAX_LINES do
- local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
- if left:GetText() then
- local _,_, value = strfind(left:GetText(), L["+(%d)%% Hit"])
- if value then
- ranged_hit = ranged_hit + tonumber(value)
- line = MAX_LINES
+ if BCS.needScanGear then
+ BCScache["gear"].ranged_hit = 0
+ if BCS_Tooltip:SetInventoryItem("player", 18) then
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _,_, value = strfind(left:GetText(), L["+(%d)%% Ranged Hit"])
+ if value then
+ BCScache["gear"].ranged_hit = BCScache["gear"].ranged_hit + tonumber(value)
+ break
+ end
end
end
end
end
-
- ranged_hit = ranged_hit - debuff
+ local ranged_hit = BCS:GetHitRating(true) + BCScache["gear"].ranged_hit - BCScache["auras"].hit_debuff
if ranged_hit < 0 then ranged_hit = 0 end
return ranged_hit
end
@@ -274,885 +279,1676 @@ function BCS:GetSpellHitRating()
local hit_frost = 0
local hit_arcane = 0
local hit_shadow = 0
+ local hit_holy = 0
local hit_Set_Bonus = {}
-
- -- scan gear
- local MAX_INVENTORY_SLOTS = 19
- for slot=0, MAX_INVENTORY_SLOTS do
- local hasItem = BCS_Tooltip:SetInventoryItem("player", slot)
-
- if hasItem then
- local SET_NAME
- local MAX_LINES = BCS_Tooltip:NumLines()
-
- for line=1, MAX_LINES do
- local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
-
- if left:GetText() then
- local _,_, value = strfind(left:GetText(), L["Equip: Improves your chance to hit with spells by (%d)%%."])
- if value then
- hit = hit + tonumber(value)
- end
- _,_, value = strfind(left:GetText(), L["/Spell Hit %+(%d+)"])
- if value then
- hit = hit + tonumber(value)
- end
-
- _,_, value = strfind(left:GetText(), "(.+) %(%d/%d%)")
- if value then
- SET_NAME = value
- end
- _, _, value = strfind(left:GetText(), L["^Set: Improves your chance to hit with spells by (%d)%%."])
- if value and SET_NAME and not tContains(hit_Set_Bonus, SET_NAME) then
- tinsert(hit_Set_Bonus, SET_NAME)
- hit = hit + tonumber(value)
+ if BCS.needScanGear then
+ BCScache["gear"].spell_hit = 0
+ -- scan gear
+ for slot=1, 19 do
+ if BCS_Tooltip:SetInventoryItem('player', slot) then
+ local _, _, eqItemLink = strfind(GetInventoryItemLink('player', slot), "(item:%d+:%d+:%d+:%d+)")
+ if eqItemLink then BCS_Tooltip:ClearLines() BCS_Tooltip:SetHyperlink(eqItemLink) end
+ local SET_NAME
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _,_, value = strfind(left:GetText(), L["Equip: Improves your chance to hit with spells by (%d)%%."])
+ if value then
+ BCScache["gear"].spell_hit = BCScache["gear"].spell_hit + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["/Spell Hit %+(%d+)"])
+ if value then
+ BCScache["gear"].spell_hit = BCScache["gear"].spell_hit + tonumber(value)
+ end
+
+ _,_, value = strfind(left:GetText(), "(.+) %(%d/%d%)")
+ if value then
+ SET_NAME = value
+ end
+ _, _, value = strfind(left:GetText(), L["^Set: Improves your chance to hit with spells by (%d)%%."])
+ if value and SET_NAME and not tContains(hit_Set_Bonus, SET_NAME) then
+ tinsert(hit_Set_Bonus, SET_NAME)
+ BCScache["gear"].spell_hit = BCScache["gear"].spell_hit + tonumber(value)
+ end
end
end
end
-
end
end
-
- -- scan talents
- local MAX_TABS = GetNumTalentTabs()
-
- for tab=1, MAX_TABS do
- local MAX_TALENTS = GetNumTalents(tab)
-
- for talent=1, MAX_TALENTS do
- BCS_Tooltip:SetTalent(tab, talent)
- local MAX_LINES = BCS_Tooltip:NumLines()
-
- for line=1, MAX_LINES do
- local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
- if left:GetText() then
- -- Mage
- -- Elemental Precision
- local _,_, value = strfind(left:GetText(), L["Reduces the chance that the opponent can resist your Frost and Fire spells by (%d)%%."])
- local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tab, talent)
- if value and rank > 0 then
- hit_fire = hit_fire + tonumber(value)
- hit_frost = hit_frost + tonumber(value)
- line = MAX_LINES
- end
-
- -- Arcane Focus
- _,_, value = strfind(left:GetText(), L["Reduces the chance that the opponent can resist your Arcane spells by (%d+)%%."])
- local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tab, talent)
- if value and rank > 0 then
- hit_arcane = hit_arcane + tonumber(value)
- line = MAX_LINES
- end
-
- -- Priest
- -- Shadow Focus
- _,_, value = strfind(left:GetText(), L["Reduces your target's chance to resist your Shadow spells by (%d+)%%."])
- local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tab, talent)
- if value and rank > 0 then
- hit_shadow = hit_shadow + tonumber(value)
- line = MAX_LINES
- end
-
- -- Druid
- -- Natural Weapons
- _,_, value = strfind(left:GetText(), "Also increases chance to hit with melee attacks and spells by (%d+)%%.")
- local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tab, talent)
- if value and rank > 0 then
- hit = hit + tonumber(value)
- line = MAX_LINES
- end
-
- -- Paladin & Shaman
- -- Precision & Nature's Guidance
- _,_, value = strfind(left:GetText(), "Increases your chance to hit with melee attacks and spells by (%d+)%%.")
- local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tab, talent)
- if value and rank > 0 then
- hit = hit + tonumber(value)
- line = MAX_LINES
+ if BCS.needScanTalents then
+ BCScache["talents"].spell_hit = 0
+ BCScache["talents"].spell_hit_fire = 0
+ BCScache["talents"].spell_hit_frost = 0
+ BCScache["talents"].spell_hit_arcane = 0
+ BCScache["talents"].spell_hit_shadow = 0
+ BCScache["talents"].spell_hit_holy = 0
+ -- scan talents
+ for tab=1, GetNumTalentTabs() do
+ for talent=1, GetNumTalents(tab) do
+ BCS_Tooltip:SetTalent(tab, talent)
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _, _, _, _, rank = GetTalentInfo(tab, talent)
+ -- Mage
+ -- Elemental Precision
+ local _,_, value = strfind(left:GetText(), L["Reduces the chance that the opponent can resist your Frost and Fire spells by (%d)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].spell_hit_fire = BCScache["talents"].spell_hit_fire + tonumber(value)
+ BCScache["talents"].spell_hit_frost = BCScache["talents"].spell_hit_frost + tonumber(value)
+ break
+ end
+ -- Arcane Focus
+ _,_, value = strfind(left:GetText(), L["Reduces the chance that the opponent can resist your Arcane spells by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].spell_hit_arcane = BCScache["talents"].spell_hit_arcane + tonumber(value)
+ break
+ end
+ -- Priest
+ -- Piercing Light
+ _,_, value = strfind(left:GetText(), L["Reduces the chance for enemies to resist your Holy and Discipline spells by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].spell_hit_holy = BCScache["talents"].spell_hit_holy + tonumber(value)
+ break
+ end
+ -- Shadow Focus
+ _,_, value = strfind(left:GetText(), L["Reduces your target's chance to resist your Shadow spells by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].spell_hit_shadow = BCScache["talents"].spell_hit_shadow + tonumber(value)
+ break
+ end
+ -- Druid
+ -- Natural Weapons
+ _,_, value = strfind(left:GetText(), "Also increases chance to hit with melee attacks and spells by (%d+)%%.")
+ if value and rank > 0 then
+ BCScache["talents"].spell_hit = BCScache["talents"].spell_hit + tonumber(value)
+ break
+ end
+ -- Paladin
+ -- Precision
+ _,_, value = strfind(left:GetText(), "Increases your chance to hit with melee attacks and spells by (%d+)%%.")
+ if value and rank > 0 then
+ BCScache["talents"].spell_hit = BCScache["talents"].spell_hit + tonumber(value)
+ break
+ end
+ -- Shaman
+ -- Elemental Devastation
+ _,_, value = strfind(left:GetText(), "Increases your chance to hit with spells and melee attacks by (%d+)%%")
+ if value and rank > 0 then
+ BCScache["talents"].spell_hit = BCScache["talents"].spell_hit + tonumber(value)
+ break
+ end
+ -- Warlock
+ -- Suppression
+ _,_, value = strfind(left:GetText(), L["Reduces the chance for enemies to resist your Affliction spells by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].spell_hit_shadow = BCScache["talents"].spell_hit_shadow + tonumber(value)
+ break
+ end
end
- end
+ end
end
-
end
end
-
-- buffs
- local _, _, hitFromAura = BCS:GetPlayerAura(L["Spell hit chance increased by (%d+)%%."])
- if hitFromAura then
- hit = hit + tonumber(hitFromAura)
+ if BCS.needScanAuras then
+ BCScache["auras"].spell_hit = 0
+ local _, _, hitFromAura = BCS:GetPlayerAura(L["Spell hit chance increased by (%d+)%%."])
+ if hitFromAura then
+ BCScache["auras"].spell_hit = BCScache["auras"].spell_hit + tonumber(hitFromAura)
+ end
+ -- Elemental Devastation
+ _, _, hitFromAura = BCS:GetPlayerAura("Increases your chance to hit with spells by (%d+)%%")
+ if hitFromAura then
+ BCScache["auras"].spell_hit = BCScache["auras"].spell_hit + tonumber(hitFromAura)
+ end
end
-
- return hit, hit_fire, hit_frost, hit_arcane, hit_shadow
+ hit = BCScache["gear"].spell_hit + BCScache["talents"].spell_hit + BCScache["auras"].spell_hit
+ hit_fire = BCScache["talents"].spell_hit_fire
+ hit_frost = BCScache["talents"].spell_hit_frost
+ hit_arcane = BCScache["talents"].spell_hit_arcane
+ hit_shadow = BCScache["talents"].spell_hit_shadow
+ hit_holy = BCScache["talents"].spell_hit_holy
+ return hit, hit_fire, hit_frost, hit_arcane, hit_shadow, hit_holy
end
-local Cache_GetCritChance_SpellID, Cache_GetCritChance_BookType, Cache_GetCritChance_Line
-local Cache_GetCritChance_Tab, Cache_GetCritChance_Talent
function BCS:GetCritChance()
local crit = 0
- local _, class = UnitClass('player')
-
- if class == 'HUNTER' then
-
- local MAX_TABS = GetNumTalentTabs()
- -- speedup
- if Cache_GetCritChance_Tab and Cache_GetCritChance_Talent then
- BCS_Tooltip:SetTalent(Cache_GetCritChance_Tab, Cache_GetCritChance_Talent)
- local MAX_LINES = BCS_Tooltip:NumLines()
-
- for line=1, MAX_LINES do
- local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
- if left:GetText() then
- local _,_, value = strfind(left:GetText(), L["Increases your critical strike chance with all attacks by (%d)%%."])
- local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(Cache_GetCritChance_Tab, Cache_GetCritChance_Talent)
- if value and rank > 0 then
- crit = crit + tonumber(value)
- line = MAX_LINES
- end
- end
- end
- else
- for tab=1, MAX_TABS do
- local MAX_TALENTS = GetNumTalents(tab)
- for talent=1, MAX_TALENTS do
- BCS_Tooltip:SetTalent(tab, talent);
- local MAX_LINES = BCS_Tooltip:NumLines()
-
- for line=1, MAX_LINES do
- local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
- if left:GetText() then
- local _,_, value = strfind(left:GetText(), L["Increases your critical strike chance with all attacks by (%d)%%."])
- local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tab, talent)
- if value and rank > 0 then
- crit = crit + tonumber(value)
-
- Cache_GetCritChance_Tab = tab
- Cache_GetCritChance_Talent = talent
-
- line = MAX_LINES
- talent = MAX_TALENTS
- tab = MAX_TABS
- end
- end
- end
-
- end
- end
- end
-
- end
-
- -- speedup
- if Cache_GetCritChance_SpellID and Cache_GetCritChance_BookType and Cache_GetCritChance_Line then
-
- BCS_Tooltip:SetSpell(Cache_GetCritChance_SpellID, Cache_GetCritChance_BookType)
- local left = getglobal(BCS_Prefix .. "TextLeft" .. Cache_GetCritChance_Line)
- if left:GetText() then
- local _,_, value = strfind(left:GetText(), L["([%d.]+)%% chance to crit"])
- if value then
- crit = crit + tonumber(value)
- end
- end
-
- return crit
- end
-
- local MAX_TABS = GetNumSpellTabs()
-
- for tab=1, MAX_TABS do
- local name, texture, offset, numSpells = GetSpellTabInfo(tab)
-
+ --scan spellbook
+ for tab=1, GetNumSpellTabs() do
+ local _, _, offset, numSpells = GetSpellTabInfo(tab)
for spell=1, numSpells do
local currentPage = ceil(spell/SPELLS_PER_PAGE)
local SpellID = spell + offset + ( SPELLS_PER_PAGE * (currentPage - 1))
-
BCS_Tooltip:SetSpell(SpellID, BOOKTYPE_SPELL)
- local MAX_LINES = BCS_Tooltip:NumLines()
-
- for line=1, MAX_LINES do
+ for line=1, BCS_Tooltip:NumLines() do
local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
if left:GetText() then
local _,_, value = strfind(left:GetText(), L["([%d.]+)%% chance to crit"])
if value then
crit = crit + tonumber(value)
-
- Cache_GetCritChance_SpellID = SpellID
- Cache_GetCritChance_BookType = BOOKTYPE_SPELL
- Cache_GetCritChance_Line = line
-
- line = MAX_LINES
- spell = numSpells
- tab = MAX_TABS
+ break
end
end
end
-
end
end
-
+
return crit
end
-local Cache_GetRangedCritChance_Tab, Cache_GetRangedCritChance_Talent, Cache_GetRangedCritChance_Line
function BCS:GetRangedCritChance()
- local crit = BCS:GetCritChance()
-
- if Cache_GetRangedCritChance_Tab and Cache_GetRangedCritChance_Talent and Cache_GetRangedCritChance_Line then
- BCS_Tooltip:SetTalent(Cache_GetRangedCritChance_Tab, Cache_GetRangedCritChance_Talent)
- local left = getglobal(BCS_Prefix .. "TextLeft" .. Cache_GetRangedCritChance_Line)
-
- if left:GetText() then
- local _,_, value = strfind(left:GetText(), L["Increases your critical strike chance with ranged weapons by (%d)%%."])
- local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(Cache_GetRangedCritChance_Tab, Cache_GetRangedCritChance_Talent)
- if value and rank > 0 then
- crit = crit + tonumber(value)
+ -- values from vmangos core
+ local crit = 0
+ local _, class = UnitClass("player")
+ local _, agility = UnitStat("player", 2)
+ local vallvl1 = 0
+ local vallvl60 = 0
+ local classrate = 0
+
+ if class == "MAGE" then
+ vallvl1 = 12.9
+ vallvl60 = 20
+ elseif class == "ROGUE" then
+ vallvl1 = 2.2
+ vallvl60 = 29
+ elseif class == "HUNTER" then
+ vallvl1 = 3.5
+ vallvl60 = 53
+ elseif class == "PRIEST" then
+ vallvl1 = 11
+ vallvl60 = 20
+ elseif class == "WARLOCK" then
+ vallvl1 = 8.4
+ vallvl60 = 20
+ elseif class == "WARRIOR" then
+ vallvl1 = 3.9
+ vallvl60 = 20
+ else
+ return crit
+ end
+
+ classrate = vallvl1 * (60 - UnitLevel("player")) / 59 + vallvl60 * (UnitLevel("player") - 1) / 59
+ crit = agility / classrate
+
+ if BCS.needScanTalents then
+ BCScache["talents"].ranged_crit = 0
+ --scan talents
+ for tab=1, GetNumTalentTabs() do
+ for talent=1, GetNumTalents(tab) do
+ BCS_Tooltip:SetTalent(tab, talent)
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _, _, _, _, rank = GetTalentInfo(tab, talent)
+ local _,_, value = strfind(left:GetText(), L["Increases your critical strike chance with ranged weapons by (%d)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].ranged_crit = BCScache["talents"].ranged_crit + tonumber(value)
+ break
+ end
+ _,_, value = strfind(left:GetText(), L["Increases your critical strike chance with all attacks by (%d)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].ranged_crit = BCScache["talents"].ranged_crit + tonumber(value)
+ break
+ end
+ end
+ end
end
end
-
- return crit
end
-
- local MAX_TABS = GetNumTalentTabs()
-
- for tab=1, MAX_TABS do
- local MAX_TALENTS = GetNumTalents(tab)
-
- for talent=1, MAX_TALENTS do
- BCS_Tooltip:SetTalent(tab, talent);
- local MAX_LINES = BCS_Tooltip:NumLines()
-
- for line=1, MAX_LINES do
- local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
- if left:GetText() then
- local _,_, value = strfind(left:GetText(), L["Increases your critical strike chance with ranged weapons by (%d)%%."])
- local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tab, talent)
- if value and rank > 0 then
- crit = crit + tonumber(value)
-
- line = MAX_LINES
- talent = MAX_TALENTS
- tab = MAX_TABS
+
+ if BCS.needScanGear then
+ BCScache["gear"].ranged_crit = 0
+ --scan gear
+ local Crit_Set_Bonus = {}
+ for slot=1, 19 do
+ if BCS_Tooltip:SetInventoryItem('player', slot) then
+ local _, _, eqItemLink = strfind(GetInventoryItemLink('player', slot), "(item:%d+:%d+:%d+:%d+)")
+ if eqItemLink then BCS_Tooltip:ClearLines() BCS_Tooltip:SetHyperlink(eqItemLink) end
+ local SET_NAME
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _,_, value = strfind(left:GetText(), L["Equip: Improves your chance to get a critical strike by (%d)%%."])
+ if value then
+ BCScache["gear"].ranged_crit = BCScache["gear"].ranged_crit + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), "Equip: Improves your chance to get a critical strike with missile weapons by (%d)%%.")
+ if value then
+ BCScache["gear"].ranged_crit = BCScache["gear"].ranged_crit + tonumber(value)
+ end
+ -- Might of the Scourge (shoulder enchant)
+ _,_, value = strfind(left:GetText(), L["%+(%d+)%% Critical Strike"])
+ if value then
+ BCScache["gear"].ranged_crit = BCScache["gear"].ranged_crit + tonumber(value)
+ end
+
+ _,_, value = strfind(left:GetText(), "(.+) %(%d/%d%)")
+ if value then
+ SET_NAME = value
+ end
+ _, _, value = strfind(left:GetText(), L["^Set: Improves your chance to get a critical strike by (%d)%%."])
+ if value and SET_NAME and not tContains(Crit_Set_Bonus, SET_NAME) then
+ tinsert(Crit_Set_Bonus, SET_NAME)
+ BCScache["gear"].ranged_crit = BCScache["gear"].ranged_crit + tonumber(value)
+ end
end
end
end
-
end
end
-
+ if BCS.needScanAuras then
+ BCScache["auras"].ranged_crit = 0
+ --buffs
+ --ony head
+ local critFromAura = BCS:GetPlayerAura(L["Increases critical chance of spells by 10%%, melee and ranged by 5%% and grants 140 attack power. 120 minute duration."])
+ if critFromAura then
+ BCScache["auras"].ranged_crit = BCScache["auras"].ranged_crit + 5
+ end
+ --mongoose
+ _, _, critFromAura = BCS:GetPlayerAura(L["Agility increased by 25, Critical hit chance increases by (%d)%%."])
+ if critFromAura then
+ BCScache["auras"].ranged_crit = BCScache["auras"].ranged_crit + tonumber(critFromAura)
+ end
+ --songflower
+ _, _, critFromAura = BCS:GetPlayerAura(L["Increases chance for a melee, ranged, or spell critical by (%d+)%% and all attributes by %d+."])
+ if critFromAura then
+ BCScache["auras"].ranged_crit = BCScache["auras"].ranged_crit + tonumber(critFromAura)
+ end
+ --leader of the pack
+ _, _, critFromAura = BCS:GetPlayerAura(L["Increases ranged and melee critical chance by (%d+)%%."])
+ if critFromAura then
+ BCScache["auras"].ranged_crit = BCScache["auras"].ranged_crit + tonumber(critFromAura)
+ --check if druid is shapeshifted and have Idol of the Moonfang equipped
+ for i=1, GetNumPartyMembers() do
+ local _, partyClass = UnitClass("party"..i)
+ if partyClass == "DRUID" then
+ if BCS_Tooltip:SetInventoryItem("party"..i, 18) and UnitCreatureType("party"..i) == "Beast" then
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ _, _, critFromAura = strfind(left:GetText(), L["Equip: Increases the critical chance provided by Leader of the Pack and Moonkin Aura by (%d)%%."])
+ if critFromAura then
+ BCScache["auras"].ranged_crit = BCScache["auras"].ranged_crit + tonumber(critFromAura)
+ break
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ if class == "MAGE" then
+ crit = crit + 3.2
+ elseif class == "PRIEST" then
+ crit = crit + 3
+ elseif class == "WARLOCK" then
+ crit = crit + 2
+ end
+
+ crit = crit + BCScache["gear"].ranged_crit + BCScache["talents"].ranged_crit + BCScache["auras"].ranged_crit
+
return crit
end
function BCS:GetSpellCritChance()
- -- school crit: most likely never
local Crit_Set_Bonus = {}
local spellCrit = 0;
- local _, intelect = UnitStat("player", 4)
+ local _, intellect = UnitStat("player", 4)
local _, class = UnitClass("player")
- -- values from theorycraft / http://wow.allakhazam.com/forum.html?forum=21&mid=1157230638252681707
+ -- values from vmangos core
+ local playerLevel = UnitLevel("player")
if class == "MAGE" then
- spellCrit = 0.2 + (intelect / 59.5)
+ spellCrit = 3.7 + intellect / (14.77 + .65 * playerLevel)
elseif class == "WARLOCK" then
- spellCrit = 1.7 + (intelect / 60.6)
+ spellCrit = 3.18 + intellect / (11.30 + .82 * playerLevel)
elseif class == "PRIEST" then
- spellCrit = 0.8 + (intelect / 59.56)
+ spellCrit = 2.97 + intellect / (10.03 + .82 * playerLevel)
elseif class == "DRUID" then
- spellCrit = 1.8 + (intelect / 60)
+ spellCrit = 3.33 + intellect / (12.41 + .79 * playerLevel)
elseif class == "SHAMAN" then
- spellCrit = 1.8 + (intelect / 59.2)
+ spellCrit = 3.54 + intellect / (11.51 + .8 * playerLevel)
elseif class == "PALADIN" then
- spellCrit = intelect / 29.5
+ spellCrit = 3.7 + intellect / (14.77 + .65 * playerLevel)
end
-
- local MAX_INVENTORY_SLOTS = 19
-
- for slot=0, MAX_INVENTORY_SLOTS do
- local hasItem = BCS_Tooltip:SetInventoryItem("player", slot)
-
- if hasItem then
- local SET_NAME = nil
-
- for line=1, BCS_Tooltip:NumLines() do
- local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
-
- if left:GetText() then
- local _,_, value = strfind(left:GetText(), L["Equip: Improves your chance to get a critical strike with spells by (%d)%%."])
- if value then
- spellCrit = spellCrit + tonumber(value)
- end
-
- _,_, value = strfind(left:GetText(), "(.+) %(%d/%d%)")
- if value then
- SET_NAME = value
- end
+ if BCS.needScanGear then
+ BCScache["gear"].spell_crit = 0
+ --scan gear
+ for slot=1, 19 do
+ if BCS_Tooltip:SetInventoryItem('player', slot) then
+ local _, _, eqItemLink = strfind(GetInventoryItemLink('player', slot), "(item:%d+:%d+:%d+:%d+)")
+ if eqItemLink then BCS_Tooltip:ClearLines() BCS_Tooltip:SetHyperlink(eqItemLink) end
+ local SET_NAME = nil
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _,_, value = strfind(left:GetText(), L["Equip: Improves your chance to get a critical strike with spells by (%d)%%."])
+ if value then
+ BCScache["gear"].spell_crit = BCScache["gear"].spell_crit + tonumber(value)
+ end
- _, _, value = strfind(left:GetText(), L["^Set: Improves your chance to get a critical strike with spells by (%d)%%."])
- if value and SET_NAME and not tContains(Crit_Set_Bonus, SET_NAME) then
- tinsert(Crit_Set_Bonus, SET_NAME)
- spellCrit = spellCrit + tonumber(value)
+ _,_, value = strfind(left:GetText(), "(.+) %(%d/%d%)")
+ if value then
+ SET_NAME = value
+ end
+ _, _, value = strfind(left:GetText(), L["^Set: Improves your chance to get a critical strike with spells by (%d)%%."])
+ if value and SET_NAME and not tContains(Crit_Set_Bonus, SET_NAME) then
+ tinsert(Crit_Set_Bonus, SET_NAME)
+ BCScache["gear"].spell_crit = BCScache["gear"].spell_crit + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), "(%d)%% Spell Critical Strike")
+ if value then
+ BCScache["gear"].spell_crit = BCScache["gear"].spell_crit + tonumber(value)
+ end
end
-
- local _,_, value = strfind(left:GetText(), "(%d)%% Spell Critical Strike")
- if value then
- spellCrit = spellCrit + tonumber(value)
+ end
+ end
+ end
+ if BCS_Tooltip:SetInventoryItem("player", 16) then
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local found = strfind(left:GetText(), "Brilliant Wizard Oil")
+ if found then
+ BCScache["gear"].spell_crit = BCScache["gear"].spell_crit + 1
end
-
end
end
end
-
- end
-
- -- buffs
- local _, _, critFromAura = BCS:GetPlayerAura(L["Chance for a critical hit with a spell increased by (%d+)%%."])
- if critFromAura then
- spellCrit = spellCrit + tonumber(critFromAura)
- end
- _, _, critFromAura = BCS:GetPlayerAura("(Moonkin Aura)")
- if critFromAura then
- spellCrit = spellCrit + 3
- end
- _, _, critFromAura = BCS:GetPlayerAura("Power of the Guardian Crit")
- if critFromAura then
- spellCrit = spellCrit + tonumber(critFromAura)
- end
- _, _, critFromAura = BCS:GetPlayerAura("Chance to get a critical strike with spells is increased by (%d+)%%")
- if critFromAura then
- spellCrit = spellCrit + tonumber(critFromAura)
- end
- _, _, critFromAura = BCS:GetPlayerAura(L["While active, target's critical hit chance with spells and attacks increases by 10%%."])
- if critFromAura then
- spellCrit = spellCrit + 10
- end
- _, _, critFromAura = BCS:GetPlayerAura(L["Increases chance for a melee, ranged, or spell critical by (%d+)%% and all attributes by %d+."])
- if critFromAura then
- spellCrit = spellCrit + tonumber(critFromAura)
- end
- critFromAura = BCS:GetPlayerAura(L["Increases critical chance of spells by 10%%, melee and ranged by 5%% and grants 140 attack power. 120 minute duration."])
- if critFromAura then
- spellCrit = spellCrit + 10
- end
- _, _, critFromAura = BCS:GetPlayerAura(L["Critical strike chance with spells and melee attacks increased by (%d+)%%."])
- if critFromAura then
- spellCrit = spellCrit + tonumber(critFromAura)
- end
- -- debuffs
- _, _, _, critFromAura = BCS:GetPlayerAura(L["Melee critical-hit chance reduced by (%d+)%%.\r\nSpell critical-hit chance reduced by (%d+)%%."], 'HARMFUL')
- if critFromAura then
- spellCrit = spellCrit - tonumber(critFromAura)
end
- return spellCrit
-end
-
-function BCS:GetSpellPower(school)
- if school then
- if not L["Equip: Increases damage done by "..school.." spells and effects by up to (%d+)."] then return -1 end
- local spellPower = 0;
- local MAX_INVENTORY_SLOTS = 19
-
- for slot=0, MAX_INVENTORY_SLOTS do
- local hasItem = BCS_Tooltip:SetInventoryItem("player", slot)
-
- if hasItem then
+ if BCS.needScanAuras then
+ BCScache["auras"].spell_crit = 0
+ -- buffs
+ local _, _, critFromAura = BCS:GetPlayerAura(L["Chance for a critical hit with a spell increased by (%d+)%%."])
+ if critFromAura then
+ BCScache["auras"].spell_crit = BCScache["auras"].spell_crit + tonumber(critFromAura)
+ end
+ _, _, critFromAura = BCS:GetPlayerAura("(Moonkin Aura)")
+ if critFromAura then
+ BCScache["auras"].spell_crit = BCScache["auras"].spell_crit + 3
+ if BCS:GetPlayerAura("Moonkin Form") and BCS_Tooltip:SetInventoryItem("player", 18) then
for line=1, BCS_Tooltip:NumLines() do
local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
-
if left:GetText() then
- local _,_, value = strfind(left:GetText(), L["Equip: Increases damage done by "..school.." spells and effects by up to (%d+)."])
- if value then
- spellPower = spellPower + tonumber(value)
- end
- if L[school.." Damage %+(%d+)"] then
- _,_, value = strfind(left:GetText(), L[school.." Damage %+(%d+)"])
- if value then
- spellPower = spellPower + tonumber(value)
- end
+ _, _, critFromAura = strfind(left:GetText(), L["Equip: Increases the critical chance provided by Leader of the Pack and Moonkin Aura by (%d)%%."])
+ if critFromAura then
+ BCScache["auras"].spell_crit = BCScache["auras"].spell_crit + tonumber(critFromAura)
end
- if L["^%+(%d+) "..school.." Spell Damage"] then
- _,_, value = strfind(left:GetText(), L["^%+(%d+) "..school.." Spell Damage"])
- if value then
- spellPower = spellPower + tonumber(value)
+ end
+ end
+ else
+ --check if druid is shapeshifted and have Idol of the Moonfang equipped
+ for i=1, GetNumPartyMembers() do
+ local _, partyClass = UnitClass("party"..i)
+ if partyClass == "DRUID" then
+ if BCS_Tooltip:SetInventoryItem("party"..i, 18) then
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ _, _, critFromAura = strfind(left:GetText(), L["Equip: Increases the critical chance provided by Leader of the Pack and Moonkin Aura by (%d)%%."])
+ if critFromAura then
+ for buff = 1, 32 do
+ if UnitBuff("party"..i, buff) and UnitBuff("party"..i, buff) == "Interface\\Icons\\Spell_Nature_ForceOfNature" then
+ BCScache["auras"].spell_crit = BCScache["auras"].spell_crit + tonumber(critFromAura)
+ break
+ end
+ end
+ end
+ end
end
end
end
end
end
-
end
-
- return spellPower
- else
- local spellPower = 0;
- local arcanePower = 0;
- local firePower = 0;
- local frostPower = 0;
- local holyPower = 0;
- local naturePower = 0;
- local shadowPower = 0;
- local damagePower = 0;
- local MAX_INVENTORY_SLOTS = 19
-
- local SpellPower_Set_Bonus = {}
-
- -- scan gear
- for slot=0, MAX_INVENTORY_SLOTS do
- local hasItem = BCS_Tooltip:SetInventoryItem("player", slot)
-
- if hasItem then
- local SET_NAME
-
+ critFromAura = BCS:GetPlayerAura("Inner Focus")
+ if critFromAura then
+ BCScache["auras"].spell_crit = BCScache["auras"].spell_crit + 25
+ end
+ _, _, critFromAura = BCS:GetPlayerAura("Power of the Guardian Crit")
+ if critFromAura then
+ BCScache["auras"].spell_crit = BCScache["auras"].spell_crit + tonumber(critFromAura)
+ end
+ _, _, critFromAura = BCS:GetPlayerAura("Chance to get a critical strike with spells is increased by (%d+)%%")
+ if critFromAura then
+ BCScache["auras"].spell_crit = BCScache["auras"].spell_crit + tonumber(critFromAura)
+ end
+ _, _, critFromAura = BCS:GetPlayerAura(L["While active, target's critical hit chance with spells and attacks increases by 10%%."])--SoD spell? 23964
+ if critFromAura then
+ BCScache["auras"].spell_crit = BCScache["auras"].spell_crit + 10
+ end
+ _, _, critFromAura = BCS:GetPlayerAura(L["Increases chance for a melee, ranged, or spell critical by (%d+)%% and all attributes by %d+."])
+ if critFromAura then
+ BCScache["auras"].spell_crit = BCScache["auras"].spell_crit + tonumber(critFromAura)
+ end
+ critFromAura = BCS:GetPlayerAura(L["Increases critical chance of spells by 10%%, melee and ranged by 5%% and grants 140 attack power. 120 minute duration."])
+ if critFromAura then
+ BCScache["auras"].spell_crit = BCScache["auras"].spell_crit + 10
+ end
+ _, _, critFromAura = BCS:GetPlayerAura(L["Critical strike chance with spells and melee attacks increased by (%d+)%%."])
+ if critFromAura then
+ BCScache["auras"].spell_crit = BCScache["auras"].spell_crit + tonumber(critFromAura)
+ end
+ -- debuffs
+ _, _, _, critFromAura = BCS:GetPlayerAura(L["Melee critical-hit chance reduced by (%d+)%%.\r\nSpell critical-hit chance reduced by (%d+)%%."], 'HARMFUL')
+ if critFromAura then
+ BCScache["auras"].spell_crit = BCScache["auras"].spell_crit - tonumber(critFromAura)
+ end
+ end
+
+ -- scan talents
+ if BCS.needScanTalents then
+ BCScache["talents"].spell_crit = 0
+ for tab=1, GetNumTalentTabs() do
+ for talent=1, GetNumTalents(tab) do
+ BCS_Tooltip:SetTalent(tab, talent)
for line=1, BCS_Tooltip:NumLines() do
local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
-
if left:GetText() then
- local _,_, value = strfind(left:GetText(), L["Equip: Increases damage and healing done by magical spells and effects by up to (%d+)."])
- if value then
- spellPower = spellPower + tonumber(value)
- end
- _,_, value = strfind(left:GetText(), "Equip: Increases your spell damage by up to (%d+)")
- if value then
- spellPower = spellPower + tonumber(value)
- end
- _,_, value = strfind(left:GetText(), L["Spell Damage %+(%d+)"])
- if value then
- spellPower = spellPower + tonumber(value)
- end
- _,_, value = strfind(left:GetText(), L["^%+(%d+) Spell Damage and Healing"])
- if value then
- spellPower = spellPower + tonumber(value)
- end
- _,_, value = strfind(left:GetText(), L["^%+(%d+) Damage and Healing Spells"])
- if value then
- spellPower = spellPower + tonumber(value)
- end
-
- _,_, value = strfind(left:GetText(), L["Equip: Increases damage done by Arcane spells and effects by up to (%d+)."])
- if value then
- arcanePower = arcanePower + tonumber(value)
- end
- _,_, value = strfind(left:GetText(), L["^%+(%d+) Arcane Spell Damage"])
- if value then
- arcanePower = arcanePower + tonumber(value)
- end
-
- _,_, value = strfind(left:GetText(), L["Equip: Increases damage done by Fire spells and effects by up to (%d+)."])
- if value then
- firePower = firePower + tonumber(value)
+ local _, _, _, _, rank = GetTalentInfo(tab, talent)
+ -- Arcane Instability
+ local _,_, value = strfind(left:GetText(), L["Increases your spell damage and critical srike chance by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].spell_crit = BCScache["talents"].spell_crit + tonumber(value)
+ break
end
- _,_, value = strfind(left:GetText(), L["Fire Damage %+(%d+)"])
- if value then
- firePower = firePower + tonumber(value)
+ end
+ end
+ end
+ end
+ end
+
+ spellCrit = spellCrit + BCScache["talents"].spell_crit + BCScache["gear"].spell_crit + BCScache["auras"].spell_crit
+
+ return spellCrit
+end
+
+function BCS:GetSpellCritFromClass(class)
+ if not class then
+ return 0, 0, 0, 0, 0, 0
+ end
+
+ if class == "PALADIN" then
+ --scan talents
+ if BCS.needScanTalents or BCS.needScanAuras then
+ BCScache["talents"].paladin_holy_light = 0
+ BCScache["talents"].paladin_flash = 0
+ BCScache["talents"].paladin_shock = 0
+ for tab=1, GetNumTalentTabs() do
+ for talent=1, GetNumTalents(tab) do
+ BCS_Tooltip:SetTalent(tab, talent)
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _, _, _, _, rank = GetTalentInfo(tab, talent)
+ -- Holy Power
+ local _,_, value = strfind(left:GetText(), L["Increases the critical effect chance of your Holy Light and Flash of Light by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].paladin_holy_light = BCScache["talents"].paladin_holy_light + tonumber(value)
+ BCScache["talents"].paladin_flash = BCScache["talents"].paladin_flash + tonumber(value)
+ break
+ end
+ -- Divine Favor
+ _,_, value = strfind(left:GetText(), L["Improves your chance to get a critical strike with Holy Shock by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].paladin_shock = BCScache["talents"].paladin_shock + tonumber(value)
+ break
+ end
end
- _,_, value = strfind(left:GetText(), L["^%+(%d+) Fire Spell Damage"])
- if value then
- firePower = firePower + tonumber(value)
+ end
+ end
+ end
+ end
+
+ return BCScache["talents"].paladin_holy_light,
+ BCScache["talents"].paladin_flash,
+ BCScache["talents"].paladin_shock, 0, 0, 0
+
+ elseif class == "DRUID" then
+ --scan talents
+ if BCS.needScanTalents then
+ BCScache["talents"].druid_moonfire = 0
+ BCScache["talents"].druid_regrowth = 0
+ -- scan talents
+ for tab=1, GetNumTalentTabs() do
+ for talent=1, GetNumTalents(tab) do
+ BCS_Tooltip:SetTalent(tab, talent)
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _, _, _, _, rank = GetTalentInfo(tab, talent)
+ -- Improved Moonfire
+ local _,_, value = strfind(left:GetText(), L["Increases the damage and critical strike chance of your Moonfire spell by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].druid_moonfire = BCScache["talents"].druid_moonfire + tonumber(value)
+ break
+ end
+ -- Improved Regrowth
+ _,_, value = strfind(left:GetText(), L["Increases the critical effect chance of your Regrowth spell by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].druid_regrowth = BCScache["talents"].druid_regrowth + tonumber(value)
+ break
+ end
end
-
- _,_, value = strfind(left:GetText(), L["Equip: Increases damage done by Frost spells and effects by up to (%d+)."])
- if value then
- frostPower = frostPower + tonumber(value)
+ end
+ end
+ end
+ end
+
+ return BCScache["talents"].druid_moonfire,
+ BCScache["talents"].druid_regrowth, 0, 0, 0, 0
+
+ elseif class == "WARLOCK" then
+ --scan talents
+ if BCS.needScanTalents then
+ BCScache["talents"].warlock_destruction_spells = 0
+ BCScache["talents"].warlock_searing_pain = 0
+ -- scan talents
+ for tab=1, GetNumTalentTabs() do
+ for talent=1, GetNumTalents(tab) do
+ BCS_Tooltip:SetTalent(tab, talent)
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _, _, _, _, rank = GetTalentInfo(tab, talent)
+ -- Devastation
+ local _,_, value = strfind(left:GetText(), L["Increases the critical strike chance of your Destruction spells by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].warlock_destruction_spells = BCScache["talents"].warlock_destruction_spells + tonumber(value)
+ BCScache["talents"].warlock_searing_pain = BCScache["talents"].warlock_searing_pain + tonumber(value)
+ break
+ end
+ -- Improved Searing Pain
+ _,_, value = strfind(left:GetText(), L["Increases the critical strike chance of your Searing Pain spell by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].warlock_searing_pain = BCScache["talents"].warlock_searing_pain + tonumber(value)
+ break
+ end
end
- _,_, value = strfind(left:GetText(), L["Frost Damage %+(%d+)"])
- if value then
- frostPower = frostPower + tonumber(value)
+ end
+ end
+ end
+ end
+
+ return BCScache["talents"].warlock_destruction_spells,
+ BCScache["talents"].warlock_searing_pain, 0, 0, 0, 0
+
+ elseif class == "MAGE" then
+ --scan talents
+ if BCS.needScanTalents or BCS.needScanAuras then
+ BCScache["talents"].mage_arcane_spells = 0
+ BCScache["talents"].mage_fire_spells = 0
+ BCScache["talents"].mage_fireblast = 0
+ BCScache["talents"].mage_scorch = 0
+ BCScache["talents"].mage_flamestrike = 0
+ BCScache["talents"].mage_shatter = 0
+ -- scan talents
+ for tab=1, GetNumTalentTabs() do
+ for talent=1, GetNumTalents(tab) do
+ BCS_Tooltip:SetTalent(tab, talent)
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _, _, _, _, rank = GetTalentInfo(tab, talent)
+ -- Arcane Impact
+ local _,_, value = strfind(left:GetText(), L["Increases the critical strike chance of your Arcane Explosion and Arcane Missiles spells by an additional (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].mage_arcane_spells = BCScache["talents"].mage_arcane_spells + tonumber(value)
+ break
+ end
+ -- Incinerate
+ _,_, value = strfind(left:GetText(), L["Increases the critical strike chance of your Fire Blast and Scorch spells by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].mage_fireblast = BCScache["talents"].mage_fireblast + tonumber(value)
+ BCScache["talents"].mage_scorch = BCScache["talents"].mage_scorch + tonumber(value)
+ break
+ end
+ -- Improved Flamestrike
+ _,_, value = strfind(left:GetText(), L["Increases the critical strike chance of your Flamestrike spell by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].mage_flamestrike = BCScache["talents"].mage_flamestrike + tonumber(value)
+ break
+ end
+ -- Critical Mass
+ _,_, value = strfind(left:GetText(), L["Increases the critical strike chance of your Fire spells by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].mage_fire_spells = BCScache["talents"].mage_fire_spells + tonumber(value)
+ BCScache["talents"].mage_fireblast = BCScache["talents"].mage_fireblast + tonumber(value)
+ BCScache["talents"].mage_flamestrike = BCScache["talents"].mage_flamestrike + tonumber(value)
+ BCScache["talents"].mage_scorch = BCScache["talents"].mage_scorch + tonumber(value)
+ break
+ end
+ -- Shatter
+ _,_, value = strfind(left:GetText(), L["Increases the critical strike chance of all your spells against frozen targets by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].mage_shatter = BCScache["talents"].mage_shatter + tonumber(value)
+ break
+ end
end
- _,_, value = strfind(left:GetText(), L["^%+(%d+) Frost Spell Damage"])
- if value then
- frostPower = frostPower + tonumber(value)
+ end
+ end
+ end
+ -- Buffs
+ local _, _, value = BCS:GetPlayerAura(L["Increases critical strike chance from Fire damage spells by (%d+)%%."])
+ -- Combustion
+ if value then
+ BCScache["talents"].mage_fire_spells = BCScache["talents"].mage_fire_spells + tonumber(value)
+ BCScache["talents"].mage_fireblast = BCScache["talents"].mage_fireblast + tonumber(value)
+ BCScache["talents"].mage_flamestrike = BCScache["talents"].mage_flamestrike + tonumber(value)
+ BCScache["talents"].mage_scorch = BCScache["talents"].mage_scorch + tonumber(value)
+ end
+ end
+
+ return BCScache["talents"].mage_arcane_spells,
+ BCScache["talents"].mage_fire_spells,
+ BCScache["talents"].mage_fireblast,
+ BCScache["talents"].mage_scorch,
+ BCScache["talents"].mage_flamestrike,
+ BCScache["talents"].mage_shatter
+
+ elseif class == "PRIEST" then
+ if BCS.needScanTalents then
+ BCScache["talents"].priest_holy_spells = 0
+ BCScache["talents"].priest_discipline_spells = 0
+ BCScache["talents"].priest_offensive_spells = 0
+ -- scan talents
+ for tab=1, GetNumTalentTabs() do
+ for talent=1, GetNumTalents(tab) do
+ BCS_Tooltip:SetTalent(tab, talent)
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _, _, _, _, rank = GetTalentInfo(tab, talent)
+ -- Divinity
+ local _,_, value = strfind(left:GetText(), L["Increases the critical effect chance of your Holy and Discipline spells by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].priest_holy_spells = BCScache["talents"].priest_holy_spells + tonumber(value)
+ BCScache["talents"].priest_discipline_spells = BCScache["talents"].priest_discipline_spells + tonumber(value)
+ break
+ end
+ -- Force of Will
+ _,_, value = strfind(left:GetText(), "Increases your spell damage and the critical strike chance of your offensive spells by (%d+)%%")
+ if value and rank > 0 then
+ BCScache["talents"].priest_offensive_spells = BCScache["talents"].priest_offensive_spells + tonumber(value)
+ break
+ end
end
-
- _,_, value = strfind(left:GetText(), L["Equip: Increases damage done by Holy spells and effects by up to (%d+)."])
- if value then
- holyPower = holyPower + tonumber(value)
+ end
+ end
+ end
+ end
+ -- scan gear
+ if BCS.needScanGear then
+ -- t1 set gives + 2% crit to holy and 25% to prayer of healing
+ BCScache["gear"].priest_holy_spells = 0
+ BCScache["gear"].priest_prayer = 0
+ local Crit_Set_Bonus = {}
+ for slot=1, 19 do
+ if BCS_Tooltip:SetInventoryItem('player', slot) then
+ local _, _, eqItemLink = strfind(GetInventoryItemLink('player', slot), "(item:%d+:%d+:%d+:%d+)")
+ if eqItemLink then BCS_Tooltip:ClearLines() BCS_Tooltip:SetHyperlink(eqItemLink) end
+ local SET_NAME = nil
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _,_, value = strfind(left:GetText(), "(.+) %(%d/%d%)")
+ if value then
+ SET_NAME = value
+ end
+ _, _, value = strfind(left:GetText(), L["^Set: Improves your chance to get a critical strike with Holy spells by (%d)%%."])
+ if value and SET_NAME and not tContains(Crit_Set_Bonus, SET_NAME) then
+ tinsert(Crit_Set_Bonus, SET_NAME)
+ BCScache["gear"].priest_holy_spells = BCScache["gear"].priest_holy_spells + tonumber(value)
+ end
+ _, _, value = strfind(left:GetText(), L["^Set: Increases your chance of a critical hit with Prayer of Healing by (%d+)%%."])
+ if value and SET_NAME and not tContains(Crit_Set_Bonus, SET_NAME) then
+ tinsert(Crit_Set_Bonus, SET_NAME)
+ BCScache["gear"].priest_prayer = BCScache["gear"].priest_prayer + tonumber(value)
+ end
end
- _,_, value = strfind(left:GetText(), L["^%+(%d+) Holy Spell Damage"])
- if value then
- holyPower = holyPower + tonumber(value)
+ end
+ end
+ end
+ end
+
+ local holySpells = BCScache["talents"].priest_holy_spells + BCScache["gear"].priest_holy_spells
+
+ return holySpells,
+ BCScache["talents"].priest_discipline_spells,
+ BCScache["talents"].priest_offensive_spells,
+ BCScache["gear"].priest_prayer, 0, 0
+
+ elseif class == "SHAMAN" then
+ if BCS.needScanTalents then
+ BCScache["talents"].shaman_lightning_bolt = 0
+ BCScache["talents"].shaman_chain_lightning = 0
+ BCScache["talents"].shaman_lightning_shield = 0
+ BCScache["talents"].shaman_firefrost_spells = 0
+ BCScache["talents"].shaman_healing_spells = 0
+ -- scan talents
+ for tab=1, GetNumTalentTabs() do
+ for talent=1, GetNumTalents(tab) do
+ BCS_Tooltip:SetTalent(tab, talent)
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _, _, _, _, rank = GetTalentInfo(tab, talent)
+ -- Call of Thunder
+ local _,_, value = strfind(left:GetText(), L["Increases the critical strike chance of your Lightning Bolt and Chain Lightning spells by an additional (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].shaman_lightning_bolt = BCScache["talents"].shaman_lightning_bolt + tonumber(value)
+ BCScache["talents"].shaman_chain_lightning = BCScache["talents"].shaman_chain_lightning + tonumber(value)
+ break
+ end
+ -- Tidal Mastery
+ _,_, value = strfind(left:GetText(), L["Increases the critical effect chance of your healing and lightning spells by (%d+)%%."])
+ if value and rank > 0 then
+ BCScache["talents"].shaman_lightning_bolt = BCScache["talents"].shaman_lightning_bolt + tonumber(value)
+ BCScache["talents"].shaman_chain_lightning = BCScache["talents"].shaman_chain_lightning + tonumber(value)
+ BCScache["talents"].shaman_lightning_shield = BCScache["talents"].shaman_lightning_shield + tonumber(value)
+ BCScache["talents"].shaman_healing_spells = BCScache["talents"].shaman_healing_spells + tonumber(value)
+ break
+ end
end
-
- _,_, value = strfind(left:GetText(), L["Equip: Increases damage done by Nature spells and effects by up to (%d+)."])
- if value then
- naturePower = naturePower + tonumber(value)
+ end
+ end
+ end
+ end
+ -- buffs
+ if BCS.needScanAuras then
+ BCScache["auras"].shaman_lightning_bolt = 0
+ BCScache["auras"].shaman_chain_lightning = 0
+ BCScache["auras"].shaman_firefrost_spells = 0
+ local hasAura = BCS:GetPlayerAura("Elemental Mastery")
+ if hasAura then
+ BCScache["auras"].shaman_lightning_bolt = 100
+ BCScache["auras"].shaman_chain_lightning = 100
+ BCScache["auras"].shaman_firefrost_spells = 100
+ end
+ end
+
+ local lightningBolt = BCScache["auras"].shaman_lightning_bolt + BCScache["talents"].shaman_lightning_bolt
+ local chainLightning = BCScache["auras"].shaman_chain_lightning + BCScache["talents"].shaman_chain_lightning
+
+ return lightningBolt, chainLightning,
+ BCScache["talents"].shaman_lightning_shield,
+ BCScache["auras"].shaman_firefrost_spells,
+ BCScache["talents"].shaman_healing_spells, 0
+
+ else
+ return 0, 0, 0, 0, 0, 0
+ end
+end
+
+local impInnerFire = nil
+local spiritualGuidance = nil
+function BCS:GetSpellPower(school)
+ if school then
+ local spellPower = 0;
+ --scan gear
+ if BCS.needScanGear then
+ if school == "Arcane" then BCScache["gear"].arcane = 0
+ elseif school == "Fire" then BCScache["gear"].fire = 0
+ elseif school == "Frost" then BCScache["gear"].frost = 0
+ elseif school == "Holy" then BCScache["gear"].holy = 0
+ elseif school == "Nature" then BCScache["gear"].nature = 0
+ elseif school == "Shadow" then BCScache["gear"].shadow = 0
+ end
+ for slot=1, 19 do
+ if BCS_Tooltip:SetInventoryItem("player", slot) then
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _,_, value = strfind(left:GetText(), L["Equip: Increases damage done by "..school.." spells and effects by up to (%d+)."])
+ if value then
+ spellPower = spellPower + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), school.." Damage %+(%d+)")
+ if value then
+ spellPower = spellPower + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), "^%+(%d+) "..school.." Spell Damage")
+ if value then
+ spellPower = spellPower + tonumber(value)
+ end
end
- _,_, value = strfind(left:GetText(), L["^%+(%d+) Nature Spell Damage"])
- if value then
- naturePower = naturePower + tonumber(value)
+ end
+ end
+ end
+ if school == "Arcane" then BCScache["gear"].arcane = spellPower
+ elseif school == "Fire" then BCScache["gear"].fire = spellPower
+ elseif school == "Frost" then BCScache["gear"].frost = spellPower
+ elseif school == "Holy" then BCScache["gear"].holy = spellPower
+ elseif school == "Nature" then BCScache["gear"].nature = spellPower
+ elseif school == "Shadow" then BCScache["gear"].shadow = spellPower
+ end
+ else
+ if school == "Arcane" then spellPower = BCScache["gear"].arcane
+ elseif school == "Fire" then spellPower = BCScache["gear"].fire
+ elseif school == "Frost" then spellPower = BCScache["gear"].frost
+ elseif school == "Holy" then spellPower = BCScache["gear"].holy
+ elseif school == "Nature" then spellPower = BCScache["gear"].nature
+ elseif school == "Shadow" then spellPower = BCScache["gear"].shadow
+ end
+ end
+
+ return spellPower
+ else
+ local damageAndHealing = 0
+ local damageOnly = 0
+ local SpellPower_Set_Bonus = {}
+ if BCS.needScanGear then
+ BCScache["gear"].damage_and_healing = 0
+ BCScache["gear"].only_damage = 0
+ BCScache["gear"].arcane = 0
+ BCScache["gear"].fire = 0
+ BCScache["gear"].frost = 0
+ BCScache["gear"].holy = 0
+ BCScache["gear"].nature = 0
+ BCScache["gear"].shadow = 0
+ -- scan gear
+ for slot=1, 19 do
+ if BCS_Tooltip:SetInventoryItem('player', slot) then
+ local _, _, eqItemLink = strfind(GetInventoryItemLink('player', slot), "(item:%d+:%d+:%d+:%d+)")
+ if eqItemLink then BCS_Tooltip:ClearLines() BCS_Tooltip:SetHyperlink(eqItemLink) end
+ local SET_NAME
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ -- generic bonus on most gear
+ local _,_, value = strfind(left:GetText(), L["Equip: Increases damage and healing done by magical spells and effects by up to (%d+)."])
+ if value then
+ BCScache["gear"].damage_and_healing = BCScache["gear"].damage_and_healing + tonumber(value)
+ end
+ -- Spell Power (weapon/bracer enchant) apparently gives healing too
+ -- Arcanum of Focus (Head/Legs enchant)
+ -- Power of the Scourge (Shoulder enchant)
+ _,_, value = strfind(left:GetText(), "Spell Damage %+(%d+)")
+ if value then
+ BCScache["gear"].damage_and_healing = BCScache["gear"].damage_and_healing + tonumber(value)
+ end
+ -- Atiesh (druid/priest)
+ _,_, value = strfind(left:GetText(), "Equip: Increases your spell damage by up to (%d+) and your healing by up to %d+.")
+ if value then
+ BCScache["gear"].only_damage = BCScache["gear"].only_damage + tonumber(value)
+ end
+ -- Zandalar Signet of Mojo (Shoulder enchant)
+ _,_, value = strfind(left:GetText(), L["^%+(%d+) Spell Damage and Healing"])
+ if value then
+ BCScache["gear"].damage_and_healing = BCScache["gear"].damage_and_healing + tonumber(value)
+ end
+ -- Enchanted Armor Kit (Leatherworking)
+ _,_, value = strfind(left:GetText(), L["^%+(%d+) Damage and Healing Spells"])
+ if value then
+ BCScache["gear"].damage_and_healing = BCScache["gear"].damage_and_healing + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["Equip: Increases damage done by Arcane spells and effects by up to (%d+)."])
+ if value then
+ BCScache["gear"].arcane = BCScache["gear"].arcane + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["^%+(%d+) Arcane Spell Damage"])
+ if value then
+ BCScache["gear"].arcane = BCScache["gear"].arcane + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["Arcane Damage %+(%d+)"])
+ if value then
+ BCScache["gear"].arcane = BCScache["gear"].arcane + tonumber(value)
+ end
+
+ _,_, value = strfind(left:GetText(), L["Equip: Increases damage done by Fire spells and effects by up to (%d+)."])
+ if value then
+ BCScache["gear"].fire = BCScache["gear"].fire + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["Fire Damage %+(%d+)"])
+ if value then
+ BCScache["gear"].fire = BCScache["gear"].fire + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["^%+(%d+) Fire Spell Damage"])
+ if value then
+ BCScache["gear"].fire = BCScache["gear"].fire + tonumber(value)
+ end
+
+ _,_, value = strfind(left:GetText(), L["Equip: Increases damage done by Frost spells and effects by up to (%d+)."])
+ if value then
+ BCScache["gear"].frost = BCScache["gear"].frost + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["Frost Damage %+(%d+)"])
+ if value then
+ BCScache["gear"].frost = BCScache["gear"].frost + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["^%+(%d+) Frost Spell Damage"])
+ if value then
+ BCScache["gear"].frost = BCScache["gear"].frost + tonumber(value)
+ end
+
+ _,_, value = strfind(left:GetText(), L["Equip: Increases damage done by Holy spells and effects by up to (%d+)."])
+ if value then
+ BCScache["gear"].holy = BCScache["gear"].holy + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["^%+(%d+) Holy Spell Damage"])
+ if value then
+ BCScache["gear"].holy = BCScache["gear"].holy + tonumber(value)
+ end
+
+ _,_, value = strfind(left:GetText(), L["Equip: Increases damage done by Nature spells and effects by up to (%d+)."])
+ if value then
+ BCScache["gear"].nature = BCScache["gear"].nature + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["^%+(%d+) Nature Spell Damage"])
+ if value then
+ BCScache["gear"].nature = BCScache["gear"].nature + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), "Nature Damage %+(%d+)")
+ if value then
+ BCScache["gear"].nature = BCScache["gear"].nature + tonumber(value)
+ end
+
+ _,_, value = strfind(left:GetText(), L["Equip: Increases damage done by Shadow spells and effects by up to (%d+)."])
+ if value then
+ BCScache["gear"].shadow = BCScache["gear"].shadow + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["Shadow Damage %+(%d+)"])
+ if value then
+ BCScache["gear"].shadow = BCScache["gear"].shadow + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["^%+(%d+) Shadow Spell Damage"])
+ if value then
+ BCScache["gear"].shadow = BCScache["gear"].shadow + tonumber(value)
+ end
+
+ _,_, value = strfind(left:GetText(), "(.+) %(%d/%d%)")
+ if value then
+ SET_NAME = value
+ end
+
+ _, _, value = strfind(left:GetText(), L["^Set: Increases damage and healing done by magical spells and effects by up to (%d+)%."])
+ if value and SET_NAME and not tContains(SpellPower_Set_Bonus, SET_NAME) then
+ tinsert(SpellPower_Set_Bonus, SET_NAME)
+ BCScache["gear"].damage_and_healing = BCScache["gear"].damage_and_healing + tonumber(value)
+ end
end
-
- _,_, value = strfind(left:GetText(), L["Equip: Increases damage done by Shadow spells and effects by up to (%d+)."])
- if value then
- shadowPower = shadowPower + tonumber(value)
+ end
+ end
+ end
+ -- SetHyperLink doesnt show temporary enhancements, have to use SetInventoryItem
+ if BCS_Tooltip:SetInventoryItem("player", 16) then
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ -- apparently gives healing too
+ local found = strfind(left:GetText(), "Brilliant Wizard Oil")
+ if found then
+ BCScache["gear"].damage_and_healing = BCScache["gear"].damage_and_healing + 36
+ break
end
- _,_, value = strfind(left:GetText(), L["Shadow Damage %+(%d+)"])
- if value then
- shadowPower = shadowPower + tonumber(value)
+ found = strfind(left:GetText(), "Lesser Wizard Oil")
+ if found then
+ BCScache["gear"].damage_and_healing = BCScache["gear"].damage_and_healing + 16
+ break
end
- _,_, value = strfind(left:GetText(), L["^%+(%d+) Shadow Spell Damage"])
- if value then
- shadowPower = shadowPower + tonumber(value)
+ found = strfind(left:GetText(), "Minor Wizard Oil")
+ if found then
+ BCScache["gear"].damage_and_healing = BCScache["gear"].damage_and_healing + 8
+ break
end
-
- _,_, value = strfind(left:GetText(), "(.+) %(%d/%d%)")
- if value then
- SET_NAME = value
+ found = strfind(left:GetText(), "Wizard Oil")
+ if found then
+ BCScache["gear"].damage_and_healing = BCScache["gear"].damage_and_healing + 24
+ break
end
+ end
+ end
+ end
+ end
- _, _, value = strfind(left:GetText(), L["^Set: Increases damage and healing done by magical spells and effects by up to (%d+)%."])
- if value and SET_NAME and not tContains(SpellPower_Set_Bonus, SET_NAME) then
- tinsert(SpellPower_Set_Bonus, SET_NAME)
- spellPower = spellPower + tonumber(value)
+ if BCS.needScanTalents then
+ impInnerFire = nil
+ spiritualGuidance = nil
+ -- scan talents
+ for tab=1, GetNumTalentTabs() do
+ for talent=1, GetNumTalents(tab) do
+ BCS_Tooltip:SetTalent(tab, talent)
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _, _, _, _, rank = GetTalentInfo(tab, talent)
+ -- Priest
+ -- Spiritual Guidance
+ local _,_, value = strfind(left:GetText(), L["Increases spell damage and healing by up to (%d+)%% of your total Spirit."])
+ if value and rank > 0 then
+ spiritualGuidance = tonumber(value)
+ break
+ end
+ -- Improved Inner Fire
+ _,_, value = strfind(left:GetText(), "Increases the effects of your Inner Fire spell by (%d+)%%.")
+ if value and rank > 0 then
+ impInnerFire = tonumber(value)
+ break
+ end
end
-
end
end
end
-
end
-
- -- scan talents
- local MAX_TABS = GetNumTalentTabs()
-
- for tab=1, MAX_TABS do
- local MAX_TALENTS = GetNumTalents(tab)
-
- for talent=1, MAX_TALENTS do
- BCS_Tooltip:SetTalent(tab, talent)
- local MAX_LINES = BCS_Tooltip:NumLines()
-
- for line=1, MAX_LINES do
- local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
- if left:GetText() then
- -- Priest
- -- Spiritual Guidance
- local _,_, value = strfind(left:GetText(), L["Increases spell damage and healing by up to (%d+)%% of your total Spirit."])
- local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tab, talent)
- if value and rank > 0 then
- local stat, effectiveStat = UnitStat("player", 5)
- spellPower = spellPower + floor(((tonumber(value) / 100) * effectiveStat))
-
- -- nothing more is currenlty supported, break out of the loops
- line = MAX_LINES
- talent = MAX_TALENTS
- tab = MAX_TABS
- end
- end
+ if BCS.needScanAuras then
+ BCScache["auras"].damage_and_healing = 0
+ BCScache["auras"].only_damage = 0
+ -- buffs
+ local _, _, spellPowerFromAura = BCS:GetPlayerAura(L["Magical damage dealt is increased by up to (%d+)."])
+ if spellPowerFromAura then
+ BCScache["auras"].only_damage = BCScache["auras"].only_damage + tonumber(spellPowerFromAura)
+ end
+ _, _, spellPowerFromAura = BCS:GetPlayerAura("Increases damage and healing done by magical spells and effects by up to (%d+).")
+ if spellPowerFromAura then
+ BCScache["auras"].damage_and_healing = BCScache["auras"].damage_and_healing + tonumber(spellPowerFromAura)
+ end
+ -- Dreamtonic/Arcane Elixir
+ _, _, spellPowerFromAura = BCS:GetPlayerAura("Magical damage dealt by spells and abilities is increased by up to (%d+)")
+ if spellPowerFromAura then
+ BCScache["auras"].only_damage = BCScache["auras"].only_damage + tonumber(spellPowerFromAura)
+ end
+ -- Dreamshard Elixir
+ _, _, spellPowerFromAura = BCS:GetPlayerAura("Spell damage is increased by up to (%d+)")
+ if spellPowerFromAura then
+ BCScache["auras"].only_damage = BCScache["auras"].only_damage + tonumber(spellPowerFromAura)
+ end
+ -- Flask of Supreme Power
+ _, _, spellPowerFromAura = BCS:GetPlayerAura("Spell damage increased by up to (%d+)")
+ if spellPowerFromAura then
+ BCScache["auras"].only_damage = BCScache["auras"].only_damage + tonumber(spellPowerFromAura)
+ end
+ -- Danonzo's Tel'Abim Delight
+ _, _, spellPowerFromAura = BCS:GetPlayerAura("Spell Damage increased by (%d+)")
+ if spellPowerFromAura then
+ BCScache["auras"].only_damage = BCScache["auras"].only_damage + tonumber(spellPowerFromAura)
+ end
+ --Inner Fire
+ _, _, spellPowerFromAura = BCS:GetPlayerAura("Increased damage done by magical spells and effects by (%d+).")
+ if spellPowerFromAura then
+ spellPowerFromAura = tonumber(spellPowerFromAura)
+ if impInnerFire then
+ spellPowerFromAura = floor((spellPowerFromAura * (impInnerFire/100)) + (spellPowerFromAura))
end
-
+ BCScache["auras"].only_damage = BCScache["auras"].only_damage + spellPowerFromAura
end
end
-
- -- buffs
- local _, _, spellPowerFromAura = BCS:GetPlayerAura(L["Magical damage dealt is increased by up to (%d+)."])
- if spellPowerFromAura then
- spellPower = spellPower + tonumber(spellPowerFromAura)
- damagePower = damagePower + tonumber(spellPowerFromAura)
- end
-
- _, _, spellPowerFromAura = BCS:GetPlayerAura("Increases damage and healing done by magical spells and effects by up to (%d+).")
- if spellPowerFromAura then
- spellPower = spellPower + tonumber(spellPowerFromAura)
- damagePower = damagePower + tonumber(spellPowerFromAura)
- end
-
- _, _, spellPowerFromAura = BCS:GetPlayerAura("Magical damage dealt by spells and abilities is increased by up to (%d+)")
- if spellPowerFromAura then
- spellPower = spellPower + tonumber(spellPowerFromAura)
- damagePower = damagePower + tonumber(spellPowerFromAura)
- end
-
- _, _, spellPowerFromAura = BCS:GetPlayerAura("Spell damage is increased by up to (%d+)")
- if spellPowerFromAura then
- spellPower = spellPower + tonumber(spellPowerFromAura)
- damagePower = damagePower + tonumber(spellPowerFromAura)
- end
-
local secondaryPower = 0
local secondaryPowerName = ""
-
- if arcanePower > secondaryPower then
- secondaryPower = arcanePower
+
+ if BCScache["gear"].arcane > secondaryPower then
+ secondaryPower = BCScache["gear"].arcane
secondaryPowerName = L.SPELL_SCHOOL_ARCANE
end
- if firePower > secondaryPower then
- secondaryPower = firePower
+ if BCScache["gear"].fire > secondaryPower then
+ secondaryPower = BCScache["gear"].fire
secondaryPowerName = L.SPELL_SCHOOL_FIRE
end
- if frostPower > secondaryPower then
- secondaryPower = frostPower
+ if BCScache["gear"].frost > secondaryPower then
+ secondaryPower = BCScache["gear"].frost
secondaryPowerName = L.SPELL_SCHOOL_FROST
end
- if holyPower > secondaryPower then
- secondaryPower = holyPower
+ if BCScache["gear"].holy > secondaryPower then
+ secondaryPower = BCScache["gear"].holy
secondaryPowerName = L.SPELL_SCHOOL_HOLY
end
- if naturePower > secondaryPower then
- secondaryPower = naturePower
+ if BCScache["gear"].nature > secondaryPower then
+ secondaryPower = BCScache["gear"].nature
secondaryPowerName = L.SPELL_SCHOOL_NATURE
end
- if shadowPower > secondaryPower then
- secondaryPower = shadowPower
+ if BCScache["gear"].shadow > secondaryPower then
+ secondaryPower = BCScache["gear"].shadow
secondaryPowerName = L.SPELL_SCHOOL_SHADOW
end
-
- return spellPower, secondaryPower, secondaryPowerName, damagePower
+
+ if spiritualGuidance ~= nil then
+ BCScache["talents"].damage_and_healing = 0
+ local _, spirit = UnitStat("player", 5)
+ BCScache["talents"].damage_and_healing = BCScache["talents"].damage_and_healing + floor(((spiritualGuidance / 100) * spirit))
+ end
+
+ damageAndHealing = BCScache["gear"].damage_and_healing + BCScache["talents"].damage_and_healing + BCScache["auras"].damage_and_healing
+ damageOnly = BCScache["auras"].only_damage + BCScache["gear"].only_damage
+
+ return damageAndHealing, secondaryPower, secondaryPowerName, damageOnly
end
end
+local ironClad = nil
+--this is stuff that gives ONLY healing, we count stuff that gives both damage and healing in GetSpellPower
function BCS:GetHealingPower()
local healPower = 0;
local healPower_Set_Bonus = {}
- local MAX_INVENTORY_SLOTS = 19
-
- for slot=0, MAX_INVENTORY_SLOTS do
- local hasItem = BCS_Tooltip:SetInventoryItem("player", slot)
-
- if hasItem then
- local SET_NAME
-
+ --talents
+ if BCS.needScanTalents then
+ ironClad = nil
+ BCScache["talents"].healing = 0
+ for tab=1, GetNumTalentTabs() do
+ for talent=1, GetNumTalents(tab) do
+ BCS_Tooltip:SetTalent(tab, talent)
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _, _, _, _, rank = GetTalentInfo(tab, talent)
+ -- Paladin
+ -- Ironclad
+ local _,_, value = strfind(left:GetText(), L["Increases your healing power by (%d+)%% of your Armor."])
+ if value and rank > 0 then
+ ironClad = tonumber(value)
+ break
+ end
+ end
+ end
+ end
+ end
+ end
+ if BCS.needScanGear then
+ BCScache["gear"].healing = 0
+ --scan gear
+ for slot=1, 19 do
+ if BCS_Tooltip:SetInventoryItem('player', slot) then
+ local _, _, eqItemLink = strfind(GetInventoryItemLink('player', slot), "(item:%d+:%d+:%d+:%d+)")
+ if eqItemLink then BCS_Tooltip:ClearLines() BCS_Tooltip:SetHyperlink(eqItemLink) end
+ local SET_NAME
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _,_, value = strfind(left:GetText(), L["Equip: Increases healing done by spells and effects by up to (%d+)."])
+ if value then
+ BCScache["gear"].healing = BCScache["gear"].healing + tonumber(value)
+ end
+ -- Atiesh (druid/priest)
+ _,_, value = strfind(left:GetText(), "Equip: Increases your spell damage by up to %d+ and your healing by up to (%d+).")
+ if value then
+ BCScache["gear"].healing = BCScache["gear"].healing + tonumber(value)
+ end
+ -- Enchant Weapon/Gloves/Bracers - Healing Power
+ _,_, value = strfind(left:GetText(), L["Healing Spells %+(%d+)"])
+ if value then
+ BCScache["gear"].healing = BCScache["gear"].healing + tonumber(value)
+ end
+ -- Zandalar Signet of Serenity (Shoulder enchant)
+ _,_, value = strfind(left:GetText(), L["^%+(%d+) Healing Spells"])
+ if value then
+ BCScache["gear"].healing = BCScache["gear"].healing + tonumber(value)
+ end
+ -- Beautiful Diamond Gemstone (Jewelcrafting)
+ -- Resilience of the Scourge (Shoulder enchant)
+ _,_, value = strfind(left:GetText(), "Healing %+(%d+)")
+ if value then
+ BCScache["gear"].healing = BCScache["gear"].healing + tonumber(value)
+ end
+ -- Enchanted Armor Kit (Leatherwotking)
+ -- Arcanum of Focus (Head/Legs enchant)
+ -- already included in GetSpellPower
+
+ _,_, value = strfind(left:GetText(), "(.+) %(%d/%d%)")
+ if value then
+ SET_NAME = value
+ end
+ _, _, value = strfind(left:GetText(), L["^Set: Increases healing done by spells and effects by up to (%d+)%."])
+ if value and SET_NAME and not tContains(healPower_Set_Bonus, SET_NAME) then
+ tinsert(healPower_Set_Bonus, SET_NAME)
+ BCScache["gear"].healing = BCScache["gear"].healing + tonumber(value)
+ end
+ end
+ end
+ end
+ end
+ -- SetHyperLink doesnt show temporary enhancements, have to use SetInventoryItem
+ if BCS_Tooltip:SetInventoryItem("player", 16) then
for line=1, BCS_Tooltip:NumLines() do
local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
-
if left:GetText() then
- local _,_, value = strfind(left:GetText(), L["Equip: Increases healing done by spells and effects by up to (%d+)."])
- if value then
- healPower = healPower + tonumber(value)
- end
- _,_, value = strfind(left:GetText(), "Equip: Increases your spell damage by up to 120 and your healing by up to (300).")
- if value then
- healPower = healPower + tonumber(value) - 120
- end
- _,_, value = strfind(left:GetText(), L["Healing Spells %+(%d+)"])
- if value then
- healPower = healPower + tonumber(value)
- end
- _,_, value = strfind(left:GetText(), L["^Healing %+(%d+) and %d+ mana per 5 sec."])
- if value then
- healPower = healPower + tonumber(value)
- end
- _,_, value = strfind(left:GetText(), L["^%+(%d+) Healing Spells"])
- if value then
- healPower = healPower + tonumber(value)
- end
- _,_, value = strfind(left:GetText(), "^Brilliant Mana Oil %((%d+) min%)")
- if value then
- healPower = healPower + 25
- end
-
- _,_, value = strfind(left:GetText(), "(.+) %(%d/%d%)")
- if value then
- SET_NAME = value
- end
- _, _, value = strfind(left:GetText(), L["^Set: Increases healing done by spells and effects by up to (%d+)%."])
- if value and SET_NAME and not tContains(healPower_Set_Bonus, SET_NAME) then
- tinsert(healPower_Set_Bonus, SET_NAME)
- healPower = healPower + tonumber(value)
+ local found = strfind(left:GetText(), "Brilliant Mana Oil")
+ if found then
+ BCScache["gear"].healing = BCScache["gear"].healing + 25
end
end
end
end
-
end
-
-- buffs
- local _, _, healPowerFromAura = BCS:GetPlayerAura(L["Healing done by magical spells is increased by up to (%d+)."])
- if healPowerFromAura then
- healPower = healPower + tonumber(healPowerFromAura)
- end
- --Sweet Surprise
- _, _, healPowerFromAura = BCS:GetPlayerAura(L["Increases healing done by magical spells by up to (%d+) for 3600 sec."])
- if healPowerFromAura then
- healPower = healPower + tonumber(healPowerFromAura)
- end
- --Unstable Power
- _, _, healPowerFromAura = BCS:GetPlayerAura(L["Healing increased by up to (%d+)."])
- if healPowerFromAura then
- healPower = healPower + tonumber(healPowerFromAura)
- end
- --The Eye of the Dead
- _, _, healPowerFromAura = BCS:GetPlayerAura(L["Healing spells increased by up to (%d+)."])
- if healPowerFromAura then
- healPower = healPower + tonumber(healPowerFromAura)
- end
- --Power of the Guardian
- _, _, healPowerFromAura = BCS:GetPlayerAura("Increases healing done by magical spells and effects by up to (%d+).")
- if healPowerFromAura then
- healPower = healPower + tonumber(healPowerFromAura)
- end
- _, _, healPowerFromAura = BCS:GetPlayerAura("Increases damage and healing done by magical spells and effects by up to (%d+).")
- if healPowerFromAura then
- healPower = healPower + tonumber(healPowerFromAura)
+ local treebonus = nil
+ if BCS.needScanAuras then
+ BCScache["auras"].healing = 0
+ local _, _, healPowerFromAura = BCS:GetPlayerAura(L["Healing done by magical spells is increased by up to (%d+)."])
+ if healPowerFromAura then
+ BCScache["auras"].healing = BCScache["auras"].healing + tonumber(healPowerFromAura)
+ end
+ --Tree of Life (own)
+ local found = BCS:GetPlayerAura("Tree of Life Form") and BCS:GetPlayerAura("Tree of Life Aura")
+ local _, spirit = UnitStat("player", 5)
+ if found then
+ treebonus = spirit * 0.2
+ end
+ --Sweet Surprise
+ _, _, healPowerFromAura = BCS:GetPlayerAura(L["Increases healing done by magical spells by up to (%d+) for 3600 sec."])
+ if healPowerFromAura then
+ BCScache["auras"].healing = BCScache["auras"].healing + tonumber(healPowerFromAura)
+ end
+ --Unstable Power
+ _, _, healPowerFromAura = BCS:GetPlayerAura(L["Healing increased by up to (%d+)."])
+ if healPowerFromAura then
+ BCScache["auras"].healing = BCScache["auras"].healing + tonumber(healPowerFromAura)
+ end
+ --The Eye of the Dead
+ _, _, healPowerFromAura = BCS:GetPlayerAura(L["Healing spells increased by up to (%d+)."])
+ if healPowerFromAura then
+ BCScache["auras"].healing = BCScache["auras"].healing + tonumber(healPowerFromAura)
+ end
+ --Power of the Guardian
+ _, _, healPowerFromAura = BCS:GetPlayerAura("Increases healing done by magical spells and effects by up to (%d+).")
+ if healPowerFromAura then
+ BCScache["auras"].healing = BCScache["auras"].healing + tonumber(healPowerFromAura)
+ end
+ --Dreamshard Elixir
+ _, _, healPowerFromAura = BCS:GetPlayerAura("Healing done is increased by up to (%d+)")
+ if healPowerFromAura then
+ BCScache["auras"].healing = BCScache["auras"].healing + tonumber(healPowerFromAura)
+ end
end
- --Dreamshard Elixir
- _, _, healPowerFromAura = BCS:GetPlayerAura("Healing done is increased by up to (%d+)")
- if healPowerFromAura then
- healPower = healPower + tonumber(healPowerFromAura)
+ if ironClad ~= nil then
+ BCScache["talents"].healing = 0
+ local base = UnitArmor("player")
+ local _, agility = UnitStat("player", 2)
+ local armorFromGear = base - (agility * 2)
+ BCScache["talents"].healing = floor(((ironClad / 100) * armorFromGear))
end
-
- return healPower
+ healPower = BCScache["gear"].healing + BCScache["auras"].healing + BCScache["talents"].healing
+
+ return healPower, treebonus, BCScache["talents"].healing
end
local function GetRegenMPPerSpirit()
local addvalue = 0
-
- local stat, Spirit, posBuff, negBuff = UnitStat("player", 5)
- local lClass, class = UnitClass("player")
-
+ local _, spirit = UnitStat("player", 5)
+ local _, class = UnitClass("player")
+
if class == "DRUID" then
- addvalue = (Spirit / 5 + 15)
+ addvalue = (spirit / 5 + 15)
elseif class == "HUNTER" then
- addvalue = (Spirit / 5 + 15)
+ addvalue = (spirit / 5 + 15)
elseif class == "MAGE" then
- addvalue = (Spirit / 4 + 12.5)
+ addvalue = (spirit / 4 + 12.5)
elseif class == "PALADIN" then
- addvalue = (Spirit / 5 + 15)
+ addvalue = (spirit / 5 + 15)
elseif class == "PRIEST" then
- addvalue = (Spirit / 4 + 12.5)
+ addvalue = (spirit / 4 + 12.5)
elseif class == "SHAMAN" then
- addvalue = (Spirit / 5 + 17)
+ addvalue = (spirit / 5 + 17)
elseif class == "WARLOCK" then
- addvalue = (Spirit / 5 + 15)
- else
- return addvalue
+ addvalue = (spirit / 5 + 15)
end
+
return addvalue
end
+local waterShield = nil
function BCS:GetManaRegen()
local base = GetRegenMPPerSpirit()
local casting = 0
local mp5 = 0
local mp5_Set_Bonus = {}
- local MAX_INVENTORY_SLOTS = 19
-
- for slot=0, MAX_INVENTORY_SLOTS do
- local hasItem = BCS_Tooltip:SetInventoryItem("player", slot)
-
- if hasItem then
- local SET_NAME
+ -- scan talents
+ if BCS.needScanTalents then
+ waterShield = nil
+ BCScache["talents"].casting = 0
+ for tab=1, GetNumTalentTabs() do
+ for talent=1, GetNumTalents(tab) do
+ BCS_Tooltip:SetTalent(tab, talent)
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _, _, _, _, rank = GetTalentInfo(tab, talent)
+ -- Priest (Meditation) / Druid (Reflection) / Mage (Arcane Meditation) / Shaman (Improved Water Shield)
+ local _,_, value = strfind(left:GetText(), L["Allows (%d+)%% of your Mana regeneration to continue while casting."])
+ if value and rank > 0 then
+ BCScache["talents"].casting = BCScache["talents"].casting + tonumber(value)
+ waterShield = rank
+ break
+ end
+ end
+ end
+ end
+ end
+ end
+
+ if BCS.needScanGear then
+ BCScache["gear"].mp5 = 0
+ BCScache["gear"].casting = 0
+ --scan gear
+ for slot=1, 19 do
+ if BCS_Tooltip:SetInventoryItem('player', slot) then
+ local _, _, eqItemLink = strfind(GetInventoryItemLink('player', slot), "(item:%d+:%d+:%d+:%d+)")
+ if eqItemLink then BCS_Tooltip:ClearLines() BCS_Tooltip:SetHyperlink(eqItemLink) end
+ local SET_NAME
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _,_, value = strfind(left:GetText(), L["^Mana Regen %+(%d+)"])
+ if value then
+ BCScache["gear"].mp5 = BCScache["gear"].mp5 + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["Equip: Restores (%d+) mana per 5 sec."])
+ if value and not strfind(left:GetText(), "to all party members") then
+ BCScache["gear"].mp5 = BCScache["gear"].mp5 + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["^Healing %+%d+ and (%d+) mana per 5 sec."])
+ if value then
+ BCScache["gear"].mp5 = BCScache["gear"].mp5 + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), L["^%+(%d+) mana every 5 sec."])
+ if value then
+ BCScache["gear"].mp5 = BCScache["gear"].mp5 + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), "^Equip: Allows (%d+)%% of your Mana regeneration to continue while casting.")
+ if value then
+ BCScache["gear"].casting = BCScache["gear"].casting + tonumber(value)
+ end
+
+ _,_, value = strfind(left:GetText(), "(.+) %(%d/%d%)")
+ if value then
+ SET_NAME = value
+ end
+ _,_, value = strfind(left:GetText(), L["^Set: Allows (%d+)%% of your Mana regeneration to continue while casting."])
+ if value and SET_NAME and not tContains(mp5_Set_Bonus, SET_NAME) then
+ tinsert(mp5_Set_Bonus, SET_NAME)
+ BCScache["gear"].casting = BCScache["gear"].casting + tonumber(value)
+ end
+ _,_, value = strfind(left:GetText(), "^Set: Restores (%d+) mana per 5 sec.")
+ if value and SET_NAME and not tContains(mp5_Set_Bonus, SET_NAME) then
+ tinsert(mp5_Set_Bonus, SET_NAME)
+ BCScache["gear"].mp5 = BCScache["gear"].mp5 + tonumber(value)
+ end
+ end
+ end
+ end
+ end
+ -- SetHyperLink doesnt show temporary enhancements, have to use SetInventoryItem
+ if BCS_Tooltip:SetInventoryItem("player", 16) then
for line=1, BCS_Tooltip:NumLines() do
local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
-
if left:GetText() then
- local _,_, value = strfind(left:GetText(), L["^Mana Regen %+(%d+)"])
- if value then
- mp5 = mp5 + tonumber(value)
+ local found = strfind(left:GetText(), "Brilliant Mana Oil")
+ if found then
+ BCScache["gear"].mp5 = BCScache["gear"].mp5 + 12
end
- _,_, value = strfind(left:GetText(), L["Equip: Restores (%d+) mana per 5 sec."])
- if value and not strfind(left:GetText(), "to all party members") then
- mp5 = mp5 + tonumber(value)
+ found = strfind(left:GetText(), "Lesser Mana Oil")
+ if found then
+ BCScache["gear"].mp5 = BCScache["gear"].mp5 + 8
end
- _,_, value = strfind(left:GetText(), L["^Healing %+%d+ and (%d+) mana per 5 sec."])
- if value then
- mp5 = mp5 + tonumber(value)
- end
- _,_, value = strfind(left:GetText(), L["^%+(%d+) mana every 5 sec."])
- if value then
- mp5 = mp5 + tonumber(value)
- end
- _,_, value = strfind(left:GetText(), "^Brilliant Mana Oil %((%d+) min%)")
- if value then
- mp5 = mp5 + 12
- end
- _,_, value = strfind(left:GetText(), "^Lesser Mana Oil %((%d+) min%)")
- if value then
- mp5 = mp5 + 8
+ found = strfind(left:GetText(), "Minor Mana Oil")
+ if found then
+ BCScache["gear"].mp5 = BCScache["gear"].mp5 + 4
end
- _,_, value = strfind(left:GetText(), "^Minor Mana Oil %((%d+) min%)")
+ end
+ end
+ end
+ end
+
+ -- buffs
+ if BCS.needScanAuras then
+ BCScache["auras"].casting = 0
+ BCScache["auras"].mp5 = 0
+ -- improved Shadowform
+ for tab=1, GetNumSpellTabs() do
+ local _, _, offset, numSpells = GetSpellTabInfo(tab);
+ for s = offset + 1, offset + numSpells do
+ local spell = GetSpellName(s, BOOKTYPE_SPELL);
+ if spell == "Improved Shadowform" and BCS:GetPlayerAura("Shadowform") then
+ BCScache["auras"].casting = BCScache["auras"].casting + 15
+ end
+ end
+ end
+ -- Warchief's Blessing
+ local _, _, mp5FromAura = BCS:GetPlayerAura(L["Increases hitpoints by 300. 15%% haste to melee attacks. (%d+) mana regen every 5 seconds."])
+ if mp5FromAura then
+ BCScache["auras"].mp5 = BCScache["auras"].mp5 + 10
+ end
+ --Epiphany
+ _, _, mp5FromAura = BCS:GetPlayerAura(L["Restores (%d+) mana per 5 sec."])
+ if mp5FromAura then
+ BCScache["auras"].mp5 = BCScache["auras"].mp5 + tonumber(mp5FromAura)
+ end
+ --Nightfin Soup
+ _, _, mp5FromAura = BCS:GetPlayerAura(L["Regenerating (%d+) Mana every 5 seconds."])
+ if mp5FromAura then
+ BCScache["auras"].mp5 = BCScache["auras"].mp5 + tonumber(mp5FromAura)*2.5 -- had to double the mp5FromAura because the item is a true mp5 tick
+ end
+ --Mageblood Potion
+ _, _, mp5FromAura = BCS:GetPlayerAura(L["Regenerate (%d+) mana per 5 sec."])
+ if mp5FromAura then
+ BCScache["auras"].mp5 = BCScache["auras"].mp5 + tonumber(mp5FromAura)
+ end
+ --Fizzy Energy Drink and Sagefin
+ _, _, mp5FromAura = BCS:GetPlayerAura(L["Mana Regeneration increased by (%d+) every 5 seconds."])
+ if mp5FromAura then
+ BCScache["auras"].mp5 = BCScache["auras"].mp5 + tonumber(mp5FromAura)*2.5
+ end
+ --Second Wind
+ _, _, mp5FromAura = BCS:GetPlayerAura(L["Restores (%d+) mana every 1 sec."])
+ if mp5FromAura then
+ BCScache["auras"].mp5 = BCScache["auras"].mp5 + tonumber(mp5FromAura)*5 -- had to multiply by 5 the mp5FromAura because the item is a sec per tick
+ end
+ --Power of the Guardian
+ _, _, mp5FromAura = BCS:GetPlayerAura("Restores (%d+) mana per 5 seconds.")
+ if mp5FromAura then
+ BCScache["auras"].mp5 = BCScache["auras"].mp5 + tonumber(mp5FromAura)
+ end
+ --Aura of the blue dragon
+ local _, _, castingFromAura = BCS:GetPlayerAura(L["(%d+)%% of your Mana regeneration continuing while casting."])
+ if castingFromAura then
+ BCScache["auras"].casting = BCScache["auras"].casting + tonumber(castingFromAura)
+ end
+ --Mage Armor
+ _, _, castingFromAura = BCS:GetPlayerAura(L["(%d+)%% of your mana regeneration to continue while casting."])
+ if castingFromAura then
+ BCScache["auras"].casting = BCScache["auras"].casting + tonumber(castingFromAura)
+ end
+ --Sylvan Blessing
+ _, _, castingFromAura = BCS:GetPlayerAura("Allows (%d+)%% of mana regeneration while casting.")
+ if castingFromAura then
+ BCScache["auras"].casting = BCScache["auras"].casting + tonumber(castingFromAura)
+ end
+ --Improved Water Shield
+ if waterShield ~= nil then
+ for i = 1, 32 do
+ local icon, stacks = UnitBuff("player", i)
+ if icon and stacks and icon == "Interface\\Icons\\Ability_Shaman_WaterShield" then
+ BCScache["auras"].casting = BCScache["auras"].casting + (tonumber(stacks) * waterShield)
+ end
+ end
+ end
+ end
+
+ casting = BCScache["auras"].casting + BCScache["talents"].casting + BCScache["gear"].casting
+ mp5 = BCScache["auras"].mp5 + BCScache["gear"].mp5
+
+ if casting > 100 then
+ casting = 100
+ end
+
+ return base, casting, mp5
+end
+
+--Weapon Skill code adapted from https://github.com/pepopo978/BetterCharacterStats
+function BCS:GetWeaponSkill(skillName)
+ -- loop through skills
+ local skillIndex = 1
+ while true do
+ local name, _, _, skillRank, _, skillModifier = GetSkillLineInfo(skillIndex)
+ if not name then
+ return 0
+ end
+
+ if name == skillName then
+ return skillRank + skillModifier
+ end
+
+ skillIndex = skillIndex + 1
+ end
+end
+
+function BCS:GetWeaponSkillForWeaponType(weaponType)
+ if weaponType == "Daggers" then
+ return BCS:GetWeaponSkill("Daggers")
+ elseif weaponType == "One-Handed Swords" then
+ return BCS:GetWeaponSkill("Swords")
+ elseif weaponType == "Two-Handed Swords" then
+ return BCS:GetWeaponSkill("Two-Handed Swords")
+ elseif weaponType == "One-Handed Axes" then
+ return BCS:GetWeaponSkill("Axes")
+ elseif weaponType == "Two-Handed Axes" then
+ return BCS:GetWeaponSkill("Two-Handed Axes")
+ elseif weaponType == "One-Handed Maces" then
+ return BCS:GetWeaponSkill("Maces")
+ elseif weaponType == "Two-Handed Maces" then
+ return BCS:GetWeaponSkill("Two-Handed Maces")
+ elseif weaponType == "Staves" then
+ return BCS:GetWeaponSkill("Staves")
+ elseif weaponType == "Polearms" then
+ return BCS:GetWeaponSkill("Polearms")
+ elseif weaponType == "Fist Weapons" then
+ return BCS:GetWeaponSkill("Unarmed")
+ elseif weaponType == "Bows" then
+ return BCS:GetWeaponSkill("Bows")
+ elseif weaponType == "Crossbows" then
+ return BCS:GetWeaponSkill("Crossbows")
+ elseif weaponType == "Guns" then
+ return BCS:GetWeaponSkill("Guns")
+ elseif weaponType == "Thrown" then
+ return BCS:GetWeaponSkill("Thrown")
+ elseif weaponType == "Wands" then
+ return BCS:GetWeaponSkill("Wands")
+ end
+ -- no weapon equipped
+ return BCS:GetWeaponSkill("Unarmed")
+end
+
+function BCS:GetItemTypeForSlot(slot)
+ local _, _, id = string.find(GetInventoryItemLink("player", GetInventorySlotInfo(slot)) or "", "(item:%d+:%d+:%d+:%d+)");
+ if not id then
+ return
+ end
+
+ local _, _, _, _, _, itemType = GetItemInfo(id);
+
+ return itemType
+end
+
+function BCS:GetMHWeaponSkill()
+ if not BCS.needScanSkills then
+ return BCScache["skills"].mh
+ end
+ local itemType = BCS:GetItemTypeForSlot("MainHandSlot")
+ BCScache["skills"].mh = BCS:GetWeaponSkillForWeaponType(itemType)
+
+ return BCScache["skills"].mh
+end
+
+function BCS:GetOHWeaponSkill()
+ if not BCS.needScanSkills then
+ return BCScache["skills"].oh
+ end
+
+ local itemType = BCS:GetItemTypeForSlot("SecondaryHandSlot")
+ BCScache["skills"].oh = BCS:GetWeaponSkillForWeaponType(itemType)
+
+ return BCScache["skills"].oh
+end
+
+function BCS:GetRangedWeaponSkill()
+ if not BCS.needScanSkills then
+ return BCScache["skills"].ranged
+ end
+
+ local itemType = BCS:GetItemTypeForSlot("RangedSlot")
+ BCScache["skills"].ranged = BCS:GetWeaponSkillForWeaponType(itemType)
+
+ return BCScache["skills"].ranged
+end
+
+--https://us.forums.blizzard.com/en/wow/t/block-value-formula/283718/18
+local enhancingTotems = nil
+function BCS:GetBlockValue()
+ local blockValue = 0
+ local _, strength = UnitStat("player", 1)
+ local mod = 0
+ -- scan gear
+ for slot=1, 19 do
+ if BCS_Tooltip:SetInventoryItem('player', slot) then
+ local _, _, eqItemLink = strfind(GetInventoryItemLink('player', slot), "(item:%d+:%d+:%d+:%d+)")
+ if eqItemLink then BCS_Tooltip:ClearLines() BCS_Tooltip:SetHyperlink(eqItemLink) end
+ for line=1, BCS_Tooltip:NumLines() do
+ local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
+ if left:GetText() then
+ local _,_, value = strfind(left:GetText(), "(%d+) Block")
if value then
- mp5 = mp5 + 4
+ blockValue = blockValue + tonumber(value)
end
- _,_, value = strfind(left:GetText(), "^Equip: Allows (%d+)%% of your Mana regeneration to continue while casting.")
+ _,_, value = strfind(left:GetText(), "Equip: Increases the block value of your shield by (%d+).")
if value then
- casting = casting + tonumber(value)
+ blockValue = blockValue + tonumber(value)
end
- _,_, value = strfind(left:GetText(), "(.+) %(%d/%d%)")
+ _,_, value = strfind(left:GetText(), "Block Value %+(%d+)")
if value then
- SET_NAME = value
- end
- _,_, value = strfind(left:GetText(), L["^Set: Allows (%d+)%% of your Mana regeneration to continue while casting."])
- if value and SET_NAME and not tContains(mp5_Set_Bonus, SET_NAME) then
- tinsert(mp5_Set_Bonus, SET_NAME)
- casting = casting + tonumber(value)
- end
- _,_, value = strfind(left:GetText(), "^Set: Restores (%d+) mana per 5 sec.")
- if value and SET_NAME and not tContains(mp5_Set_Bonus, SET_NAME) then
- tinsert(mp5_Set_Bonus, SET_NAME)
- mp5 = mp5 + tonumber(value)
+ blockValue = blockValue + tonumber(value)
end
end
end
end
end
-
-- scan talents
- local MAX_TABS = GetNumTalentTabs()
-
- for tab=1, MAX_TABS do
- local MAX_TALENTS = GetNumTalents(tab)
-
- for talent=1, MAX_TALENTS do
+ for tab=1, GetNumTalentTabs() do
+ for talent=1, GetNumTalents(tab) do
BCS_Tooltip:SetTalent(tab, talent)
- local MAX_LINES = BCS_Tooltip:NumLines()
-
- for line=1, MAX_LINES do
+ for line=1, BCS_Tooltip:NumLines() do
local left = getglobal(BCS_Prefix .. "TextLeft" .. line)
if left:GetText() then
- -- Priest (Meditation) / Druid (Reflection)
- local _,_, value = strfind(left:GetText(), L["Allows (%d+)%% of your Mana regeneration to continue while casting."])
- local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tab, talent)
+ local _, _, _, _, rank = GetTalentInfo(tab, talent)
+ --warrior/paladin
+ local _,_, value = strfind(left:GetText(), "amount of damage absorbed by your shield by (%d+)%%")
+ if value and rank > 0 then
+ mod = mod + tonumber(value)
+ break
+ end
+ --shaman
+ --shield specialization
+ _,_, value = strfind(left:GetText(), "increases the amount blocked by (%d+)%%")
+ if value and rank > 0 then
+ mod = mod + tonumber(value)
+ break
+ end
+ --enhancing totems
+ _,_, value = strfind(left:GetText(), "increases block amount by (%d+)%%")
if value and rank > 0 then
- casting = casting + tonumber(value)
+ enhancingTotems = tonumber(value)
+ break
end
- end
+ end
end
-
end
end
-
-- buffs
- -- Warchief's Blessing
- local _, _, mp5FromAura = BCS:GetPlayerAura(L["Increases hitpoints by 300. 15%% haste to melee attacks. (%d+) mana regen every 5 seconds."])
- if mp5FromAura then
- mp5 = mp5 + 10
- end
- --Epiphany
- _, _, mp5FromAura = BCS:GetPlayerAura(L["Restores (%d+) mana per 5 sec."])
- if mp5FromAura then
- mp5 = mp5 + tonumber(mp5FromAura)
- end
- --Nightfin Soup
- _, _, mp5FromAura = BCS:GetPlayerAura(L["Regenerating (%d+) Mana every 5 seconds."])
- if mp5FromAura then
- mp5 = mp5 + tonumber(mp5FromAura)*2.5 -- had to double the mp5FromAura because the item is a true mp5 tick
- end
- --Mageblood Potion
- _, _, mp5FromAura = BCS:GetPlayerAura(L["Regenerate (%d+) mana per 5 sec."])
- if mp5FromAura then
- mp5 = mp5 + tonumber(mp5FromAura)
- end
- --Fizzy Energy Drink and Sagefin
- _, _, mp5FromAura = BCS:GetPlayerAura(L["Mana Regeneration increased by (%d+) every 5 seconds."])
- if mp5FromAura then
- mp5 = mp5 + tonumber(mp5FromAura)*2.5
- end
- --Second Wind
- _, _, mp5FromAura = BCS:GetPlayerAura(L["Restores (%d+) mana every 1 sec."])
- if mp5FromAura then
- mp5 = mp5 + tonumber(mp5FromAura)*5 -- had to multiply by 5 the mp5FromAura because the item is a sec per tick
- end
- --Aura of the blue dragon
- _, _, castingFromAura = BCS:GetPlayerAura(L["(%d+)%% of your Mana regeneration continuing while casting."])
- if mp5FromAura then
- casting = casting + tonumber(castingFromAura)
+ --Glyph of Deflection
+ local _, _, value = BCS:GetPlayerAura("Block value increased by (%d+).")
+ if value then
+ blockValue = blockValue + tonumber(value)
end
- --Power of the Guardian
- _, _, castingFromAura = BCS:GetPlayerAura("Restores (%d+) mana per 5 seconds.")
- if mp5FromAura then
- mp5 = mp5 + tonumber(mp5FromAura)
+ if enhancingTotems and BCS:GetPlayerAura("^Stoneskin") then
+ mod = mod + enhancingTotems
end
- if casting > 100 then
- casting = 100
- end
- return base, casting, mp5
-end
\ No newline at end of file
+
+ mod = mod/100
+ blockValue = blockValue + (strength/20 - 1)
+ blockValue = floor(blockValue + blockValue * mod)
+
+ if blockValue < 0 then blockValue = 0 end
+
+ return blockValue
+end