MAX_SPELLS = 1024; MAX_SKILLLINE_TABS = 8; SPELLS_PER_PAGE = 12; MAX_SPELL_PAGES = ceil(MAX_SPELLS / SPELLS_PER_PAGE); BOOKTYPE_SPELL = "spell"; BOOKTYPE_PET = "pet"; SPELLBOOK_PAGENUMBERS = {}; local ceil = ceil; local strlen = strlen; local tinsert = tinsert; local tremove = tremove; function ToggleSpellBook(bookType) if ( not HasPetSpells() and bookType == BOOKTYPE_PET ) then return; end local isShown = SpellBookFrame:IsShown(); if ( isShown and (SpellBookFrame.bookType ~= bookType) ) then SpellBookFrame.suppressCloseSound = true; end HideUIPanel(SpellBookFrame); if ( (not isShown or (SpellBookFrame.bookType ~= bookType)) ) then SpellBookFrame.bookType = bookType; ShowUIPanel(SpellBookFrame); end SpellBookFrame_UpdatePages(); SpellBookFrame.suppressCloseSound = nil; end function SpellBookFrame_OnLoad(self) self:RegisterEvent("SPELLS_CHANGED"); self:RegisterEvent("LEARNED_SPELL_IN_TAB"); SpellBookFrame.bookType = BOOKTYPE_SPELL; -- Init page nums SPELLBOOK_PAGENUMBERS[1] = 1; SPELLBOOK_PAGENUMBERS[2] = 1; SPELLBOOK_PAGENUMBERS[3] = 1; SPELLBOOK_PAGENUMBERS[4] = 1; SPELLBOOK_PAGENUMBERS[5] = 1; SPELLBOOK_PAGENUMBERS[6] = 1; SPELLBOOK_PAGENUMBERS[7] = 1; SPELLBOOK_PAGENUMBERS[8] = 1; SPELLBOOK_PAGENUMBERS[BOOKTYPE_PET] = 1; -- Set to the first tab by default SpellBookSkillLineTab_OnClick(nil, 1); -- Initialize tab flashing SpellBookFrame.flashTabs = nil; end function SpellBookFrame_OnEvent(self, event, ...) if ( event == "SPELLS_CHANGED" ) then if ( SpellBookFrame:IsVisible() ) then SpellBookFrame_Update(); end elseif ( event == "LEARNED_SPELL_IN_TAB" ) then local arg1 = ...; local flashFrame = _G["SpellBookSkillLineTab"..arg1.."Flash"]; if ( SpellBookFrame.bookType == BOOKTYPE_PET ) then return; else if ( flashFrame ) then flashFrame:Show(); SpellBookFrame.flashTabs = 1; end end end end function SpellBookFrame_OnShow(self) SpellBookFrame_Update(1); -- If there are tabs waiting to flash, then flash them... yeah.. if ( self.flashTabs ) then UIFrameFlash(SpellBookTabFlashFrame, 0.5, 0.5, 30, nil); end -- Show multibar slots MultiActionBar_ShowAllGrids(); UpdateMicroButtons(); SpellBookFrame_PlayOpenSound(); end function SpellBookFrame_Update(showing) -- Hide all tabs SpellBookFrameTabButton1:Hide(); SpellBookFrameTabButton2:Hide(); SpellBookFrameTabButton3:Hide(); -- Setup skillline tabs if ( showing ) then SpellBookSkillLineTab_OnClick(nil, SpellBookFrame.selectedSkillLine); UpdateSpells(); end local numSkillLineTabs = GetNumSpellTabs(); local name, texture, offset, numSpells; local skillLineTab; for i=1, MAX_SKILLLINE_TABS do skillLineTab = _G["SpellBookSkillLineTab"..i]; if ( i <= numSkillLineTabs and SpellBookFrame.bookType == BOOKTYPE_SPELL ) then name, texture = GetSpellTabInfo(i); skillLineTab:SetNormalTexture(texture); skillLineTab.tooltip = name; skillLineTab:Show(); -- Set the selected tab if ( SpellBookFrame.selectedSkillLine == i ) then skillLineTab:SetChecked(1); else skillLineTab:SetChecked(nil); end else _G["SpellBookSkillLineTab"..i.."Flash"]:Hide(); skillLineTab:Hide(); end end -- Setup tabs local hasPetSpells, petToken = HasPetSpells(); SpellBookFrame.petTitle = nil; if ( hasPetSpells ) then SpellBookFrame_SetTabType(SpellBookFrameTabButton1, BOOKTYPE_SPELL); SpellBookFrame_SetTabType(SpellBookFrameTabButton2, BOOKTYPE_PET, petToken); else SpellBookFrame_SetTabType(SpellBookFrameTabButton1, BOOKTYPE_SPELL); if ( SpellBookFrame.bookType == BOOKTYPE_PET ) then -- if has no pet spells but trying to show the pet spellbook close the window; HideUIPanel(SpellBookFrame); SpellBookFrame.bookType = BOOKTYPE_SPELL; end end if ( SpellBookFrame.bookType == BOOKTYPE_SPELL ) then SpellBookTitleText:SetText(SPELLBOOK); SpellBookFrame_ShowSpells(); SpellBookFrame_UpdatePages(); elseif ( SpellBookFrame.bookType == BOOKTYPE_PET ) then SpellBookTitleText:SetText(SpellBookFrame.petTitle); SpellBookFrame_ShowSpells(); SpellBookFrame_UpdatePages(); end end function SpellBookFrame_HideSpells () for i = 1, SPELLS_PER_PAGE do _G["SpellButton" .. i]:Hide(); end for i = 1, MAX_SKILLLINE_TABS do _G["SpellBookSkillLineTab" .. i]:Hide(); end SpellBookPrevPageButton:Hide(); SpellBookNextPageButton:Hide(); SpellBookPageText:Hide(); end function SpellBookFrame_ShowSpells () for i = 1, SPELLS_PER_PAGE do _G["SpellButton" .. i]:Show(); end SpellBookPrevPageButton:Show(); SpellBookNextPageButton:Show(); SpellBookPageText:Show(); end function SpellBookFrame_UpdatePages() local currentPage, maxPages = SpellBook_GetCurrentPage(); if ( maxPages == 0 ) then return; end if ( currentPage > maxPages ) then if ( SpellBookFrame.bookType == BOOKTYPE_PET ) then SPELLBOOK_PAGENUMBERS[BOOKTYPE_PET] = maxPages; else SPELLBOOK_PAGENUMBERS[SpellBookFrame.selectedSkillLine] = maxPages; end currentPage = maxPages; UpdateSpells(); if ( currentPage == 1 ) then SpellBookPrevPageButton:Disable(); else SpellBookPrevPageButton:Enable(); end if ( currentPage == maxPages ) then SpellBookNextPageButton:Disable(); else SpellBookNextPageButton:Enable(); end end if ( currentPage == 1 ) then SpellBookPrevPageButton:Disable(); else SpellBookPrevPageButton:Enable(); end if ( currentPage == maxPages ) then SpellBookNextPageButton:Disable(); else SpellBookNextPageButton:Enable(); end SpellBookPageText:SetFormattedText(PAGE_NUMBER, currentPage); -- Hide spell rank checkbox if the player is a rogue or warrior local _, class = UnitClass("player"); local showSpellRanks = true; if ( class == "ROGUE" or class == "WARRIOR" ) then showSpellRanks = false; end if ( SpellBookFrame.bookType == BOOKTYPE_SPELL and showSpellRanks ) then ShowAllSpellRanksCheckBox:Show(); else ShowAllSpellRanksCheckBox:Hide(); end end function SpellBookFrame_SetTabType(tabButton, bookType, token) if ( bookType == BOOKTYPE_SPELL ) then tabButton.bookType = BOOKTYPE_SPELL; tabButton:SetText(SPELLBOOK); tabButton:SetFrameLevel(SpellBookFrame:GetFrameLevel() + 1); tabButton.binding = "TOGGLESPELLBOOK"; elseif ( bookType == BOOKTYPE_PET ) then tabButton.bookType = BOOKTYPE_PET; tabButton:SetText(_G["PET_TYPE_"..token]); tabButton:SetFrameLevel(SpellBookFrame:GetFrameLevel() + 1); tabButton.binding = "TOGGLEPETBOOK"; SpellBookFrame.petTitle = _G["PET_TYPE_"..token]; else tabButton.bookType = INSCRIPTION; tabButton:SetText(GLYPHS); tabButton:SetFrameLevel(SpellBookFrame:GetFrameLevel() + 2); tabButton.binding = "TOGGLEINSCRIPTION"; end if ( SpellBookFrame.bookType == bookType ) then tabButton:Disable(); else tabButton:Enable(); end tabButton:Show(); end function SpellBookFrame_PlayOpenSound() if ( SpellBookFrame.bookType == BOOKTYPE_SPELL ) then PlaySound("igSpellBookOpen"); elseif ( SpellBookFrame.bookType == BOOKTYPE_PET ) then -- Need to change to pet book open sound PlaySound("igAbilityOpen"); else PlaySound("igSpellBookOpen"); end end function SpellBookFrame_PlayCloseSound() if ( not SpellBookFrame.suppressCloseSound ) then if ( SpellBookFrame.bookType == BOOKTYPE_SPELL ) then PlaySound("igSpellBookClose"); else -- Need to change to pet book close sound PlaySound("igAbilityClose"); end end end function SpellBookFrame_OnHide(self) SpellBookFrame_PlayCloseSound(); -- Stop the flash frame from flashing if its still flashing.. flash flash flash UIFrameFlashRemoveFrame(SpellBookTabFlashFrame); -- Hide all the flashing textures for i=1, MAX_SKILLLINE_TABS do _G["SpellBookSkillLineTab"..i.."Flash"]:Hide(); end -- Hide multibar slots MultiActionBar_HideAllGrids(); -- Do this last, it can cause taint. UpdateMicroButtons(); end function SpellButton_OnLoad(self) self:RegisterForDrag("LeftButton"); self:RegisterForClicks("LeftButtonUp", "RightButtonUp"); end function SpellButton_OnEvent(self, event, ...) if ( event == "SPELLS_CHANGED" or event == "SPELL_UPDATE_COOLDOWN" or event == "UPDATE_SHAPESHIFT_FORM" ) then -- need to listen for UPDATE_SHAPESHIFT_FORM because attack icons change when the shapeshift form changes SpellButton_UpdateButton(self); elseif ( event == "CURRENT_SPELL_CAST_CHANGED" ) then SpellButton_UpdateSelection(self); elseif ( event == "TRADE_SKILL_SHOW" or event == "TRADE_SKILL_CLOSE" ) then SpellButton_UpdateSelection(self); elseif ( event == "PET_BAR_UPDATE" ) then if ( SpellBookFrame.bookType == BOOKTYPE_PET ) then SpellButton_UpdateButton(self); end end end function SpellButton_OnShow(self) self:RegisterEvent("SPELLS_CHANGED"); self:RegisterEvent("SPELL_UPDATE_COOLDOWN"); self:RegisterEvent("UPDATE_SHAPESHIFT_FORM"); self:RegisterEvent("CURRENT_SPELL_CAST_CHANGED"); self:RegisterEvent("TRADE_SKILL_SHOW"); self:RegisterEvent("TRADE_SKILL_CLOSE"); self:RegisterEvent("PET_BAR_UPDATE"); SpellButton_UpdateButton(self); end function SpellButton_OnHide(self) self:UnregisterEvent("SPELLS_CHANGED"); self:UnregisterEvent("SPELL_UPDATE_COOLDOWN"); self:UnregisterEvent("UPDATE_SHAPESHIFT_FORM"); self:UnregisterEvent("CURRENT_SPELL_CAST_CHANGED"); self:UnregisterEvent("TRADE_SKILL_SHOW"); self:UnregisterEvent("TRADE_SKILL_CLOSE"); self:UnregisterEvent("PET_BAR_UPDATE"); end function SpellButton_OnEnter(self) local id = SpellBook_GetSpellID(self:GetID()); GameTooltip:SetOwner(self, "ANCHOR_RIGHT"); if ( GameTooltip:SetSpell(id, SpellBookFrame.bookType) ) then self.UpdateTooltip = SpellButton_OnEnter; else self.UpdateTooltip = nil; end end function SpellButton_OnClick(self, button) local id = SpellBook_GetSpellID(self:GetID()); if ( id > MAX_SPELLS ) then return; end if ( button ~= "LeftButton" and SpellBookFrame.bookType == BOOKTYPE_PET ) then ToggleSpellAutocast(id, SpellBookFrame.bookType); else CastSpell(id, SpellBookFrame.bookType); SpellButton_UpdateSelection(self); end end function SpellButton_OnModifiedClick(self, button) local id = SpellBook_GetSpellID(self:GetID()); if ( id > MAX_SPELLS ) then return; end if ( IsModifiedClick("CHATLINK") ) then if ( MacroFrame and MacroFrame:IsShown() ) then local spellName, subSpellName = GetSpellName(id, SpellBookFrame.bookType); if ( spellName and not IsPassiveSpell(id, SpellBookFrame.bookType) ) then if ( subSpellName and (strlen(subSpellName) > 0) ) then ChatEdit_InsertLink(spellName.."("..subSpellName..")"); else ChatEdit_InsertLink(spellName); end end return; else local spellLink, tradeSkillLink = GetSpellLink(id, SpellBookFrame.bookType); if ( tradeSkillLink ) then ChatEdit_InsertLink(tradeSkillLink); elseif ( spellLink ) then ChatEdit_InsertLink(spellLink); end return; end end if ( IsModifiedClick("PICKUPACTION") ) then PickupSpell(id, SpellBookFrame.bookType); return; end if ( IsModifiedClick("SELFCAST") ) then CastSpell(id, SpellBookFrame.bookType, true); return; end end function SpellButton_OnDrag(self) local id = SpellBook_GetSpellID(self:GetID()); if ( id > MAX_SPELLS or not _G[self:GetName().."IconTexture"]:IsShown() ) then return; end self:SetChecked(0); PickupSpell(id, SpellBookFrame.bookType); end function SpellButton_UpdateSelection(self) local temp, texture, offset, numSpells = SpellBook_GetTabInfo(SpellBookFrame.selectedSkillLine); local id, displayID = SpellBook_GetSpellID(self:GetID()); if ( (SpellBookFrame.bookType ~= BOOKTYPE_PET) and (not displayID or displayID > (offset + numSpells)) ) then self:SetChecked("false"); return; end if ( IsSelectedSpell(id, SpellBookFrame.bookType) ) then self:SetChecked("true"); else self:SetChecked("false"); end end function SpellButton_UpdateButton(self) if ( not SpellBookFrame.selectedSkillLine ) then SpellBookFrame.selectedSkillLine = 1; end local temp, texture, offset, numSpells = SpellBook_GetTabInfo(SpellBookFrame.selectedSkillLine); SpellBookFrame.selectedSkillLineOffset = offset; local id, displayID = SpellBook_GetSpellID(self:GetID()); local name = self:GetName(); local iconTexture = _G[name.."IconTexture"]; local spellString = _G[name.."SpellName"]; local subSpellString = _G[name.."SubSpellName"]; local cooldown = _G[name.."Cooldown"]; local autoCastableTexture = _G[name.."AutoCastable"]; if ( (SpellBookFrame.bookType ~= BOOKTYPE_PET) and (not displayID or displayID > (offset + numSpells)) ) then self:Disable(); iconTexture:Hide(); spellString:Hide(); subSpellString:Hide(); cooldown:Hide(); autoCastableTexture:Hide(); SpellBook_ReleaseAutoCastShine(self.shine) self.shine = nil; self:SetChecked(0); _G[name.."NormalTexture"]:SetVertexColor(1.0, 1.0, 1.0); return; else self:Enable(); end local texture = GetSpellTexture(id, SpellBookFrame.bookType); local highlightTexture = _G[name.."Highlight"]; local normalTexture = _G[name.."NormalTexture"]; -- If no spell, hide everything and return if ( not texture or (strlen(texture) == 0) ) then iconTexture:Hide(); spellString:Hide(); subSpellString:Hide(); cooldown:Hide(); autoCastableTexture:Hide(); SpellBook_ReleaseAutoCastShine(self.shine); self.shine = nil; highlightTexture:SetTexture("Interface\\Buttons\\ButtonHilight-Square"); self:SetChecked(0); normalTexture:SetVertexColor(1.0, 1.0, 1.0); return; end local start, duration, enable = GetSpellCooldown(id, SpellBookFrame.bookType); CooldownFrame_SetTimer(cooldown, start, duration, enable); if ( enable == 1 ) then iconTexture:SetVertexColor(1.0, 1.0, 1.0); else iconTexture:SetVertexColor(0.4, 0.4, 0.4); end local autoCastAllowed, autoCastEnabled = GetSpellAutocast(id, SpellBookFrame.bookType); if ( autoCastAllowed ) then autoCastableTexture:Show(); else autoCastableTexture:Hide(); end if ( autoCastEnabled and not self.shine ) then self.shine = SpellBook_GetAutoCastShine(); self.shine:Show(); self.shine:SetParent(self); self.shine:SetPoint("CENTER", self, "CENTER"); AutoCastShine_AutoCastStart(self.shine); elseif ( autoCastEnabled ) then self.shine:Show(); self.shine:SetParent(self); self.shine:SetPoint("CENTER", self, "CENTER"); AutoCastShine_AutoCastStart(self.shine); elseif ( not autoCastEnabled ) then SpellBook_ReleaseAutoCastShine(self.shine); self.shine = nil; end local spellName, subSpellName = GetSpellName(id, SpellBookFrame.bookType); local isPassive = IsPassiveSpell(id, SpellBookFrame.bookType); if ( isPassive ) then normalTexture:SetVertexColor(0, 0, 0); highlightTexture:SetTexture("Interface\\Buttons\\UI-PassiveHighlight"); --subSpellName = PASSIVE_PARENS; spellString:SetTextColor(PASSIVE_SPELL_FONT_COLOR.r, PASSIVE_SPELL_FONT_COLOR.g, PASSIVE_SPELL_FONT_COLOR.b); else normalTexture:SetVertexColor(1.0, 1.0, 1.0); highlightTexture:SetTexture("Interface\\Buttons\\ButtonHilight-Square"); spellString:SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b); end iconTexture:SetTexture(texture); spellString:SetText(spellName); subSpellString:SetText(subSpellName); if ( subSpellName ~= "" ) then spellString:SetPoint("LEFT", self, "RIGHT", 4, 4); else spellString:SetPoint("LEFT", self, "RIGHT", 4, 2); end iconTexture:Show(); spellString:Show(); subSpellString:Show(); SpellButton_UpdateSelection(self); end function SpellBookPrevPageButton_OnClick() local pageNum = SpellBook_GetCurrentPage() - 1; if ( SpellBookFrame.bookType == BOOKTYPE_SPELL ) then PlaySound("igAbiliityPageTurn"); SPELLBOOK_PAGENUMBERS[SpellBookFrame.selectedSkillLine] = pageNum; else SpellBookTitleText:SetText(SpellBookFrame.petTitle); -- Need to change to pet book pageturn sound PlaySound("igAbiliityPageTurn"); SPELLBOOK_PAGENUMBERS[BOOKTYPE_PET] = pageNum; end SpellBook_UpdatePageArrows(); SpellBookPageText:SetFormattedText(PAGE_NUMBER, pageNum); UpdateSpells(); end function SpellBookNextPageButton_OnClick() local pageNum = SpellBook_GetCurrentPage() + 1; if ( SpellBookFrame.bookType == BOOKTYPE_SPELL ) then PlaySound("igAbiliityPageTurn"); SPELLBOOK_PAGENUMBERS[SpellBookFrame.selectedSkillLine] = pageNum; else SpellBookTitleText:SetText(SpellBookFrame.petTitle); -- Need to change to pet book pageturn sound PlaySound("igAbiliityPageTurn"); SPELLBOOK_PAGENUMBERS[BOOKTYPE_PET] = pageNum; end SpellBook_UpdatePageArrows(); SpellBookPageText:SetFormattedText(PAGE_NUMBER, pageNum); UpdateSpells(); end function SpellBookSkillLineTab_OnClick(self, id) local update; if ( not id ) then update = 1; id = self:GetID(); end if ( SpellBookFrame.selectedSkillLine ~= id ) then PlaySound("igAbiliityPageTurn"); end SpellBookFrame.selectedSkillLine = id; local name, texture, offset, numSpells = SpellBook_GetTabInfo(SpellBookFrame.selectedSkillLine); SpellBookFrame.selectedSkillLineOffset = offset; SpellBookFrame.selectedSkillLineNumSpells = numSpells; SpellBook_UpdatePageArrows(); SpellBookFrame_Update(); SpellBookPageText:SetFormattedText(PAGE_NUMBER, SpellBook_GetCurrentPage()); if ( update ) then UpdateSpells(); end -- Stop tab flashing if ( self ) then local tabFlash = _G[self:GetName().."Flash"]; if ( tabFlash ) then tabFlash:Hide(); end end end function SpellBookFrameTabButton_OnClick(self) -- suppress the hiding sound so we don't play a hide and show sound simultaneously SpellBookFrame.suppressCloseSound = true; ToggleSpellBook(self.bookType, true); SpellBookFrame.suppressCloseSound = false; end function SpellBook_GetSpellID(id) if ( SpellBookFrame.bookType == BOOKTYPE_PET ) then return id + (SPELLS_PER_PAGE * (SPELLBOOK_PAGENUMBERS[BOOKTYPE_PET] - 1)); else local slot = id + SpellBookFrame.selectedSkillLineOffset + ( SPELLS_PER_PAGE * (SPELLBOOK_PAGENUMBERS[SpellBookFrame.selectedSkillLine] - 1)); if ( not GetCVarBool("ShowAllSpellRanks") ) then return GetKnownSlotFromHighestRankSlot(slot), slot; end return slot, slot; end end function SpellBook_UpdatePageArrows() local currentPage, maxPages = SpellBook_GetCurrentPage(); if ( currentPage == 1 ) then SpellBookPrevPageButton:Disable(); else SpellBookPrevPageButton:Enable(); end if ( currentPage == maxPages ) then SpellBookNextPageButton:Disable(); else SpellBookNextPageButton:Enable(); end end function SpellBook_GetCurrentPage() local currentPage, maxPages; local numPetSpells = HasPetSpells(); if ( SpellBookFrame.bookType == BOOKTYPE_PET ) then currentPage = SPELLBOOK_PAGENUMBERS[BOOKTYPE_PET]; maxPages = ceil(numPetSpells/SPELLS_PER_PAGE); else currentPage = SPELLBOOK_PAGENUMBERS[SpellBookFrame.selectedSkillLine]; local name, texture, offset, numSpells = SpellBook_GetTabInfo(SpellBookFrame.selectedSkillLine); maxPages = ceil(numSpells/SPELLS_PER_PAGE); end return currentPage, maxPages; end local maxShines = 1; shineGet = {} function SpellBook_GetAutoCastShine () local shine = shineGet[1]; if ( shine ) then tremove(shineGet, 1); else shine = CreateFrame("FRAME", "AutocastShine" .. maxShines, SpellBookFrame, "SpellBookShineTemplate"); maxShines = maxShines + 1; end return shine; end function SpellBook_ReleaseAutoCastShine (shine) if ( not shine ) then return; end shine:Hide(); AutoCastShine_AutoCastStop(shine); tinsert(shineGet, shine); end function SpellBook_GetTabInfo(skillLine) local name, texture, offset, numSpells, highestRankOffset, highestRankNumSpells = GetSpellTabInfo(skillLine); if ( not GetCVarBool("ShowAllSpellRanks")) then offset = highestRankOffset; numSpells = highestRankNumSpells; end return name, texture, offset, numSpells; end