-- module independent variables --
----------------------------------
local addon , ns = ... ;
local C , L , I = ns.LC . color , ns.L , ns.I ;
-- module own local variables and local cached functions --
-----------------------------------------------------------
local name = " Durability " ; -- DURABILITY L["ModDesc-Durability"]
local ttName , tt , module , _ = name .. " TT " ;
local last_repairs = { } ;
local merchant , currentDurability = { repair = false , costs = 0 , diff = 0 , single = 0 } , { 0 , 0 , 0 , 100 , 100 , false } ;
local discount = { [ 5 ] = 0.95 , [ 6 ] = 0.9 , [ 7 ] = 0.85 , [ 8 ] = 0.8 } ;
local slotNames = {
HEADSLOT , NECKSLOT , SHOULDERSLOT , SHIRTSLOT , CHESTSLOT , WAISTSLOT , LEGSSLOT , FEETSLOT ,
WRISTSLOT , HANDSSLOT , FINGER0SLOT_UNIQUE , FINGER1SLOT_UNIQUE , TRINKET0SLOT_UNIQUE ,
TRINKET1SLOT_UNIQUE , BACKSLOT , MAINHANDSLOT , SECONDARYHANDSLOT , RANGEDSLOT
} ;
local date_format = " %Y-%m-%d %H:%M " ;
local date_formats = {
[ " %d.%m. %H:%M " ] = " 28.07. 16:23 " ,
[ " %d.%m. %I:%M %p " ] = " 28.07. 04:23 pm " ,
[ " %Y-%m-%d %H:%M " ] = " 2099-07-28 16:23 " ,
[ " %Y-%m-%d %I:%M %p " ] = " 2099-07-28 04:23 pm " ,
[ " %d/%m/%Y %H:%M " ] = " 28/07/2099 16:23 " ,
[ " %d/%m/%Y %I:%M %p " ] = " 28/07/2099 04:23 pm "
} ;
local colorSets = setmetatable ( { values = { } } , {
__newindex = function ( t , k , v )
local tb , n = { } , 0 ;
for i , col in ns.pairsByKeys ( v ) do
table.insert ( tb , C ( col , ( n < 100 and n .. " - " or " " ) .. i .. " % " ) ) ;
n = i + 1 ;
end
rawset ( t.values , k , table.concat ( tb , " , " ) ) ;
rawset ( t , k , v ) ;
end ,
__call = function ( t , d )
local c , n = nil , 0
local set = t [ ns.profile [ name ] . colorSet ]
for i , v in ns.pairsByKeys ( set ) do if d >= n and d <= i then c , n = v , i + 1 end end
return c
end
} )
-- register icon names and default files --
-------------------------------------------
I [ name ] = { iconfile = " Interface \\ Minimap \\ TRACKING \\ Repair " , coords = { 0.05 , 0.95 , 0.05 , 0.95 } } ; --IconName::Durability--
-- some local functions --
--------------------------
local function updateBroker ( )
local obj = ns.LDB : GetDataObjectByName ( module.ldbName ) or { } ;
local repairCosts , equipCost , bagCost , dA , dL , dLSlot , d = unpack ( currentDurability ) ;
if ( ns.profile [ name ] . inBroker == " costs " ) then
obj.text = ns.GetCoinColorOrTextureString ( name , repairCosts )
else
d = floor ( ( ns.profile [ name ] . lowestItem ) and dL or dA ) ;
if ( ns.profile [ name ] . inBroker == " percent " ) then
obj.text = C ( colorSets ( d ) or " blue " , d .. " % " ) ;
elseif ( ns.profile [ name ] . inBroker == " percent/costs " ) then
obj.text = C ( colorSets ( d ) or " blue " , d .. " % " ) .. " , " .. ns.GetCoinColorOrTextureString ( name , repairCosts ) ;
elseif ( ns.profile [ name ] . inBroker == " costs/percent " ) then
obj.text = ns.GetCoinColorOrTextureString ( name , repairCosts ) .. " , " .. C ( colorSets ( d ) or " blue " , d .. " % " ) ;
end
end
end
local function nsItems2Callback ( )
local repairCost , durabilitySum , lowest , tbl , slot , bag = { bags = 0 , inv = 0 } , { current = 0 , max = 0 , count = 0 } , { 1 , false } ;
for sharedSlot in pairs ( ns.items . equip ) do
local tbl = sharedSlot < 0 and " inv " or " bags " ;
local obj = ns.items . bySlot [ sharedSlot ] ;
local durability , durabilityMax = 0 , 0 ;
if obj and obj.equip then
local d , dM ;
if tbl == " inv " then
d , dM = GetInventoryItemDurability ( obj.slot ) ;
else
d , dM = ( C_Container and C_Container.GetContainerItemDurability or GetContainerItemDurability ) ( obj.bag , obj.slot ) ;
end
durability , durabilityMax = d or 0 , dM or 0 ;
end
if obj and obj.equip and durabilityMax > 0 then
durabilitySum.count = durabilitySum.count + 1 ;
durabilitySum.current = durabilitySum.current + durability ;
durabilitySum.max = durabilitySum.max + durabilityMax ;
local percentage = durability / durabilityMax ;
if percentage < 1 then
if tbl == " inv " and percentage < lowest [ 1 ] then
lowest = { percentage , obj.slot } ;
end
local itemRepairCost = 0 ;
if C_TooltipInfo then -- api changed since dragonflight
local hasItem
if tbl == " inv " then
hasItem = C_TooltipInfo.GetInventoryItem ( " player " , obj.slot ) ;
else
hasItem = C_TooltipInfo.GetBagItem ( obj.bag , obj.slot ) ;
end
if type ( hasItem ) == " table " then
if hasItem.args then
TooltipUtil.SurfaceArgs ( hasItem ) ;
end
if hasItem.repairCost then
itemRepairCost = hasItem.repairCost ;
end
end
else -- prev api and classic; need tooltip to get item repair costs
local data = { type = tbl == " bags " and " bag " or " inventory " , slot = obj.slot , bag = obj.bag } ;
ns.ScanTT . query ( data , true ) ;
itemRepairCost = tonumber ( data.repairCost ) or 0 ;
end
repairCost [ tbl ] = repairCost [ tbl ] + itemRepairCost ;
end
end
end
local avPercentage = 1 ;
if durabilitySum.max > 0 then
avPercentage = durabilitySum.current / durabilitySum.max ;
end
local total = repairCost.bags + repairCost.inv ;
if merchant.repair then
total = GetRepairAllCost ( ) or 0 ;
end
currentDurability = { total , repairCost.inv , repairCost.bags , avPercentage * 100 , lowest [ 1 ] * 100 , lowest [ 2 ] } ;
updateBroker ( ) ;
end
local function lastRepairs_add ( cost , fund , repairType )
local t = { } ;
table.insert ( last_repairs , 1 , { time ( ) , cost , ( fund == true ) , ( repairType == true ) } ) ;
for i , v in ipairs ( last_repairs ) do
if ( # t < 50 ) then table.insert ( t , v ) ; end
end
last_repairs = t ;
if ( ns.profile [ name ] . saveCosts ) then
ns.toon [ name ] = t ;
end
end
local function AutoRepairAll ( costs )
local chat = ns.profile [ name ] . chatRepairInfo ;
if ( ns.profile [ name ] . autorepairbyguild == true ) and ( IsInGuild ( ) == true ) and ( CanGuildBankRepair ( ) == true ) and ( costs < GetGuildBankWithdrawMoney ( ) ) then
if ( not GetGuildInfoText ( ) : find ( " %[noautorepair%] " ) ) then -- auto repair with guild fund allowed by guild leader?
merchant = { repair = false , costs = 0 , diff = 0 , single = 0 } ; -- must be changed befor Repair all items
RepairAllItems ( true ) ;
lastRepairs_add ( costs , true , true ) ;
if ( chat ) then
ns : print ( L [ " Automatically repaired with guild money " ] .. HEADER_COLON , ns.GetCoinColorOrTextureString ( name , costs , { color = " white " } ) ) ;
end
return true ;
elseif ( chat ) then
ns : print ( L [ " AutoRepair " ] , L [ " Your guild leadership denied the use of guild money for auto repair. " ] , L [ " Try fallback to player money. " ] ) ;
end
end
if ( costs < GetMoney ( ) ) then
merchant = { repair = false , costs = 0 , diff = 0 , single = 0 } ; -- must be changed befor Repair all items
RepairAllItems ( ) ;
lastRepairs_add ( costs , nil , true ) ;
if ( chat ) then
ns : print ( L [ " Automatically repaired with player money " ] .. HEADER_COLON , ns.GetCoinColorOrTextureString ( name , costs , { color = " white " } ) ) ;
end
return nil ;
end
return false ;
end
local function createTooltip ( tt )
if not ( tt and tt.key and tt.key == ttName ) then return end -- don't override other LibQTip tooltips...
if tt.lines ~= nil then tt : Clear ( ) ; end
local repairCost , equipCost , bagCost , durabilityA , durabilityL , durabilityLslot = unpack ( currentDurability ) ;
local repairCostN = repairCost ;
local reputation = UnitReaction ( " npc " , " player " ) ;
if discount [ reputation ] and ns.client_version >= 2 then
repairCostN = floor ( repairCost / discount [ reputation ] ) ;
end
durabilityA = floor ( durabilityA ) ;
durabilityL = floor ( durabilityL ) ;
local a , g , d = ns.profile [ name ] . autorepair , ns.profile [ name ] . autorepairbyguild ;
local lst = setmetatable ( { } , { __call = function ( t , a ) rawset ( t , # t + 1 , a ) end } ) ;
lst ( { sep = { 3 , 0 , 0 , 0 , 0 } } ) ;
lst ( { c1 = C ( " ltblue " , gsub ( REPAIR_COST , HEADER_COLON , " " ) ) , c2 = ns.GetCoinColorOrTextureString ( name , repairCost , { inTooltip = true } ) } ) ;
lst ( { sep = { 1 } } ) ;
lst ( { c1 = CHARACTER , c2 = ns.GetCoinColorOrTextureString ( name , equipCost , { inTooltip = true } ) } ) ;
lst ( { c1 = L [ " Bags " ] , c2 = ns.GetCoinColorOrTextureString ( name , bagCost , { inTooltip = true } ) } ) ;
if ns.profile [ name ] . showDiscount and ns.client_version >= 2 then
lst ( { sep = { 3 , 0 , 0 , 0 , 0 } } ) ;
lst ( { c0 = C ( " ltblue " , L [ " Reputation discounts " ] ) } ) ;
lst ( { sep = { 1 } } ) ;
lst ( { c1 = C ( " white " , FACTION_STANDING_LABEL4 ) , c2 = ns.GetCoinColorOrTextureString ( name , repairCostN , { inTooltip = true } ) } ) ;
lst ( { c1 = C ( " white " , FACTION_STANDING_LABEL5 ) , c2 = ns.GetCoinColorOrTextureString ( name , ceil ( repairCostN * discount [ 5 ] ) , { inTooltip = true } ) } ) ;
lst ( { c1 = C ( " white " , FACTION_STANDING_LABEL6 ) , c2 = ns.GetCoinColorOrTextureString ( name , ceil ( repairCostN * discount [ 6 ] ) , { inTooltip = true } ) } ) ;
lst ( { c1 = C ( " white " , FACTION_STANDING_LABEL7 ) , c2 = ns.GetCoinColorOrTextureString ( name , ceil ( repairCostN * discount [ 7 ] ) , { inTooltip = true } ) } ) ;
lst ( { c1 = C ( " white " , FACTION_STANDING_LABEL8 ) , c2 = ns.GetCoinColorOrTextureString ( name , ceil ( repairCostN * discount [ 8 ] ) , { inTooltip = true } ) } ) ;
end
if ( ns.profile [ name ] . listCosts ) then
lst ( { sep = { 3 , 0 , 0 , 0 , 0 } } ) ;
if ( ns.profile [ name ] . saveCosts ) then
lst ( { c0 = C ( " ltblue " , L [ " Last %d repair costs " ] : format ( ns.profile [ name ] . maxCosts ) ) } ) ;
else
lst ( { c1 = C ( " ltblue " , L [ " Last %d repair costs " ] : format ( ns.profile [ name ] . maxCosts ) ) , c2 = C ( " ltblue " , L [ " (session only) " ] ) } ) ;
end
lst ( { sep = { 1 } } ) ;
if ( # last_repairs > 0 ) then
local indicator = " " ;
for i , v in ipairs ( last_repairs ) do
if ( i <= tonumber ( ns.profile [ name ] . maxCosts ) ) then
indicator = ( ( v [ 4 ] ) and " a " or " " ) .. ( ( v [ 3 ] ) and " G " or " P " ) ;
lst ( { c1 = date ( date_format , v [ 1 ] ) .. ( strlen ( indicator ) > 0 and " " .. indicator or " " ) , c2 = ns.GetCoinColorOrTextureString ( name , ceil ( v [ 2 ] ) , { inTooltip = true } ) } ) ;
end
end
else
lst ( { c0 = L [ " No data found " ] } ) ;
end
end
tt : AddHeader ( C ( " dkyellow " , DURABILITY ) ) ;
tt : AddSeparator ( ) ;
local slotName = " " ;
if ( durabilityLslot ) and ( durabilityLslot ~= 0 ) then
if ( slotNames [ durabilityLslot ] ) then
slotName = ( " (%s) " ) : format ( slotNames [ durabilityLslot ] ) ;
end
end
tt : AddLine ( L [ " Lowest item " ] .. slotName , C ( colorSets ( durabilityL ) or " blue " , durabilityL .. " % " ) ) ;
tt : AddLine ( GMSURVEYRATING3 , C ( colorSets ( durabilityA ) or " blue " , durabilityA .. " % " ) ) ;
for i , v in ipairs ( lst ) do
if ( v.sep ~= nil ) then
tt : AddSeparator ( unpack ( v.sep ) ) ;
elseif ( v.c0 ~= nil ) then
local l , c = tt : AddLine ( ) ;
tt : SetCell ( l , 1 , v.c0 , nil , nil , 2 ) ;
if ( v.f0 ~= nil ) then
tt : SetCellScript ( l , 1 , " OnMouseUp " , v.f0 ) ;
end
else
local l , c = tt : AddLine ( ) ;
tt : SetCell ( l , 1 , v.c1 ) ;
tt : SetCell ( l , 2 , v.c2 ) ;
end
end
if ( ns.profile . GeneralOptions.showHints ) then
tt : AddSeparator ( 4 , 0 , 0 , 0 , 0 ) ;
ns.ClickOpts . ttAddHints ( tt , name ) ;
end
ns.roundupTooltip ( tt ) ;
end
-- module functions and variables --
------------------------------------
module = {
events = {
" PLAYER_LOGIN " ,
" PLAYER_DEAD " ,
" PLAYER_REGEN_ENABLED " ,
" MERCHANT_CLOSED " ,
" MERCHANT_SHOW " ,
" PLAYER_MONEY " ,
" CHAT_MSG_MONEY "
} ,
config_defaults = {
enabled = false ,
inBroker = " percent " ,
colorSet = " set1 " ,
autorepair = false ,
autorepairbyguild = false ,
listCosts = true ,
saveCosts = true ,
maxCosts = 5 ,
dateFormat = " %Y-%m-%d %H:%M " ,
showDiscount = true ,
lowestItem = true ,
chatRepairInfo = false
} ,
clickOptionsRename = {
[ " charinfo " ] = " 1_open_character_info " ,
[ " menu " ] = " 2_open_menu "
} ,
clickOptions = {
[ " charinfo " ] = " CharacterInfo " , -- _LEFT
[ " menu " ] = " OptionMenuCustom "
}
} ;
ns.ClickOpts . addDefaults ( module , {
charinfo = " _LEFT " ,
menu = " _RIGHT "
} ) ;
function module . options ( )
return {
broker = {
lowestItem = { type = " toggle " , order = 1 , name = L [ " Lowest durability " ] , desc = L [ " Display the lowest item durability in broker. " ] } ,
inBroker = { type = " select " , order = 2 , name = L [ " Broker format " ] , desc = L [ " Choose your favorite display format for the broker button. " ] ,
values = {
[ " percent " ] = " 54% " ,
[ " costs " ] = " 34.23.01 " ,
[ " costs/percent " ] = " 32.27.16, 54% " ,
[ " percent/costs " ] = " 54%, 32.27.16 "
}
} ,
} ,
tooltip = {
showDiscount = { type = " toggle " , order = 1 , name = L [ " Show discount " ] , desc = L [ " Show list of reputation discounts in tooltip " ] , hidden = ns.IsClassicClient } ,
dateFormat = { type = " select " , order = 2 , name = L [ " Date format " ] , desc = L [ " Choose the date format if used in the list of repair costs " ] , values = date_formats } ,
} ,
misc = {
colorSet = { type = " select " , order = 1 , name = L [ " Percent color set " ] , desc = L [ " Choose your favorite color set in which the percent text in broker should be displayed. " ] , values = colorSets.values , width = " double " } ,
header = { type = " header " , order = 2 , name = L [ " Repair options " ] } ,
autorepair = { type = " toggle " , order = 4 , name = L [ " Enable auto repair " ] , desc = L [ " Automatically repair your equipment on opening a merchant with repair option. " ] } ,
autorepairbyguild = { type = " toggle " , order = 5 , name = L [ " Use guild money " ] , desc = function ( ) return L [ " Use guild money on auto repair if you can " ] .. ( ( GetGuildInfoText ( ) : find ( " %[noautorepair%] " ) ) and " |n " .. C ( " red " , L [ " Your guild leadership denied the use of guild money for auto repair. " ] ) or " " ) ; end , --[[disabled=function() return (GetGuildInfoText():find("%[noautorepair%]")); end]] } ,
chatRepairInfo = { type = " toggle " , order = 6 , name = L [ " Repair info " ] , desc = L [ " Post repair actions in chatframe " ] } ,
listCosts = { type = " toggle " , order = 7 , name = L [ " List of repair costs " ] , desc = L [ " Display a list of the last repair costs in tooltip " ] } ,
saveCosts = { type = " toggle " , order = 8 , name = L [ " Save repair costs " ] , desc = L [ " Save the list of repair costs over the session " ] } ,
maxCosts = { type = " range " , order = 9 , name = L [ " Max. list entries " ] , desc = L [ " Choose how much entries the list of repair costs can have. " ] , min = 1 , max = 50 , step = 1 } ,
} ,
} ,
{
dateFormat = true ,
colorSet = true ,
autorepair = true ,
autorepairbyguild = true
}
end
function module . OptionMenu ( self , button , modName )
if ( tt ~= nil ) and ( tt : IsShown ( ) ) then ns.hideTooltip ( tt ) ; end
ns.EasyMenu : InitializeMenu ( ) ;
ns.EasyMenu : AddConfig ( name ) ;
ns.EasyMenu : AddEntry ( { separator = true } ) ;
ns.EasyMenu : AddEntry ( {
label = L [ " Reset last repairs " ] ,
colorName = " yellow " ,
func = function ( )
wipe ( last_repairs ) ;
wipe ( ns.toon [ name ] ) ;
end ,
disabled = ( false )
} ) ;
ns.EasyMenu : ShowMenu ( self ) ;
end
function module . init ( )
colorSets.set1 = { [ 20 ] = " red " , [ 40 ] = " orange " , [ 99 ] = " yellow " , [ 100 ] = " green " } ;
colorSets.set2 = { [ 15 ] = " red " , [ 40 ] = " orange " , [ 70 ] = " yellow " , [ 100 ] = " white " } ;
colorSets.set3 = { [ 20 ] = " red " , [ 40 ] = " orange " , [ 60 ] = " yellow " , [ 99 ] = " green " , [ 100 ] = " ltblue " } ;
colorSets.set4 = { [ 15 ] = " red " , [ 40 ] = " orange " , [ 60 ] = " yellow " , [ 80 ] = " green " , [ 100 ] = " white " } ;
date_format = ns.profile [ name ] . dateFormat ;
if ( ns.toon [ name ] == nil ) then
ns.toon [ name ] = { } ;
end
if ( ns.profile [ name ] . saveCosts ) then
last_repairs = ns.toon [ name ] ;
end
MerchantRepairAllButton : HookScript ( " OnClick " , function ( self , button )
module.onevent ( { } , " BE_EVENT_REPAIRALL_PLAYER " ) ;
end ) ;
MerchantGuildBankRepairButton : HookScript ( " OnClick " , function ( self , button )
module.onevent ( { } , " BE_EVENT_REPAIRALL_GUILD " ) ;
end ) ;
ns.items . RegisterCallback ( name , nsItems2Callback , " equip " ) ;
end
function module . onevent ( self , event , arg1 )
if event == " BE_UPDATE_CFG " then
if arg1 and arg1 : find ( " ^ClickOpt " ) then
ns.ClickOpts . update ( name ) ;
return ;
end
date_format = ns.profile [ name ] . dateFormat ;
end
if event == " MERCHANT_SHOW " then
local costs , canRepair = GetRepairAllCost ( ) ;
if ( costs > 0 ) and ( canRepair ) then
merchant.repair = true ;
merchant.costs = costs ;
if ( ns.profile [ name ] . autorepair ) then
if ( AutoRepairAll ( costs ) == false ) and ( ns.profile [ name ] . chatRepairInfo ) then
ns : print ( L [ " AutoRepair " ] , L [ " Automatically repair failed. Not enough money... " ] ) ;
end
end
end
elseif ns.eventPlayerEnteredWorld then
-- RepairAll - ButtonHooks - custom events
if ( event == " BE_EVENT_REPAIRALL_GUILD " ) then
lastRepairs_add ( merchant.costs , true ) ;
if ( ns.profile [ name ] . chatRepairInfo ) then
ns : print ( L [ " RepairAll " ] , L [ " by guild fund " ] .. HEADER_COLON , ns.GetCoinColorOrTextureString ( name , merchant.costs , { color = " white " } ) ) ;
end
merchant.costs = 0 ;
elseif ( event == " BE_EVENT_REPAIRALL_PLAYER " ) then
lastRepairs_add ( merchant.costs ) ;
if ( ns.profile [ name ] . chatRepairInfo ) then
ns : print ( L [ " RepairAll " ] , L [ " by player money " ] .. HEADER_COLON , ns.GetCoinColorOrTextureString ( name , merchant.costs , { color = " white " } ) ) ;
end
merchant.costs = 0 ;
end
if ( merchant.repair ) then
if ( InRepairMode ( ) ) and ( merchant.costs > 0 ) and ( event == " PLAYER_MONEY " ) then
local costs = GetRepairAllCost ( ) ;
merchant.diff = merchant.costs - costs ;
if ( merchant.diff > 0 ) then
merchant.costs = costs ;
merchant.single = merchant.single + merchant.diff ; -- single item repair mode, step 1
end
end
if ( event == " MERCHANT_CLOSED " ) then
if ( merchant.single > 0 ) then -- single item repair mode, step 2
lastRepairs_add ( merchant.single , nil , false ) ;
if ( ns.profile [ name ] . chatRepairInfo ) then
ns : print ( L [ " SingleRepairSummary " ] .. HEADER_COLON , ns.GetCoinColorOrTextureString ( name , merchant.single , { color = " white " } ) ) ;
end
end
merchant = { repair = false , costs = 0 , diff = 0 , single = 0 } ;
end
end
end
end
-- function module.onclick(self,button) end
-- function module.onmousewheel(self,direction) end
-- function module.optionspanel(panel) end
-- function module.ontooltip(tt) end
-- function module.ondblclick(self,button) end
function module . onenter ( self )
if ( ns.tooltipChkOnShowModifier ( false ) ) then return ; end
tt = ns.acquireTooltip ( { ttName , 2 , " LEFT " , " RIGHT " } , { true } , { self } ) ;
createTooltip ( tt ) ;
end
-- function module.onleave(self) end
-- final module registration --
-------------------------------
ns.modules [ name ] = module ;