Files
DuvelCorp 51d4c785fa 1.5.0
2026-03-31 21:25:35 +02:00

763 lines
21 KiB
Lua

local function zButtonRanged_GetRoot()
return MTH_ZH_GetSavedRoot()
end
local root = zButtonRanged_GetRoot()
if not root["zButtonRanged"] then
root["zButtonRanged"] = {}
root["zButtonRanged"]["spells"] = {}
root["zButtonRanged"]["visible"] = {}
root["zButtonRanged"]["rows"] = 1
root["zButtonRanged"]["horizontal"] = nil
root["zButtonRanged"]["vertical"] = nil
root["zButtonRanged"]["firstbutton"] = "RIGHT"
root["zButtonRanged"]["enabled"] = false
root["zButtonRanged"]["tooltip"] = 1
root["zButtonRanged"]["parent"] = {}
root["zButtonRanged"]["parent"]["size"] = 36
root["zButtonRanged"]["parent"]["hide"] = nil
root["zButtonRanged"]["parent"]["circle"] = 1
root["zButtonRanged"]["children"] = {}
root["zButtonRanged"]["children"]["size"] = 36
root["zButtonRanged"]["children"]["hideonclick"] = 1
end
ZHunterMod_Ranged_Weapons = ZHunterMod_Ranged_Weapons or {}
local ZHUNTER_RANGED_MAX_COUNT = 80
local zButtonRanged_Cache = {}
local zButtonRanged_TooltipWarmed = false
local zButtonRanged_LastRefreshAt = 0
local zButtonRanged_MinBagRefreshInterval = 0.75
local zButtonRanged_IdleBagRefreshInterval = 3.00
local zButtonRanged_BagDirty = false
local zButtonRanged_RefreshButtonsFromState
local function zButtonRanged_GetKnownItemSpeed(itemId)
local idNum = tonumber(itemId)
if not idNum then
return nil
end
local items = _G["MTH_DS_Items"] or MTH_DS_AmmoItems
local item = items and items[idNum]
local speedVal = item and tonumber(item.speed)
if speedVal and speedVal > 0 then
return speedVal
end
return nil
end
local function zButtonRanged_GetSaved()
local currentRoot = zButtonRanged_GetRoot()
if not currentRoot["zButtonRanged"] then
currentRoot["zButtonRanged"] = {}
end
return currentRoot["zButtonRanged"]
end
local function zButtonRanged_GetColorGradient(perc)
perc = perc > 1 and 1 or perc
perc = perc < 0 and 0 or perc
local r1, g1, b1, r2, g2, b2
if perc <= 0.5 then
perc = perc * 2
r1, g1, b1 = 1, 0, 0
r2, g2, b2 = 1, 1, 0
else
perc = perc * 2 - 1
r1, g1, b1 = 1, 1, 0
r2, g2, b2 = 0, 1, 0
end
local r = r1 + (r2 - r1) * perc
local g = g1 + (g2 - g1) * perc
local b = b1 + (b2 - b1) * perc
return r, g, b
end
local function zButtonRanged_EnsureDurabilityText(button)
if button and not button.rangedDurabilityText then
button.rangedDurabilityText = button:CreateFontString(nil, "OVERLAY")
button.rangedDurabilityText:SetPoint("BOTTOMRIGHT", button)
button.rangedDurabilityText:SetFont("Fonts\\ARIALN.ttf", 13, "OUTLINE")
end
return button and button.rangedDurabilityText
end
local function zButtonRanged_EnsureSpeedText(button)
if button and not button.rangedSpeedText then
button.rangedSpeedText = button:CreateFontString(nil, "OVERLAY")
button.rangedSpeedText:SetPoint("TOPLEFT", button, "TOPLEFT", 1, -1)
button.rangedSpeedText:SetFont("Fonts\\ARIALN.ttf", 9, "OUTLINE")
end
return button and button.rangedSpeedText
end
local function zButtonRanged_IsRangedItem(subtype, equipLoc)
local subtypeText = tostring(subtype or "")
if subtypeText == tostring(MTH_WEAPON_BOWS or "Bows") then return true end
if subtypeText == tostring(MTH_WEAPON_GUNS or "Guns") then return true end
if subtypeText == tostring(MTH_WEAPON_CROSSBOWS or "Crossbows") then return true end
return false
end
local function zButtonRanged_RebuildCache()
zButtonRanged_Cache = {}
for bag = 0, 4 do
for slot = 1, GetContainerNumSlots(bag) do
local link = GetContainerItemLink(bag, slot)
if link then
local _, _, id = string.find(link, "Hitem:((%d+).-)")
if id then
local itemNameResolved, itemLink, itemQuality, itemReqLevel, _, itemSubtype, _, itemEquipLoc, itemIcon = GetItemInfo(id)
local _, _, linkName = string.find(link, "%[(.+)%]")
local itemName = itemNameResolved or linkName
if itemName and zButtonRanged_IsRangedItem(itemSubtype, itemEquipLoc) then
local _, itemCount = GetContainerItemInfo(bag, slot)
local knownSpeed = zButtonRanged_GetKnownItemSpeed(id)
local rangedInfo = zButtonRanged_Cache[itemName]
if not rangedInfo then
rangedInfo = {
name = itemName,
quality = itemQuality,
reqLevel = itemReqLevel,
subtype = itemSubtype,
equipLoc = itemEquipLoc,
bag = bag,
slot = slot,
id = id,
link = link,
icon = itemIcon,
speed = knownSpeed,
count = 0,
}
zButtonRanged_Cache[itemName] = rangedInfo
end
if rangedInfo.speed == nil and knownSpeed ~= nil then
rangedInfo.speed = knownSpeed
end
rangedInfo.count = (rangedInfo.count or 0) + (itemCount or 0)
end
end
end
end
end
end
local function zButtonRanged_GetOrderedWeapons()
local names = {}
for weaponName, weaponInfo in pairs(zButtonRanged_Cache) do
if weaponName and weaponInfo and (tonumber(weaponInfo.count) or 0) > 0 then
table.insert(names, weaponName)
end
end
table.sort(names, function(a, b)
local infoA = zButtonRanged_Cache[a] or {}
local infoB = zButtonRanged_Cache[b] or {}
local lvlA = tonumber(infoA.reqLevel) or 0
local lvlB = tonumber(infoB.reqLevel) or 0
if lvlA ~= lvlB then
return lvlA < lvlB
end
local qualityA = tonumber(infoA.quality) or 0
local qualityB = tonumber(infoB.quality) or 0
if qualityA ~= qualityB then
return qualityA < qualityB
end
return tostring(a) < tostring(b)
end)
return names
end
local function zButtonRanged_EnsureSpellOrder(weapons)
local saved = zButtonRanged_GetSaved()
if not saved["spells"] then
saved["spells"] = {}
end
if not saved["visible"] then
saved["visible"] = {}
end
local maxWeapons = table.getn(weapons or {})
local needsReset = false
for i = 1, maxWeapons do
local index = saved["spells"][i]
if not tonumber(index) or index < 1 or index > maxWeapons then
needsReset = true
break
end
end
if needsReset or table.getn(saved["spells"]) ~= maxWeapons then
saved["spells"] = {}
for i = 1, maxWeapons do
saved["spells"][i] = i
end
end
for i = 1, maxWeapons do
if saved["visible"][i] == nil then
saved["visible"][i] = 1
end
end
end
local function zButtonRanged_AssignButtonFromInfo(button, weaponInfo)
if not (button and weaponInfo) then
return false
end
button.id = weaponInfo.id
button.icon = weaponInfo.icon
button.isspell = nil
button.ammoname = weaponInfo.name
button.ammobag = weaponInfo.bag
button.ammoslot = weaponInfo.slot
button.ammoid = weaponInfo.id
button.ammolink = weaponInfo.link
button.ammocount = weaponInfo.count
button.rangedType = weaponInfo.subtype
button.rangedReqLevel = weaponInfo.reqLevel
button.rangedQuality = weaponInfo.quality
button.rangedSpeed = weaponInfo.speed
return true
end
local function zButtonRanged_ApplyRuntimeSettings()
if not zButtonRanged then
return
end
local saved = zButtonRanged_GetSaved()
zButtonRanged.tooltip = saved["tooltip"] and true or false
zButtonRanged.hideonclick = saved["children"] and saved["children"]["hideonclick"] and true or false
zButtonRanged.expandonhover = saved["children"] and saved["children"]["expandonhover"] and true or false
zButtonRanged.fadetimer = saved["children"] and tonumber(saved["children"]["fadetimer"]) or 0
end
local function zButtonRanged_UpdateDurabilityText(button)
local text = zButtonRanged_EnsureDurabilityText(button)
if not text then
return
end
if button ~= zButtonRanged then
text:SetText("")
return
end
if type(GetInventoryItemDurability) ~= "function" then
text:SetText("")
return
end
local cur, max = nil, nil
local okPlayer, cPlayer, mPlayer = pcall(GetInventoryItemDurability, "player", 18)
if okPlayer then
cur, max = cPlayer, mPlayer
else
local okSlot, cSlot, mSlot = pcall(GetInventoryItemDurability, 18)
if okSlot then
cur, max = cSlot, mSlot
end
end
cur = tonumber(cur)
max = tonumber(max)
if not cur or not max or max <= 0 then
text:SetText("")
return
end
local perc = math.floor(((cur / max) * 100) + 0.5)
if perc >= 100 then
text:SetText("")
return
end
local r, g, b = zButtonRanged_GetColorGradient(cur / max)
text:SetTextColor(r, g, b)
text:SetText(tostring(perc) .. "%")
end
local function zButtonRanged_UpdateSpeedText(button)
local text = zButtonRanged_EnsureSpeedText(button)
if not text then
return
end
local speedVal = tonumber(button and button.rangedSpeed)
if not speedVal then
text:SetText("")
return
end
text:SetText(string.format("%.2f", speedVal))
end
local function zButtonRanged_UpdateParentFromEquipped()
if not zButtonRanged then
return false
end
local equippedLink = GetInventoryItemLink("player", 18)
if not equippedLink then
return false
end
local _, _, linkName = string.find(equippedLink, "%[(.+)%]")
local _, _, itemId = string.find(equippedLink, "item:(%d+)")
if not itemId then
local _, _, altId = string.find(equippedLink, "Hitem:((%d+).-)")
itemId = altId
end
local itemName, _, itemQuality, itemReqLevel, _, itemSubtype, _, itemEquipLoc, itemIcon = GetItemInfo(itemId)
itemName = itemName or linkName
if not itemName then
return false
end
if (itemSubtype or itemEquipLoc) and not zButtonRanged_IsRangedItem(itemSubtype, itemEquipLoc) then
return false
end
local cacheInfo = zButtonRanged_Cache[itemName]
local info = {
name = itemName,
quality = itemQuality,
reqLevel = itemReqLevel,
subtype = itemSubtype,
equipLoc = itemEquipLoc,
bag = cacheInfo and cacheInfo.bag or nil,
slot = cacheInfo and cacheInfo.slot or nil,
id = itemId,
link = equippedLink,
icon = itemIcon or GetInventoryItemTexture("player", 18),
speed = (cacheInfo and cacheInfo.speed) or zButtonRanged_GetKnownItemSpeed(itemId),
count = cacheInfo and cacheInfo.count or 1,
}
if not info.icon then
info.icon = GetInventoryItemTexture("player", 18)
end
zButtonRanged_AssignButtonFromInfo(zButtonRanged, info)
zButtonRanged:Enable()
zButtonRanged:Show()
return true
end
local function zButtonRanged_SetButtons(parent, weaponNames)
if not (parent and parent.count) then
return 0
end
for i = 1, parent.count do
local child = getglobal(parent.name .. i)
if child then
child:Hide()
child.id = nil
child.icon = nil
child.isspell = nil
child.ammoname = nil
child.ammobag = nil
child.ammoslot = nil
end
end
local found = 0
for i = 1, table.getn(weaponNames or {}) do
if found >= parent.count then
break
end
local weaponName = weaponNames[i]
local weaponInfo = weaponName and zButtonRanged_Cache[weaponName] or nil
if weaponInfo and weaponInfo.id then
found = found + 1
local child = getglobal(parent.name .. found)
if child then
zButtonRanged_AssignButtonFromInfo(child, weaponInfo)
zButtonRanged_UpdateButton(child)
child:Show()
end
end
end
local hasEquipped = zButtonRanged_UpdateParentFromEquipped()
if not hasEquipped and found > 0 then
local first = getglobal(parent.name .. 1)
if first then
zButtonRanged_AssignButtonFromInfo(parent, {
name = first.ammoname,
quality = first.rangedQuality,
reqLevel = first.rangedReqLevel,
subtype = first.rangedType,
equipLoc = nil,
bag = first.ammobag,
slot = first.ammoslot,
id = first.ammoid or first.id,
link = first.ammolink,
icon = first.icon,
speed = first.rangedSpeed,
count = first.ammocount,
})
zButtonRanged_UpdateButton(parent)
parent:Enable()
parent:Show()
end
end
if found <= 0 and not hasEquipped then
parent.id = nil
parent.icon = nil
parent.ammoname = nil
parent:Hide()
else
parent:Show()
end
return found
end
function zButtonRanged_UpdateButton(button)
if not button then
button = this
end
if not (button and button.icontexture) then
return
end
if not button.id then
button.icontexture:Hide()
zButtonRanged_UpdateSpeedText(button)
zButtonRanged_UpdateDurabilityText(button)
return
end
button.icontexture:SetTexture(button.icon)
button.icontexture:Show()
zButtonRanged_UpdateSpeedText(button)
zButtonRanged_UpdateDurabilityText(button)
end
local function zButtonRanged_OnEnter(button)
if not button then
button = this
end
if not button then
return
end
if zButtonRanged_BagDirty then
local previousFound = tonumber(zButtonRanged and zButtonRanged.found) or 0
zButtonRanged_RefreshButtonsFromState()
if (tonumber(zButtonRanged and zButtonRanged.found) or 0) ~= previousFound then
zButtonRanged_SetupSizeAndPosition()
else
zButtonRanged_UpdateButton(zButtonRanged)
end
end
if not (zButtonRanged and zButtonRanged.tooltip) then
return
end
GameTooltip:SetOwner(button, "ANCHOR_TOPLEFT")
if button == zButtonRanged then
if not GameTooltip:SetInventoryItem("player", 18) and button.ammobag and button.ammoslot then
GameTooltip:SetBagItem(button.ammobag, button.ammoslot)
end
elseif button.ammobag and button.ammoslot then
GameTooltip:SetBagItem(button.ammobag, button.ammoslot)
elseif button.ammolink then
GameTooltip:SetHyperlink(button.ammolink)
end
GameTooltip:Show()
end
local function zButtonRanged_PrewarmTooltipOnce()
if zButtonRanged_TooltipWarmed then
return
end
zButtonRanged_TooltipWarmed = true
if not GameTooltip then
return
end
GameTooltip:SetOwner(UIParent, "ANCHOR_NONE")
GameTooltip:ClearLines()
if zButtonRanged and zButtonRanged.ammolink then
GameTooltip:SetHyperlink(zButtonRanged.ammolink)
elseif zButtonRanged and zButtonRanged.ammobag and zButtonRanged.ammoslot then
GameTooltip:SetBagItem(zButtonRanged.ammobag, zButtonRanged.ammoslot)
else
GameTooltip:SetInventoryItem("player", 18)
end
GameTooltip:Hide()
end
local function zButtonRanged_EquipFromButton(button)
if not (button and button.ammobag and button.ammoslot) then
return false
end
PickupContainerItem(button.ammobag, button.ammoslot)
if CursorHasItem() then
EquipCursorItem(18)
end
if CursorHasItem() then
if ClearCursor then ClearCursor() end
return false
end
zButtonRanged_UpdateParentFromEquipped()
zButtonRanged_UpdateButton(zButtonRanged)
return true
end
local function zButtonRanged_BeforeClick(button)
if not button then
return false
end
if button.isspell then
return false
end
zButtonRanged_EquipFromButton(button)
return true
end
zButtonRanged_RefreshButtonsFromState = function()
if not zButtonRanged then
return
end
local saved = zButtonRanged_GetSaved()
zButtonRanged_RebuildCache()
local weapons = zButtonRanged_GetOrderedWeapons()
ZHunterMod_Ranged_Weapons = weapons
zButtonRanged_EnsureSpellOrder(weapons)
local info = {}
local infoIndex = 1
for i = 1, table.getn(weapons) do
local weaponIndex = saved["spells"][i]
if weaponIndex and saved["visible"][weaponIndex] ~= false then
info[infoIndex] = weapons[weaponIndex]
infoIndex = infoIndex + 1
end
end
zButtonRanged.found = ZSpellButton_SetButtons(zButtonRanged, info)
zButtonRanged_BagDirty = false
zButtonRanged_ApplyRuntimeSettings()
end
local function zButtonRanged_EnsureConfig()
local saved = zButtonRanged_GetSaved()
saved["count"] = nil
if saved["enabled"] == nil then
saved["enabled"] = false
end
if saved["tooltip"] == nil then
saved["tooltip"] = 1
end
if not saved["children"] then
saved["children"] = {}
end
if saved["children"]["hideonclick"] == nil then
saved["children"]["hideonclick"] = 1
end
if not saved["spells"] then
saved["spells"] = {}
end
if not saved["visible"] then
saved["visible"] = {}
end
if not saved["parent"] then
saved["parent"] = {}
end
if not saved["children"]["size"] then
saved["children"]["size"] = 36
end
if not saved["parent"]["size"] then
saved["parent"]["size"] = 36
end
if saved["parent"]["circle"] == nil then
saved["parent"]["circle"] = 1
end
if not saved["rows"] or saved["rows"] < 1 then
saved["rows"] = 1
end
if not saved["firstbutton"] then
saved["firstbutton"] = "RIGHT"
end
end
function zButtonRanged_OnLoad()
this:RegisterEvent("VARIABLES_LOADED")
end
function zButtonRanged_OnEvent()
if event == "VARIABLES_LOADED" then
if not zButtonRanged then
return
end
if UnitClass("player") ~= ZHUNTER_HUNTER then
zButtonRanged:UnregisterAllEvents()
zButtonRanged:Hide()
return
end
zButtonRanged.customSetButtons = zButtonRanged_SetButtons
zButtonRanged.customUpdateButton = zButtonRanged_UpdateButton
zButtonRanged.beforeclick = zButtonRanged_BeforeClick
zButtonRanged_CreateButtons()
MTH_ZH_RangedAdjust = CreateFrame("Frame", "MTH_ZH_RangedAdjust")
MTH_ZH_RangedAdjust:RegisterEvent("UNIT_INVENTORY_CHANGED")
MTH_ZH_RangedAdjust:RegisterEvent("BAG_UPDATE")
MTH_ZH_RangedAdjust:RegisterEvent("PLAYER_ENTERING_WORLD")
MTH_ZH_RangedAdjust:SetScript("OnEvent", MTH_ZH_RangedAdjust_OnEvent)
zButtonRanged_SetupSizeAndPosition()
end
end
function zButtonRanged_CreateButtons()
zButtonRanged_EnsureConfig()
if not zButtonRanged.children then
ZSpellButton_CreateChildren(zButtonRanged, "zButtonRanged", ZHUNTER_RANGED_MAX_COUNT)
else
zButtonRanged.count = ZHUNTER_RANGED_MAX_COUNT
end
zButtonRanged:SetScript("OnEnter", function()
zButtonRanged_OnEnter(this)
end)
for i = 1, zButtonRanged.count do
local child = getglobal("zButtonRanged" .. i)
if child then
child:SetScript("OnEnter", function()
zButtonRanged_OnEnter(this)
end)
end
end
zButtonRanged_RefreshButtonsFromState()
end
function zButtonRanged_SetupSizeAndPosition()
local saved = zButtonRanged_GetSaved()
if saved["enabled"] == false or saved["enabled"] == 0 then
if MTH_ZH_RangedAdjust and MTH_ZH_RangedAdjust.SetScript then
MTH_ZH_RangedAdjust:SetScript("OnEvent", nil)
end
if zButtonRanged and zButtonRanged.Hide then
zButtonRanged:Hide()
end
return
end
if MTH_ZH_RangedAdjust and MTH_ZH_RangedAdjust.SetScript then
MTH_ZH_RangedAdjust:SetScript("OnEvent", MTH_ZH_RangedAdjust_OnEvent)
end
local displayCount = zButtonRanged.found or 0
if displayCount < 0 then
displayCount = 0
end
if not (type(MTH_ZBar_ApplyToButton) == "function" and MTH_ZBar_ApplyToButton(zButtonRanged, displayCount)) then
ZSpellButton_SetSize(zButtonRanged, saved["parent"]["size"])
ZSpellButton_SetSize(zButtonRanged, saved["children"]["size"], 1)
ZSpellButton_SetExpandDirection(zButtonRanged, saved["firstbutton"])
ZSpellButton_ArrangeChildren(zButtonRanged, saved["rows"], displayCount, saved["horizontal"], saved["vertical"])
end
zButtonRanged_UpdateButton(zButtonRanged)
end
function zButtonRanged_Reset()
local currentRoot = zButtonRanged_GetRoot()
currentRoot["zButtonRanged"] = {}
local saved = zButtonRanged_GetSaved()
saved["spells"] = {}
saved["visible"] = {}
saved["rows"] = 1
saved["horizontal"] = nil
saved["vertical"] = nil
saved["firstbutton"] = "RIGHT"
saved["enabled"] = false
saved["tooltip"] = 1
saved["parent"] = {}
saved["parent"]["size"] = 36
saved["parent"]["hide"] = nil
saved["parent"]["circle"] = 1
saved["children"] = {}
saved["children"]["size"] = 36
saved["children"]["hideonclick"] = 1
zButtonRanged_EnsureConfig()
end
function MTH_ZH_RangedAdjust_OnEvent()
if not zButtonRanged then
return
end
if event == "UNIT_INVENTORY_CHANGED" and arg1 and arg1 ~= "player" then
return
end
if event == "BAG_UPDATE" then
local now = GetTime and GetTime() or 0
local isInspecting = false
if zButtonRanged.children and zButtonRanged.children.IsShown and zButtonRanged.children:IsShown() then
isInspecting = true
elseif GameTooltip and GameTooltip.IsOwned and GameTooltip:IsOwned(zButtonRanged) then
isInspecting = true
end
if not isInspecting then
zButtonRanged_BagDirty = true
return
end
local minInterval = isInspecting and zButtonRanged_MinBagRefreshInterval or zButtonRanged_IdleBagRefreshInterval
if now > 0 and zButtonRanged_LastRefreshAt > 0 and (now - zButtonRanged_LastRefreshAt) < minInterval then
return
end
end
zButtonRanged_LastRefreshAt = GetTime and GetTime() or 0
local previousFound = tonumber(zButtonRanged.found) or 0
zButtonRanged_RefreshButtonsFromState()
if (tonumber(zButtonRanged.found) or 0) ~= previousFound then
zButtonRanged_SetupSizeAndPosition()
else
zButtonRanged_UpdateButton(zButtonRanged)
end
if event == "PLAYER_ENTERING_WORLD" then
zButtonRanged_PrewarmTooltipOnce()
end
if GameTooltip:IsOwned(zButtonRanged) then
zButtonRanged_OnEnter(zButtonRanged)
end
end
function zButtonRanged_KeyBinding(index)
if MTH_ZH_IsModuleEnabled and not MTH_ZH_IsModuleEnabled() then
return
end
local button
if index then
button = getglobal("zButtonRanged" .. index)
else
button = zButtonRanged
end
if button then
zButtonRanged_EquipFromButton(button)
if zButtonRanged.hideonclick then
if type(ZSpellButton_SetChildrenExpanded) == "function" then
ZSpellButton_SetChildrenExpanded(zButtonRanged, false)
elseif zButtonRanged.children then
zButtonRanged.children:Hide()
end
end
end
end
SLASH_zButtonRanged1 = "/ZRanged"
SlashCmdList["zButtonRanged"] = function(msg)
if MTH_ZH_HandleDisabledSlash and MTH_ZH_HandleDisabledSlash("Ranged button is disabled while module 'zhunter' is disabled.") then
return
end
if msg == "reset" then
zButtonRanged_Reset()
zButtonRanged:ClearAllPoints()
zButtonRanged:SetPoint("CENTER", UIParent, "CENTER", 120, 0)
zButtonRanged_RefreshButtonsFromState()
zButtonRanged_SetupSizeAndPosition()
elseif msg == "options" then
MTH_OpenOptions("Ranged")
else
MTH_ZH_Print("Possible Commands: \"reset\", \"options\"")
end
end