You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

405 lines
15 KiB

--[[ MIT License
Copyright (c) 2021 Christophe MICHEL
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
local ADDON_NAME, SJ = ...
local L = LibStub("AceLocale-3.0"):GetLocale(ADDON_NAME)
local NIGHT_FAE = Enum.CovenantType["NightFae"]
local CollectionPanelMixin = {
selectedSoulshape = nil,
onSoulshapeChangeCallbacks = {},
}
local function CreateInsets(panel)
local leftInset = CreateFrame("Frame", "$parentLeftInset", panel, "InsetFrameTemplate")
leftInset:SetSize(260, 496)
leftInset:SetPoint("TOPLEFT", 4, -60)
leftInset:SetPoint("BOTTOMLEFT", 4, 5)
local rightInset = CreateFrame("Frame", "$parentRightInset", panel, "InsetFrameTemplate")
rightInset:SetPoint("TOPLEFT", leftInset, "TOPRIGHT", 23, 0)
rightInset:SetPoint("BOTTOMRIGHT", -6, 5)
panel.LeftInset = leftInset
panel.RightInset = rightInset
end
local function CreateCount(panel)
local countFrame = CreateFrame("Frame", "$parentCount", panel, "InsetFrameTemplate3")
countFrame:SetSize(130, 20)
countFrame:SetPoint("TOPLEFT", 70, -35)
local countNumber = countFrame:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
countNumber:SetText("0")
countNumber:SetJustifyH("RIGHT")
countNumber:SetPoint("RIGHT", -10, 0)
local countLabel = countFrame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
countLabel:SetText(L["COUNT_LABEL"])
countLabel:SetJustifyH("LEFT")
countLabel:SetPoint("LEFT", 10, 0)
countLabel:SetPoint("RIGHT", countNumber, "LEFT", -3, 0)
panel.Count = countNumber
end
local function CreateCovenantWarning(panel)
local warningFrame = CreateFrame("Frame", nil, panel)
-- This positioning is super ugly, but it does the job, don't do this at home
warningFrame:SetPoint("TOPRIGHT", panel, "TOPRIGHT", -10, -28)
warningFrame:SetSize(395, 30)
local warningLabel = warningFrame:CreateFontString(nil, "ARTWORK", "GameFontNormal")
warningLabel:SetText(L["WARNING_NOT_NIGHT_FAE"])
warningLabel:SetJustifyH("LEFT")
warningLabel:SetAllPoints()
warningLabel:Hide()
panel.CovenantWarning = warningLabel
end
local function CreateScrollFrame(panel)
local ScrollFrameMixin = {}
function ScrollFrameMixin:ResetButton(button)
button.name:SetText("")
button.icon:SetTexture(GetSpellTexture(310143))
button.critterIcon:Hide()
button.untrackableAddButton:Hide()
button.selectedTexture:Hide()
button.selected = false
button:SetEnabled(false)
button.icon:SetAlpha(0.25)
button.icon:SetDesaturated(true)
button.name:SetFontObject("GameFontDisable")
end
function ScrollFrameMixin:CreateButtons()
HybridScrollFrame_CreateButtons(self, "SoulshapeListButtonTemplate", 44, 0)
end
function ScrollFrameMixin:UpdateButtons()
local buttons = HybridScrollFrame_GetButtons(self)
local offset = HybridScrollFrame_GetOffset(self)
local buttonHeight;
local filteredItems = SJ.Filters:Filter(SJ.Database.soulshapes)
for index = 1, #buttons do
local button = buttons[index]
local itemIndex = index + offset
buttonHeight = button:GetHeight()
if itemIndex <= #filteredItems then
local item = filteredItems[itemIndex]
button.name:SetText(item.name)
button.icon:SetTexture(item.icon)
button.soulshape = item
if item.collected then
button.icon:SetAlpha(1)
button.icon:SetDesaturated(false)
button.name:SetFontObject("GameFontNormal")
button.background:SetVertexColor(1, 1, 1, 1);
else
button.icon:SetAlpha(0.25)
button.icon:SetDesaturated(true)
button.name:SetFontObject("GameFontDisable")
end
button.critterIcon:SetShown(item.critter)
button.untrackableAddButton:SetShown(not item.collected and item.untrackable)
button.selected = panel.selectedSoulshape == item
button.selectedTexture:SetShown(button.selected)
button:SetEnabled(true)
else
self:ResetButton(button)
end
end
HybridScrollFrame_Update(self, #filteredItems * buttonHeight, self:GetHeight())
end
local scrollFrame = Mixin(CreateFrame("ScrollFrame", "$parentScrollFrame", panel, "HybridScrollFrameTemplate"), ScrollFrameMixin)
scrollFrame:SetPoint("TOPLEFT", panel.LeftInset, "TOPLEFT", 3, -36)
scrollFrame:SetPoint("BOTTOMRIGHT", panel.LeftInset, "BOTTOMRIGHT", -3, 5)
scrollFrame.items = SJ.Database.soulshapes
local scrollBar = CreateFrame("Slider", "$parentScrollBar", scrollFrame, "HybridScrollBarTemplate")
scrollBar:SetPoint("TOPLEFT", panel.LeftInset, "TOPRIGHT", 1, -16)
scrollBar:SetPoint("BOTTOMLEFT", panel.LeftInset, "BOTTOMRIGHT", 1, 16)
scrollBar.doNotHide = true
scrollFrame.ScrollBar = scrollBar
scrollFrame:CreateButtons()
scrollFrame.update = scrollFrame.UpdateButtons
panel.ScrollFrame = scrollFrame
end
local function CreateModelView(panel)
local soulshapeDisplay = CreateFrame("Frame", nil, panel)
soulshapeDisplay:SetPoint("TOPLEFT", panel.RightInset, "TOPLEFT", 3, -3)
soulshapeDisplay:SetPoint("BOTTOMRIGHT", panel.RightInset, "BOTTOMRIGHT", -3, 3)
local background = soulshapeDisplay:CreateTexture(nil, "BACKGROUND")
background:SetAllPoints()
background:SetTexture("Interface/CovenantChoice/CovenantChoiceOfferingsNightFae")
background:SetTexCoord(0.0434117648, 0.3608851102, 0.427734375, 0.8486328125)
local shadow = CreateFrame("Frame", nil, soulshapeDisplay, "ShadowOverlayTemplate")
shadow:Lower()
shadow:SetAllPoints()
local soulshapeInfo = CreateFrame("Frame", nil, soulshapeDisplay)
soulshapeInfo:SetPoint("TOPLEFT", 20, -20)
soulshapeInfo:SetPoint("BOTTOMRIGHT", -20, 20)
-- FIXME: extract this
local bannerLeft = soulshapeInfo:CreateTexture(nil, "ARTWORK")
bannerLeft:SetPoint("TOPLEFT", 0, 5)
bannerLeft:SetAtlas("UI-Frame-NightFae-TitleLeft", false)
bannerLeft:SetSize(100, 42)
local bannerRight = soulshapeInfo:CreateTexture(nil, "ARTWORK")
bannerRight:SetPoint("TOPRIGHT", soulshapeInfo, "TOPRIGHT", 0, 5)
bannerRight:SetAtlas("UI-Frame-NightFae-TitleRight", false)
bannerRight:SetSize(100, 42)
local bannerMid = soulshapeInfo:CreateTexture(nil, "ARTWORK")
bannerMid:SetPoint("TOPLEFT", bannerLeft, "TOPRIGHT")
bannerMid:SetPoint("BOTTOMRIGHT", bannerRight, "BOTTOMLEFT")
bannerMid:SetAtlas("_UI-Frame-NightFae-TitleMiddle", false)
local infoName = soulshapeInfo:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge2")
infoName:SetPoint("TOPLEFT", 0, 0)
infoName:SetPoint("TOPRIGHT", 0, 0)
infoName:SetSize(0, 35)
infoName:SetJustifyH("CENTER")
panel.Name = infoName
local infoSource = soulshapeInfo:CreateFontString(nil, "OVERLAY", "GameFontHighlightMedium")
infoSource:SetJustifyH("LEFT")
infoSource:SetPoint("TOPLEFT", infoName, "BOTTOMLEFT", 20, -10)
infoSource:SetPoint("TOPRIGHT", infoName, "BOTTOMRIGHT", -20, -10)
infoSource:SetWordWrap(true)
panel.Source = infoSource
local infoGuide = soulshapeInfo:CreateFontString(nil, "OVERLAY", "GameFontNormal")
infoGuide:SetJustifyH("LEFT")
infoGuide:SetPoint("TOPLEFT", infoSource, "BOTTOMLEFT", 0, -5)
infoGuide:SetPoint("TOPRIGHT", infoSource, "BOTTOMRIGHT", 0, -5)
infoGuide:SetWordWrap(true)
panel.Guide = infoGuide
local modelScene = CreateFrame("ModelScene", nil, soulshapeDisplay, "WrappedAndUnwrappedModelScene")
modelScene:Lower()
modelScene:SetPoint("TOPLEFT", 0, -80)
modelScene:SetPoint("BOTTOMRIGHT", 0, 40)
modelScene.RotateLeftButton = CreateFrame("Button", nil, modelScene, "RotateOrbitCameraLeftButtonTemplate")
modelScene.RotateLeftButton:SetPoint("TOPRIGHT", modelScene, "BOTTOM", -5, 20)
modelScene.RotateRightButton = CreateFrame("Button", nil, modelScene, "RotateOrbitCameraRightButtonTemplate")
modelScene.RotateRightButton:SetPoint("TOPLEFT", modelScene, "BOTTOM", 5, 20)
modelScene:Hide()
soulshapeDisplay.ModelScene = modelScene
panel.SoulshapeDisplay = soulshapeDisplay
end
local function CreateSearchBox(panel)
local editBox = CreateFrame("EditBox", nil, panel, "SearchBoxTemplate")
editBox:SetSize(145, 20)
editBox:SetPoint("TOPLEFT", panel.LeftInset, 15, -9)
editBox.letters = 40
editBox:SetScript("OnTextChanged", function(self)
SearchBoxTemplate_OnTextChanged(self)
SJ.Filters:SetTextFilter(self:GetText())
panel.ScrollFrame:UpdateButtons()
end)
editBox:SetScript("OnHide", function(self)
self:SetText("")
end)
end
local function CreateTab(panel)
local tab = LibStub('SecureTabs-2.0'):Add(CollectionsJournal)
tab:SetText(L["TAB_TITLE"])
tab.frame = panel
tab.OnSelect = function()
-- Some addons aren't aware that we exist and won't hide themselves correctly when
-- we show up. We'll circumvent this by telling the UI we're selecting another tab
-- from the CollectionsJournal immediately before switching to ours, which causes
-- those addons to hide themselves gracefully.
-- The chosen tab is the Heirlooms Journal because we don't expect any popular
-- addon to modify its frame. If it's the case, well, we're screwed.
-- If you read this and you have a better technique to attach tabs to the
-- collection journal, please message me.
CollectionsJournal_SetTab(CollectionsJournal, CollectionsJournalTab4:GetID())
end
panel.Tab = tab
end
function CollectionPanelMixin:UpdateSoulshapeDisplay()
local scene = self.SoulshapeDisplay.ModelScene
local function showModel(creatureDisplayID, scale, modelSceneID, animation)
scene:SetFromModelSceneID(modelSceneID, true, false)
local actor = scene:GetActorByTag("unwrapped")
if actor then
actor:SetModelByCreatureDisplayID(creatureDisplayID)
if scale then
actor:SetRequestedScale(scale)
end
if animation then
actor:SetAnimation(animation, 0)
end
end
scene:Show()
end
local function enableUserControls(enabled)
scene.RotateLeftButton:SetShown(enabled)
scene.RotateRightButton:SetShown(enabled)
scene:EnableMouse(enabled)
scene:EnableMouseWheel(enabled)
end
local soulshape = self.selectedSoulshape
if soulshape then
enableUserControls(true)
self.Name:SetText(soulshape.name)
self.Source:SetText(soulshape.source)
self.Guide:SetText(soulshape.guide)
showModel(soulshape.model, soulshape.scale, soulshape.modelSceneID or 44)
else
-- Default display
enableUserControls(false)
self.Name:SetText(L["Soulshape Journal"])
-- Running Vulpine!
showModel(93949, 4, 4, 5)
end
end
function CollectionPanelMixin:ShowUntrackableTooltip(addButton)
local soulshape = addButton:GetParent().soulshape
GameTooltip:SetOwner(addButton, "ANCHOR_RIGHT")
GameTooltip:AddLine(string.format("|A:services-icon-warning:16:16|a |cFFFFFFFF%s|r", L["UNTRACKABLE_TOOLTIP_TITLE"]))
GameTooltip:AddLine(string.format(L["UNTRACKABLE_TOOLTIP_CLICK_ME"], NIGHT_FAE_BLUE_COLOR:WrapTextInColorCode(soulshape.name)))
GameTooltip:Show()
end
function CollectionPanelMixin:UpdateCount()
self.Count:SetText(SJ.Database:CountCollected() .. "/" .. SJ.Database:CountTotal())
end
function CollectionPanelMixin:UpdateCovenantWarning()
local covenant = C_Covenants.GetActiveCovenantID()
self.CovenantWarning:SetShown(covenant ~= NIGHT_FAE)
end
function CollectionPanelMixin:OnButtonClick(button)
self.selectedSoulshape = button.soulshape
self:UpdateSoulshapeDisplay()
-- FIXME: move this to callbacks
self.ScrollFrame:UpdateButtons()
-- callbacks for other components
self:SoulshapeChanged(button.soulshape)
end
--- Callback signature: callback(soulshape)
function CollectionPanelMixin:OnSoulshapeChange(callback)
tinsert(self.onSoulshapeChangeCallbacks, callback)
end
function CollectionPanelMixin:SoulshapeChanged(soulshape)
--SJ:Print("Panel:SoulshapeChanged")
for _, callback in ipairs(self.onSoulshapeChangeCallbacks) do
callback(soulshape)
end
end
--- FIXME Remove this
function CollectionPanelMixin:GetSelectedSoulshape()
return self.selectedSoulshape
end
function CollectionPanelMixin:AddUntrackableToCollection(addButton)
local soulshape = addButton:GetParent().soulshape
if SJ.Database:AddUntrackable(soulshape) then
self:Update()
end
end
-- Called when journal is shown
function CollectionPanelMixin:Update()
SJ.Database:Update()
self.ScrollFrame:UpdateButtons()
self:UpdateSoulshapeDisplay()
self:UpdateCount()
self:UpdateCovenantWarning()
end
function SJ:CreateCollectionPanel()
local panel = Mixin(CreateFrame("Frame", "SoulshapeCollectionPanel", CollectionsJournal, "PortraitFrameTemplate"), CollectionPanelMixin)
panel:Hide()
panel:SetAllPoints()
panel:SetPortraitToAsset(GetSpellTexture(310143))
panel:SetTitle(L["TAB_TITLE"])
SJ.Panel = panel
CreateInsets(panel)
CreateCount(panel)
CreateCovenantWarning(panel)
CreateScrollFrame(panel)
CreateModelView(panel)
CreateSearchBox(panel)
SJ.UIFactory:CreateFilterDropDown(panel)
-- Map
panel.Map = SJ.UIFactory:CreateMap(panel, panel.RightInset)
panel:OnSoulshapeChange(function(soulshape)
panel.Map:OnSoulshapeChange(soulshape)
end)
CreateTab(panel)
panel:SetScript("OnShow", function(self)
self:Update()
end)
end
function SJ:GetSelectedSoulshape()
return self.Panel:GetSelectedSoulshape()
end