944 lines
31 KiB
Lua
944 lines
31 KiB
Lua
SFrames.Pet = {}
|
|
local _A = SFrames.ActiveTheme
|
|
|
|
local function Clamp(value, minValue, maxValue)
|
|
if value < minValue then return minValue end
|
|
if value > maxValue then return maxValue end
|
|
return value
|
|
end
|
|
|
|
function SFrames.Pet:Initialize()
|
|
local f = CreateFrame("Button", "SFramesPetFrame", UIParent)
|
|
f:SetWidth(150)
|
|
f:SetHeight(30)
|
|
|
|
if SFramesDB and SFramesDB.Positions and SFramesDB.Positions["PetFrame"] then
|
|
local pos = SFramesDB.Positions["PetFrame"]
|
|
f:SetPoint(pos.point, UIParent, pos.relativePoint, pos.xOfs, pos.yOfs)
|
|
else
|
|
f:SetPoint("TOPLEFT", SFramesPlayerFrame, "BOTTOMLEFT", 10, -55)
|
|
end
|
|
|
|
local frameScale = (SFramesDB and type(SFramesDB.petFrameScale) == "number") and SFramesDB.petFrameScale or 1
|
|
f:SetScale(Clamp(frameScale, 0.7, 1.8))
|
|
|
|
f:SetMovable(true)
|
|
f:EnableMouse(true)
|
|
f:RegisterForDrag("LeftButton")
|
|
f:SetScript("OnDragStart", function() if IsAltKeyDown() or SFrames.isUnlocked then f:StartMoving() end end)
|
|
f:SetScript("OnDragStop", function()
|
|
f:StopMovingOrSizing()
|
|
if not SFramesDB then SFramesDB = {} end
|
|
if not SFramesDB.Positions then SFramesDB.Positions = {} end
|
|
local point, relativeTo, relativePoint, xOfs, yOfs = f:GetPoint()
|
|
SFramesDB.Positions["PetFrame"] = { point = point, relativePoint = relativePoint, xOfs = xOfs, yOfs = yOfs }
|
|
end)
|
|
|
|
f:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
|
f:SetScript("OnClick", function()
|
|
if arg1 == "LeftButton" then
|
|
if SpellIsTargeting() then
|
|
SpellTargetUnit("pet")
|
|
elseif CursorHasItem() then
|
|
DropItemOnUnit("pet")
|
|
else
|
|
TargetUnit("pet")
|
|
end
|
|
else
|
|
ToggleDropDownMenu(1, nil, PetFrameDropDown, "SFramesPetFrame", 106, 27)
|
|
end
|
|
end)
|
|
|
|
f:SetScript("OnReceiveDrag", function()
|
|
if CursorHasItem() then
|
|
DropItemOnUnit("pet")
|
|
end
|
|
end)
|
|
|
|
f:SetScript("OnEnter", function()
|
|
GameTooltip_SetDefaultAnchor(GameTooltip, this)
|
|
GameTooltip:SetUnit("pet")
|
|
GameTooltip:Show()
|
|
end)
|
|
f:SetScript("OnLeave", function()
|
|
GameTooltip:Hide()
|
|
end)
|
|
|
|
SFrames:CreateUnitBackdrop(f)
|
|
|
|
-- Health Bar
|
|
f.health = SFrames:CreateStatusBar(f, "SFramesPetHealth")
|
|
f.health:SetPoint("TOPLEFT", f, "TOPLEFT", 1, -1)
|
|
f.health:SetPoint("TOPRIGHT", f, "TOPRIGHT", -1, -1)
|
|
f.health:SetHeight(18)
|
|
|
|
local hbg = CreateFrame("Frame", nil, f)
|
|
hbg:SetPoint("TOPLEFT", f.health, "TOPLEFT", -1, 1)
|
|
hbg:SetPoint("BOTTOMRIGHT", f.health, "BOTTOMRIGHT", 1, -1)
|
|
hbg:SetFrameLevel(f:GetFrameLevel() - 1)
|
|
SFrames:CreateUnitBackdrop(hbg)
|
|
|
|
f.health.bg = f.health:CreateTexture(nil, "BACKGROUND")
|
|
f.health.bg:SetAllPoints()
|
|
f.health.bg:SetTexture(SFrames:GetTexture())
|
|
f.health.bg:SetVertexColor(_A.slotBg[1], _A.slotBg[2], _A.slotBg[3], _A.slotBg[4] or 1)
|
|
|
|
-- Power Bar
|
|
f.power = SFrames:CreateStatusBar(f, "SFramesPetPower")
|
|
f.power:SetPoint("TOPLEFT", f.health, "BOTTOMLEFT", 0, -1)
|
|
f.power:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -1, 1)
|
|
|
|
local pbg = CreateFrame("Frame", nil, f)
|
|
pbg:SetPoint("TOPLEFT", f.power, "TOPLEFT", -1, 1)
|
|
pbg:SetPoint("BOTTOMRIGHT", f.power, "BOTTOMRIGHT", 1, -1)
|
|
pbg:SetFrameLevel(f:GetFrameLevel() - 1)
|
|
SFrames:CreateUnitBackdrop(pbg)
|
|
|
|
f.power.bg = f.power:CreateTexture(nil, "BACKGROUND")
|
|
f.power.bg:SetAllPoints()
|
|
f.power.bg:SetTexture(SFrames:GetTexture())
|
|
f.power.bg:SetVertexColor(_A.slotBg[1], _A.slotBg[2], _A.slotBg[3], _A.slotBg[4] or 1)
|
|
|
|
-- Texts
|
|
local fontPath = SFrames:GetFont()
|
|
local outline = (SFrames and SFrames.Media and SFrames.Media.fontOutline) or "OUTLINE"
|
|
|
|
f.nameText = SFrames:CreateFontString(f.health, 10, "LEFT")
|
|
f.nameText:SetPoint("LEFT", f.health, "LEFT", 4, 0)
|
|
f.nameText:SetWidth(75)
|
|
f.nameText:SetHeight(12)
|
|
f.nameText:SetJustifyH("LEFT")
|
|
f.nameText:SetFont(fontPath, 10, outline)
|
|
f.nameText:SetShadowColor(0, 0, 0, 1)
|
|
f.nameText:SetShadowOffset(1, -1)
|
|
|
|
f.healthText = SFrames:CreateFontString(f.health, 10, "RIGHT")
|
|
f.healthText:SetPoint("RIGHT", f.health, "RIGHT", -4, 0)
|
|
f.healthText:SetFont(fontPath, 10, outline)
|
|
f.healthText:SetShadowColor(0, 0, 0, 1)
|
|
f.healthText:SetShadowOffset(1, -1)
|
|
|
|
-- Happiness Icon (for hunters)
|
|
local hBG = CreateFrame("Frame", nil, f)
|
|
hBG:SetWidth(20)
|
|
hBG:SetHeight(20)
|
|
hBG:SetPoint("RIGHT", f, "LEFT", -2, 0)
|
|
SFrames:CreateUnitBackdrop(hBG)
|
|
|
|
f.happiness = hBG:CreateTexture(nil, "OVERLAY")
|
|
f.happiness:SetPoint("TOPLEFT", hBG, "TOPLEFT", 1, -1)
|
|
f.happiness:SetPoint("BOTTOMRIGHT", hBG, "BOTTOMRIGHT", -1, 1)
|
|
f.happiness:SetTexture("Interface\\PetPaperDollFrame\\UI-PetHappiness")
|
|
|
|
f.happinessBG = hBG
|
|
f.happinessBG:Hide()
|
|
|
|
self.frame = f
|
|
self.frame.unit = "pet"
|
|
f:Hide()
|
|
|
|
self:CreateCastbar()
|
|
|
|
SFrames:RegisterEvent("UNIT_PET", function() if arg1 == "player" then self:UpdateAll() end end)
|
|
SFrames:RegisterEvent("PET_BAR_UPDATE", function() self:UpdateAll() end)
|
|
SFrames:RegisterEvent("UNIT_HEALTH", function() if arg1 == "pet" then self:UpdateHealth() end end)
|
|
SFrames:RegisterEvent("UNIT_MAXHEALTH", function() if arg1 == "pet" then self:UpdateHealth() end end)
|
|
SFrames:RegisterEvent("UNIT_MANA", function() if arg1 == "pet" then self:UpdatePower() end end)
|
|
SFrames:RegisterEvent("UNIT_MAXMANA", function() if arg1 == "pet" then self:UpdatePower() end end)
|
|
SFrames:RegisterEvent("UNIT_ENERGY", function() if arg1 == "pet" then self:UpdatePower() end end)
|
|
SFrames:RegisterEvent("UNIT_MAXENERGY", function() if arg1 == "pet" then self:UpdatePower() end end)
|
|
SFrames:RegisterEvent("UNIT_FOCUS", function() if arg1 == "pet" then self:UpdatePower() end end)
|
|
SFrames:RegisterEvent("UNIT_MAXFOCUS", function() if arg1 == "pet" then self:UpdatePower() end end)
|
|
SFrames:RegisterEvent("UNIT_RAGE", function() if arg1 == "pet" then self:UpdatePower() end end)
|
|
SFrames:RegisterEvent("UNIT_MAXRAGE", function() if arg1 == "pet" then self:UpdatePower() end end)
|
|
SFrames:RegisterEvent("UNIT_DISPLAYPOWER", function() if arg1 == "pet" then self:UpdatePowerType() end end)
|
|
SFrames:RegisterEvent("UNIT_HAPPINESS", function() if arg1 == "pet" then self:UpdateHappiness() end end)
|
|
SFrames:RegisterEvent("UNIT_NAME_UPDATE", function() if arg1 == "pet" then self:UpdateAll() end end)
|
|
SFrames:RegisterEvent("PLAYER_ENTERING_WORLD", function() self:UpdateAll() end)
|
|
|
|
self:InitFoodFeature()
|
|
self:UpdateAll()
|
|
end
|
|
|
|
function SFrames.Pet:UpdateAll()
|
|
if UnitExists("pet") then
|
|
if SFramesDB and SFramesDB.showPetFrame == false then
|
|
self.frame:Hide()
|
|
if self.foodPanel then self.foodPanel:Hide() end
|
|
return
|
|
end
|
|
|
|
self.frame:Show()
|
|
self:UpdateHealth()
|
|
self:UpdatePowerType()
|
|
self:UpdatePower()
|
|
self:UpdateHappiness()
|
|
|
|
local name = UnitName("pet")
|
|
if name == UNKNOWNOBJECT or name == "未知目标" or name == "Unknown" then
|
|
name = "宠物"
|
|
end
|
|
self.frame.nameText:SetText(name)
|
|
|
|
local r, g, b = 0.33, 0.59, 0.33
|
|
self.frame.health:SetStatusBarColor(r, g, b)
|
|
else
|
|
self.frame:Hide()
|
|
if self.foodPanel then self.foodPanel:Hide() end
|
|
end
|
|
end
|
|
|
|
function SFrames.Pet:UpdateHealth()
|
|
local hp = UnitHealth("pet")
|
|
local maxHp = UnitHealthMax("pet")
|
|
self.frame.health:SetMinMaxValues(0, maxHp)
|
|
self.frame.health:SetValue(hp)
|
|
|
|
if maxHp > 0 then
|
|
self.frame.healthText:SetText(hp .. " / " .. maxHp)
|
|
else
|
|
self.frame.healthText:SetText("")
|
|
end
|
|
end
|
|
|
|
function SFrames.Pet:UpdatePowerType()
|
|
local powerType = UnitPowerType("pet")
|
|
local color = SFrames.Config.colors.power[powerType]
|
|
if color then
|
|
self.frame.power:SetStatusBarColor(color.r, color.g, color.b)
|
|
else
|
|
self.frame.power:SetStatusBarColor(0, 0, 1)
|
|
end
|
|
end
|
|
|
|
function SFrames.Pet:UpdatePower()
|
|
local power = UnitMana("pet")
|
|
local maxPower = UnitManaMax("pet")
|
|
self.frame.power:SetMinMaxValues(0, maxPower)
|
|
self.frame.power:SetValue(power)
|
|
end
|
|
|
|
function SFrames.Pet:UpdateHappiness()
|
|
local happiness = GetPetHappiness()
|
|
if not happiness then
|
|
self.frame.happinessBG:Hide()
|
|
self:UpdateFoodButton()
|
|
return
|
|
end
|
|
|
|
local isHunter = false
|
|
local _, class = UnitClass("player")
|
|
if class == "HUNTER" then isHunter = true end
|
|
|
|
if isHunter then
|
|
if happiness == 1 then
|
|
self.frame.happiness:SetTexCoord(0.375, 0.5625, 0, 0.359375)
|
|
self.frame.happinessBG:Show()
|
|
elseif happiness == 2 then
|
|
self.frame.happiness:SetTexCoord(0.1875, 0.375, 0, 0.359375)
|
|
self.frame.happinessBG:Show()
|
|
elseif happiness == 3 then
|
|
self.frame.happiness:SetTexCoord(0, 0.1875, 0, 0.359375)
|
|
self.frame.happinessBG:Show()
|
|
end
|
|
else
|
|
self.frame.happinessBG:Hide()
|
|
end
|
|
self:UpdateFoodButton()
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Pet Food Feature (Hunter only)
|
|
-- Food button on pet frame, food selection panel, quick-feed via right-click
|
|
--------------------------------------------------------------------------------
|
|
|
|
local petFoodScanTip
|
|
local cachedFeedSpell
|
|
|
|
local function EnsureFoodScanTooltip()
|
|
if not petFoodScanTip then
|
|
petFoodScanTip = CreateFrame("GameTooltip", "NanamiPetFoodScanTip", UIParent, "GameTooltipTemplate")
|
|
petFoodScanTip:SetOwner(UIParent, "ANCHOR_NONE")
|
|
end
|
|
return petFoodScanTip
|
|
end
|
|
|
|
local function GetFeedPetSpell()
|
|
if cachedFeedSpell then return cachedFeedSpell end
|
|
for tab = 1, GetNumSpellTabs() do
|
|
local _, _, offset, numSpells = GetSpellTabInfo(tab)
|
|
for i = offset + 1, offset + numSpells do
|
|
local spellName = GetSpellName(i, BOOKTYPE_SPELL)
|
|
if spellName and (spellName == "Feed Pet" or spellName == "喂养宠物") then
|
|
cachedFeedSpell = spellName
|
|
return cachedFeedSpell
|
|
end
|
|
end
|
|
end
|
|
cachedFeedSpell = "Feed Pet"
|
|
return cachedFeedSpell
|
|
end
|
|
|
|
local REJECT_NAME_PATTERNS = {
|
|
"Potion", "potion", "药水",
|
|
"Elixir", "elixir", "药剂",
|
|
"Flask", "flask", "合剂",
|
|
"Bandage", "bandage", "绷带",
|
|
"Scroll", "scroll", "卷轴",
|
|
"Healthstone", "healthstone", "治疗石",
|
|
"Mana Gem", "法力宝石",
|
|
"Thistle Tea", "蓟花茶",
|
|
"Firewater", "火焰花水",
|
|
"Juju", "符咒",
|
|
}
|
|
|
|
local function NameIsRejected(itemName)
|
|
for i = 1, table.getn(REJECT_NAME_PATTERNS) do
|
|
if string.find(itemName, REJECT_NAME_PATTERNS[i], 1, true) then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function IsItemPetFood(bag, slot)
|
|
local link = GetContainerItemLink(bag, slot)
|
|
if not link then return false end
|
|
|
|
local texture = GetContainerItemInfo(bag, slot)
|
|
|
|
local _, _, itemIdStr = string.find(link, "item:(%d+)")
|
|
local name, itemType, subType
|
|
|
|
if itemIdStr then
|
|
local n, _, _, _, _, t, st = GetItemInfo("item:" .. itemIdStr)
|
|
name = n
|
|
itemType = t
|
|
subType = st
|
|
end
|
|
|
|
if not name then
|
|
local _, _, parsed = string.find(link, "%[(.+)%]")
|
|
name = parsed
|
|
end
|
|
if not name then return false end
|
|
|
|
if NameIsRejected(name) then
|
|
return false
|
|
end
|
|
|
|
if itemType then
|
|
if itemType ~= "Consumable" and itemType ~= "消耗品" then
|
|
return false
|
|
end
|
|
if subType then
|
|
if string.find(subType, "Potion", 1, true) or string.find(subType, "药水", 1, true)
|
|
or string.find(subType, "Elixir", 1, true) or string.find(subType, "药剂", 1, true)
|
|
or string.find(subType, "Flask", 1, true) or string.find(subType, "合剂", 1, true)
|
|
or string.find(subType, "Bandage", 1, true) or string.find(subType, "绷带", 1, true)
|
|
or string.find(subType, "Scroll", 1, true) or string.find(subType, "卷轴", 1, true) then
|
|
return false
|
|
end
|
|
if string.find(subType, "Food", 1, true) or string.find(subType, "食物", 1, true) then
|
|
return true, name, texture
|
|
end
|
|
end
|
|
end
|
|
|
|
local tip = EnsureFoodScanTooltip()
|
|
tip:SetOwner(UIParent, "ANCHOR_NONE")
|
|
tip:SetBagItem(bag, slot)
|
|
|
|
local found = false
|
|
local rejected = false
|
|
for i = 1, tip:NumLines() do
|
|
local leftObj = _G["NanamiPetFoodScanTipTextLeft" .. i]
|
|
if leftObj then
|
|
local text = leftObj:GetText()
|
|
if text then
|
|
if string.find(text, "进食", 1, true) or string.find(text, "eating", 1, true) then
|
|
found = true
|
|
end
|
|
if string.find(text, "Well Fed", 1, true) or string.find(text, "充分进食", 1, true) then
|
|
found = true
|
|
end
|
|
if string.find(text, "Restores", 1, true) and string.find(text, "health", 1, true)
|
|
and string.find(text, "over", 1, true) then
|
|
found = true
|
|
end
|
|
if string.find(text, "恢复", 1, true) and string.find(text, "生命", 1, true)
|
|
and string.find(text, "秒", 1, true) then
|
|
found = true
|
|
end
|
|
if string.find(text, "Potion", 1, true) or string.find(text, "药水", 1, true)
|
|
or string.find(text, "Elixir", 1, true) or string.find(text, "药剂", 1, true)
|
|
or string.find(text, "Bandage", 1, true) or string.find(text, "绷带", 1, true) then
|
|
rejected = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
tip:Hide()
|
|
|
|
if found and not rejected then
|
|
return true, name, texture
|
|
end
|
|
|
|
if itemType and subType then
|
|
if (itemType == "Consumable" or itemType == "消耗品")
|
|
and (subType == "Food & Drink" or subType == "食物和饮料") then
|
|
return true, name, texture
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function SFrames.Pet:ScanBagsForFood()
|
|
local foods = {}
|
|
for bag = 0, 4 do
|
|
for slot = 1, GetContainerNumSlots(bag) do
|
|
local isFood, name, itemTex = IsItemPetFood(bag, slot)
|
|
if isFood then
|
|
local texture, itemCount = GetContainerItemInfo(bag, slot)
|
|
local link = GetContainerItemLink(bag, slot)
|
|
table.insert(foods, {
|
|
bag = bag,
|
|
slot = slot,
|
|
name = name,
|
|
link = link,
|
|
texture = texture or itemTex,
|
|
count = itemCount or 1,
|
|
})
|
|
end
|
|
end
|
|
end
|
|
return foods
|
|
end
|
|
|
|
function SFrames.Pet:FeedPet(bag, slot)
|
|
if not UnitExists("pet") then return end
|
|
local link = GetContainerItemLink(bag, slot)
|
|
local tex = GetContainerItemInfo(bag, slot)
|
|
local spell = GetFeedPetSpell()
|
|
CastSpellByName(spell)
|
|
PickupContainerItem(bag, slot)
|
|
if link then
|
|
local _, _, itemId = string.find(link, "item:(%d+)")
|
|
if itemId then
|
|
if not SFramesDB then SFramesDB = {} end
|
|
SFramesDB.lastPetFoodId = tonumber(itemId)
|
|
end
|
|
end
|
|
if tex and self.foodButton then
|
|
self.foodButton.icon:SetTexture(tex)
|
|
if not SFramesDB then SFramesDB = {} end
|
|
SFramesDB.lastPetFoodIcon = tex
|
|
end
|
|
end
|
|
|
|
function SFrames.Pet:QuickFeed()
|
|
if not UnitExists("pet") then return end
|
|
local foods = self:ScanBagsForFood()
|
|
if table.getn(foods) == 0 then
|
|
DEFAULT_CHAT_FRAME:AddMessage("|cffff9900[Nanami]|r 背包中没有可喂食的食物")
|
|
return
|
|
end
|
|
local preferred = SFramesDB and SFramesDB.lastPetFoodId
|
|
if preferred then
|
|
for i = 1, table.getn(foods) do
|
|
local f = foods[i]
|
|
if f.link then
|
|
local _, _, itemId = string.find(f.link, "item:(%d+)")
|
|
if itemId and tonumber(itemId) == preferred then
|
|
self:FeedPet(f.bag, f.slot)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
self:FeedPet(foods[1].bag, foods[1].slot)
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Food Button & Panel UI
|
|
--------------------------------------------------------------------------------
|
|
|
|
local FOOD_COLS = 6
|
|
local FOOD_SLOT_SIZE = 30
|
|
local FOOD_SLOT_GAP = 2
|
|
local FOOD_PAD = 8
|
|
|
|
function SFrames.Pet:CreateFoodButton()
|
|
local f = self.frame
|
|
local A = SFrames.ActiveTheme
|
|
|
|
local btn = CreateFrame("Button", "SFramesPetFoodBtn", f)
|
|
btn:SetWidth(20)
|
|
btn:SetHeight(20)
|
|
btn:SetPoint("TOP", f.happinessBG, "BOTTOM", 0, -2)
|
|
SFrames:CreateUnitBackdrop(btn)
|
|
|
|
local icon = btn:CreateTexture(nil, "ARTWORK")
|
|
icon:SetPoint("TOPLEFT", btn, "TOPLEFT", 1, -1)
|
|
icon:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -1, 1)
|
|
icon:SetTexture("Interface\\Icons\\INV_Misc_Food_14")
|
|
btn.icon = icon
|
|
|
|
btn:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
|
|
|
local pet = self
|
|
btn:SetScript("OnClick", function()
|
|
if CursorHasItem() then
|
|
local curTex = pet:GetCursorItemTexture()
|
|
DropItemOnUnit("pet")
|
|
if curTex then
|
|
pet:SetFoodIcon(curTex)
|
|
end
|
|
return
|
|
end
|
|
if arg1 == "RightButton" then
|
|
pet:QuickFeed()
|
|
else
|
|
if pet.foodPanel and pet.foodPanel:IsShown() then
|
|
pet.foodPanel:Hide()
|
|
else
|
|
pet:ShowFoodPanel()
|
|
end
|
|
end
|
|
end)
|
|
|
|
btn:SetScript("OnEnter", function()
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:AddLine("喂养宠物", 1, 1, 1)
|
|
GameTooltip:AddLine("左键: 选择食物", 0.7, 0.7, 0.7)
|
|
GameTooltip:AddLine("右键: 快速喂食", 0.7, 0.7, 0.7)
|
|
GameTooltip:AddLine("可拖拽背包食物到此按钮", 0.7, 0.7, 0.7)
|
|
GameTooltip:Show()
|
|
end)
|
|
|
|
btn:SetScript("OnLeave", function()
|
|
GameTooltip:Hide()
|
|
end)
|
|
|
|
btn:SetScript("OnReceiveDrag", function()
|
|
if CursorHasItem() then
|
|
DropItemOnUnit("pet")
|
|
end
|
|
end)
|
|
|
|
self.foodButton = btn
|
|
btn:Hide()
|
|
end
|
|
|
|
function SFrames.Pet:CreateFoodPanel()
|
|
if self.foodPanel then return self.foodPanel end
|
|
|
|
local A = SFrames.ActiveTheme
|
|
|
|
local panel = CreateFrame("Frame", "SFramesPetFoodPanel", UIParent)
|
|
panel:SetFrameStrata("DIALOG")
|
|
panel:SetFrameLevel(20)
|
|
panel:SetWidth(FOOD_COLS * (FOOD_SLOT_SIZE + FOOD_SLOT_GAP) + FOOD_PAD * 2 - FOOD_SLOT_GAP)
|
|
panel:SetHeight(80)
|
|
panel:SetPoint("BOTTOMLEFT", self.frame, "TOPLEFT", -22, 4)
|
|
SFrames:CreateUnitBackdrop(panel)
|
|
panel:EnableMouse(true)
|
|
panel:Hide()
|
|
|
|
local titleBar = CreateFrame("Frame", nil, panel)
|
|
titleBar:SetPoint("TOPLEFT", panel, "TOPLEFT", 1, -1)
|
|
titleBar:SetPoint("TOPRIGHT", panel, "TOPRIGHT", -1, -1)
|
|
titleBar:SetHeight(18)
|
|
titleBar:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8" })
|
|
local hdr = A.headerBg or A.panelBg
|
|
titleBar:SetBackdropColor(hdr[1], hdr[2], hdr[3], (hdr[4] or 0.9) * 0.6)
|
|
|
|
local titleText = titleBar:CreateFontString(nil, "OVERLAY")
|
|
titleText:SetFont(SFrames:GetFont(), 10, SFrames.Media.fontOutline or "OUTLINE")
|
|
titleText:SetPoint("LEFT", titleBar, "LEFT", FOOD_PAD, 0)
|
|
titleText:SetTextColor(A.title[1], A.title[2], A.title[3])
|
|
titleText:SetText("选择食物")
|
|
panel.titleText = titleText
|
|
|
|
local hintText = panel:CreateFontString(nil, "OVERLAY")
|
|
hintText:SetFont(SFrames:GetFont(), 9, SFrames.Media.fontOutline or "OUTLINE")
|
|
hintText:SetPoint("BOTTOMLEFT", panel, "BOTTOMLEFT", FOOD_PAD, 4)
|
|
hintText:SetPoint("BOTTOMRIGHT", panel, "BOTTOMRIGHT", -FOOD_PAD, 4)
|
|
hintText:SetJustifyH("LEFT")
|
|
local dim = A.dimText or { 0.5, 0.5, 0.5 }
|
|
hintText:SetTextColor(dim[1], dim[2], dim[3])
|
|
hintText:SetText("点击喂食 | 可拖拽食物到此面板")
|
|
panel.hintText = hintText
|
|
|
|
local emptyText = panel:CreateFontString(nil, "OVERLAY")
|
|
emptyText:SetFont(SFrames:GetFont(), 10, SFrames.Media.fontOutline or "OUTLINE")
|
|
emptyText:SetPoint("CENTER", panel, "CENTER", 0, 0)
|
|
emptyText:SetTextColor(dim[1], dim[2], dim[3])
|
|
emptyText:SetText("背包中没有可喂食的食物")
|
|
emptyText:Hide()
|
|
panel.emptyText = emptyText
|
|
|
|
local pet = self
|
|
panel:SetScript("OnReceiveDrag", function()
|
|
if CursorHasItem() then
|
|
local curTex = pet:GetCursorItemTexture()
|
|
DropItemOnUnit("pet")
|
|
if curTex then
|
|
pet:SetFoodIcon(curTex)
|
|
end
|
|
end
|
|
end)
|
|
|
|
table.insert(UISpecialFrames, "SFramesPetFoodPanel")
|
|
|
|
panel.slots = {}
|
|
self.foodPanel = panel
|
|
return panel
|
|
end
|
|
|
|
function SFrames.Pet:CreateFoodSlot(parent, index)
|
|
local A = SFrames.ActiveTheme
|
|
|
|
local slot = CreateFrame("Button", "SFramesPetFoodSlot" .. index, parent)
|
|
slot:SetWidth(FOOD_SLOT_SIZE)
|
|
slot:SetHeight(FOOD_SLOT_SIZE)
|
|
slot:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, tileSize = 0, edgeSize = 1,
|
|
insets = { left = 1, right = 1, top = 1, bottom = 1 }
|
|
})
|
|
slot:SetBackdropColor(A.slotBg[1], A.slotBg[2], A.slotBg[3], A.slotBg[4] or 0.9)
|
|
slot:SetBackdropBorderColor(0, 0, 0, 1)
|
|
|
|
local icon = slot:CreateTexture(nil, "ARTWORK")
|
|
icon:SetPoint("TOPLEFT", 2, -2)
|
|
icon:SetPoint("BOTTOMRIGHT", -2, 2)
|
|
slot.icon = icon
|
|
|
|
local count = slot:CreateFontString(nil, "OVERLAY")
|
|
count:SetFont(SFrames:GetFont(), 10, "OUTLINE")
|
|
count:SetPoint("BOTTOMRIGHT", -2, 2)
|
|
count:SetJustifyH("RIGHT")
|
|
count:SetTextColor(1, 1, 1)
|
|
slot.count = count
|
|
|
|
slot:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
|
|
|
local pet = self
|
|
slot:SetScript("OnClick", function()
|
|
if CursorHasItem() then
|
|
local curTex = pet:GetCursorItemTexture()
|
|
DropItemOnUnit("pet")
|
|
if curTex then
|
|
pet:SetFoodIcon(curTex)
|
|
end
|
|
return
|
|
end
|
|
if IsShiftKeyDown() and this.foodLink then
|
|
if ChatFrameEditBox and ChatFrameEditBox:IsVisible() then
|
|
ChatFrameEditBox:Insert(this.foodLink)
|
|
end
|
|
return
|
|
end
|
|
if this.foodBag and this.foodSlot then
|
|
pet:FeedPet(this.foodBag, this.foodSlot)
|
|
if pet.foodPanel then pet.foodPanel:Hide() end
|
|
end
|
|
end)
|
|
|
|
slot:SetScript("OnEnter", function()
|
|
if this.foodBag and this.foodSlot then
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:SetBagItem(this.foodBag, this.foodSlot)
|
|
GameTooltip:AddLine(" ")
|
|
GameTooltip:AddLine("点击: 喂食宠物", 0.5, 1, 0.5)
|
|
GameTooltip:AddLine("Shift+点击: 链接到聊天", 0.7, 0.7, 0.7)
|
|
GameTooltip:Show()
|
|
end
|
|
this:SetBackdropBorderColor(0.4, 0.4, 0.4, 1)
|
|
end)
|
|
|
|
slot:SetScript("OnLeave", function()
|
|
GameTooltip:Hide()
|
|
this:SetBackdropBorderColor(0, 0, 0, 1)
|
|
end)
|
|
|
|
slot:SetScript("OnReceiveDrag", function()
|
|
if CursorHasItem() then
|
|
DropItemOnUnit("pet")
|
|
end
|
|
end)
|
|
|
|
return slot
|
|
end
|
|
|
|
function SFrames.Pet:ShowFoodPanel()
|
|
self:CreateFoodPanel()
|
|
self:RefreshFoodPanel()
|
|
self.foodPanel:Show()
|
|
end
|
|
|
|
function SFrames.Pet:RefreshFoodPanel()
|
|
local panel = self.foodPanel
|
|
if not panel then return end
|
|
|
|
local foods = self:ScanBagsForFood()
|
|
local numFoods = table.getn(foods)
|
|
|
|
for i = 1, table.getn(panel.slots) do
|
|
panel.slots[i]:Hide()
|
|
end
|
|
|
|
if numFoods == 0 then
|
|
panel:SetHeight(60)
|
|
panel.emptyText:Show()
|
|
panel.hintText:Hide()
|
|
return
|
|
end
|
|
|
|
panel.emptyText:Hide()
|
|
panel.hintText:Show()
|
|
|
|
local rows = math.ceil(numFoods / FOOD_COLS)
|
|
local panelH = FOOD_PAD + 20 + rows * (FOOD_SLOT_SIZE + FOOD_SLOT_GAP) + 18
|
|
panel:SetHeight(panelH)
|
|
|
|
for i = 1, numFoods do
|
|
local food = foods[i]
|
|
local slot = panel.slots[i]
|
|
if not slot then
|
|
slot = self:CreateFoodSlot(panel, i)
|
|
panel.slots[i] = slot
|
|
end
|
|
|
|
local col = mod(i - 1, FOOD_COLS)
|
|
local row = math.floor((i - 1) / FOOD_COLS)
|
|
|
|
slot:ClearAllPoints()
|
|
slot:SetPoint("TOPLEFT", panel, "TOPLEFT",
|
|
FOOD_PAD + col * (FOOD_SLOT_SIZE + FOOD_SLOT_GAP),
|
|
-(FOOD_PAD + 18 + row * (FOOD_SLOT_SIZE + FOOD_SLOT_GAP)))
|
|
|
|
slot.icon:SetTexture(food.texture)
|
|
slot.count:SetText(food.count > 1 and tostring(food.count) or "")
|
|
slot.foodBag = food.bag
|
|
slot.foodSlot = food.slot
|
|
slot.foodName = food.name
|
|
slot.foodLink = food.link
|
|
slot:Show()
|
|
end
|
|
end
|
|
|
|
function SFrames.Pet:GetCursorItemTexture()
|
|
for bag = 0, 4 do
|
|
for slot = 1, GetContainerNumSlots(bag) do
|
|
local texture, count, locked = GetContainerItemInfo(bag, slot)
|
|
if locked and texture then
|
|
return texture
|
|
end
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function SFrames.Pet:SetFoodIcon(tex)
|
|
if not tex or not self.foodButton then return end
|
|
self.foodButton.icon:SetTexture(tex)
|
|
if not SFramesDB then SFramesDB = {} end
|
|
SFramesDB.lastPetFoodIcon = tex
|
|
end
|
|
|
|
function SFrames.Pet:InitFoodFeature()
|
|
local _, playerClass = UnitClass("player")
|
|
if playerClass ~= "HUNTER" then return end
|
|
|
|
self:CreateFoodButton()
|
|
|
|
if SFramesDB and SFramesDB.lastPetFoodIcon then
|
|
self.foodButton.icon:SetTexture(SFramesDB.lastPetFoodIcon)
|
|
end
|
|
|
|
local pet = self
|
|
SFrames:RegisterEvent("BAG_UPDATE", function()
|
|
if pet.foodPanel and pet.foodPanel:IsShown() then
|
|
pet:RefreshFoodPanel()
|
|
end
|
|
end)
|
|
end
|
|
|
|
function SFrames.Pet:UpdateFoodButton()
|
|
if not self.foodButton then return end
|
|
|
|
local _, class = UnitClass("player")
|
|
if class == "HUNTER" and UnitExists("pet") then
|
|
self.foodButton:Show()
|
|
local happiness = GetPetHappiness()
|
|
if happiness and happiness == 1 then
|
|
self.foodButton.icon:SetVertexColor(1, 0.3, 0.3)
|
|
elseif happiness and happiness == 2 then
|
|
self.foodButton.icon:SetVertexColor(1, 0.8, 0.4)
|
|
else
|
|
self.foodButton.icon:SetVertexColor(1, 1, 1)
|
|
end
|
|
else
|
|
self.foodButton:Hide()
|
|
if self.foodPanel then self.foodPanel:Hide() end
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Pet Castbar
|
|
--------------------------------------------------------------------------------
|
|
|
|
function SFrames.Pet:CreateCastbar()
|
|
local f = self.frame
|
|
local cbHeight = SFrames.Config.castbarHeight
|
|
|
|
local cb = SFrames:CreateStatusBar(f, "SFramesPetCastbar")
|
|
cb:SetHeight(cbHeight)
|
|
cb:SetPoint("BOTTOMLEFT", f, "TOPLEFT", 0, 4)
|
|
cb:SetPoint("BOTTOMRIGHT", f, "TOPRIGHT", -(cbHeight + 6), 4)
|
|
|
|
local cbbg = CreateFrame("Frame", nil, f)
|
|
cbbg:SetPoint("TOPLEFT", cb, "TOPLEFT", -1, 1)
|
|
cbbg:SetPoint("BOTTOMRIGHT", cb, "BOTTOMRIGHT", 1, -1)
|
|
cbbg:SetFrameLevel(cb:GetFrameLevel() - 1)
|
|
SFrames:CreateUnitBackdrop(cbbg)
|
|
|
|
cb.bg = cb:CreateTexture(nil, "BACKGROUND")
|
|
cb.bg:SetAllPoints()
|
|
cb.bg:SetTexture(SFrames:GetTexture())
|
|
cb.bg:SetVertexColor(_A.slotBg[1], _A.slotBg[2], _A.slotBg[3], _A.slotBg[4] or 1)
|
|
cb:SetStatusBarColor(1, 0.7, 0)
|
|
|
|
cb.text = SFrames:CreateFontString(cb, 10, "LEFT")
|
|
cb.text:SetPoint("LEFT", cb, "LEFT", 4, 0)
|
|
|
|
cb.time = SFrames:CreateFontString(cb, 10, "RIGHT")
|
|
cb.time:SetPoint("RIGHT", cb, "RIGHT", -4, 0)
|
|
|
|
cb.icon = cb:CreateTexture(nil, "ARTWORK")
|
|
cb.icon:SetWidth(cbHeight + 2)
|
|
cb.icon:SetHeight(cbHeight + 2)
|
|
cb.icon:SetPoint("LEFT", cb, "RIGHT", 4, 0)
|
|
cb.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93)
|
|
|
|
local ibg = CreateFrame("Frame", nil, f)
|
|
ibg:SetPoint("TOPLEFT", cb.icon, "TOPLEFT", -1, 1)
|
|
ibg:SetPoint("BOTTOMRIGHT", cb.icon, "BOTTOMRIGHT", 1, -1)
|
|
ibg:SetFrameLevel(cb:GetFrameLevel() - 1)
|
|
SFrames:CreateUnitBackdrop(ibg)
|
|
|
|
cb:Hide()
|
|
cbbg:Hide()
|
|
cb.icon:Hide()
|
|
ibg:Hide()
|
|
|
|
f.castbar = cb
|
|
f.castbar.cbbg = cbbg
|
|
f.castbar.ibg = ibg
|
|
|
|
f.castbarUpdater = CreateFrame("Frame", nil, f)
|
|
f.castbarUpdater:SetScript("OnUpdate", function() SFrames.Pet:CastbarOnUpdate() end)
|
|
end
|
|
|
|
function SFrames.Pet:CastbarOnUpdate()
|
|
local cb = self.frame.castbar
|
|
if not UnitExists("pet") then
|
|
cb:Hide()
|
|
cb.cbbg:Hide()
|
|
cb.icon:Hide()
|
|
cb.ibg:Hide()
|
|
return
|
|
end
|
|
|
|
local cast, texture, startTime, endTime, channel
|
|
|
|
-- 1) Try native UnitCastingInfo / UnitChannelInfo (TurtleWoW extended API)
|
|
local _UnitCastingInfo = UnitCastingInfo or (ShaguTweaks and ShaguTweaks.UnitCastingInfo)
|
|
if _UnitCastingInfo then
|
|
local c, _, _, tex, st, et = _UnitCastingInfo("pet")
|
|
if c then
|
|
cast, texture, startTime, endTime = c, tex, st, et
|
|
end
|
|
end
|
|
|
|
if not cast then
|
|
local _UnitChannelInfo = UnitChannelInfo or (ShaguTweaks and ShaguTweaks.UnitChannelInfo)
|
|
if _UnitChannelInfo then
|
|
local c, _, _, tex, st, et = _UnitChannelInfo("pet")
|
|
if c then
|
|
cast, texture, startTime, endTime = c, tex, st, et
|
|
channel = true
|
|
end
|
|
end
|
|
end
|
|
|
|
-- 2) Fallback: SuperWoW castdb (GUID-based)
|
|
if not cast and SFrames.castdb and UnitGUID then
|
|
local guid = UnitGUID("pet")
|
|
if guid then
|
|
local entry = SFrames.castdb[guid]
|
|
if entry and entry.cast and entry.start and entry.casttime then
|
|
local elapsed = GetTime() - entry.start
|
|
local duration = entry.casttime / 1000
|
|
if elapsed < duration + 0.5 then
|
|
cast = entry.cast
|
|
texture = entry.icon
|
|
startTime = entry.start * 1000
|
|
endTime = (entry.start + duration) * 1000
|
|
channel = entry.channel
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if cast and startTime and endTime then
|
|
local duration = (endTime - startTime) / 1000
|
|
local cur = GetTime() - (startTime / 1000)
|
|
|
|
if channel then
|
|
cur = duration + (startTime / 1000) - GetTime()
|
|
end
|
|
|
|
if cur > duration then cur = duration end
|
|
if cur < 0 then cur = 0 end
|
|
|
|
cb:SetMinMaxValues(0, duration)
|
|
cb:SetValue(cur)
|
|
cb.text:SetText(cast)
|
|
cb.time:SetText(string.format("%.1f", channel and cur or math.max(duration - cur, 0)))
|
|
|
|
if texture then
|
|
cb.icon:SetTexture(texture)
|
|
end
|
|
|
|
cb:SetAlpha(1)
|
|
cb.cbbg:SetAlpha(1)
|
|
cb.icon:SetAlpha(1)
|
|
cb.ibg:SetAlpha(1)
|
|
|
|
cb:Show()
|
|
cb.cbbg:Show()
|
|
cb.icon:Show()
|
|
cb.ibg:Show()
|
|
else
|
|
if cb:IsShown() then
|
|
local alpha = cb:GetAlpha() - 0.05
|
|
if alpha > 0 then
|
|
cb:SetAlpha(alpha)
|
|
cb.cbbg:SetAlpha(alpha)
|
|
cb.icon:SetAlpha(alpha)
|
|
cb.ibg:SetAlpha(alpha)
|
|
else
|
|
cb:Hide()
|
|
cb.cbbg:Hide()
|
|
cb.icon:Hide()
|
|
cb.ibg:Hide()
|
|
end
|
|
end
|
|
end
|
|
end
|