--[[-------------------------------------------------------------------- Copyright (C) 2009, 2010, 2011, 2012 Sidoine De Wispelaere. Copyright (C) 2012, 2013, 2014 Johnny C. Lam. See the file LICENSE.txt for copying permission. --]]-------------------------------------------------------------------- local OVALE, Ovale = ... --<class name="OvaleFrame" inherits="Frame" /> do --<private-static-properties> local AceGUI = LibStub("AceGUI-3.0") local Masque = LibStub("Masque", true) local OvaleBestAction = Ovale.OvaleBestAction local OvaleCompile = Ovale.OvaleCompile local OvaleCooldown = Ovale.OvaleCooldown local OvaleDebug = Ovale.OvaleDebug local OvaleGUID = Ovale.OvaleGUID local OvaleSpellFlash = Ovale.OvaleSpellFlash local OvaleState = Ovale.OvaleState local OvaleTimeSpan = Ovale.OvaleTimeSpan local Type = OVALE .. "Frame" local Version = 7 local ipairs = ipairs local next = next local pairs = pairs local tostring = tostring local wipe = wipe local API_CreateFrame = CreateFrame local API_GetTime = GetTime local API_RegisterStateDriver = RegisterStateDriver local NextTime = OvaleTimeSpan.NextTime --</private-static-properties> --<public-methods> local function frameOnClose(self) self.obj:Fire("OnClose") end local function closeOnClick(self) self.obj:Hide() end local function frameOnMouseDown(self) if (not Ovale.db.profile.apparence.verrouille) then self:StartMoving() AceGUI:ClearFocus() end end local function ToggleOptions(self) if (self.content:IsShown()) then self.content:Hide() else self.content:Show() end end local function frameOnMouseUp(self) self:StopMovingOrSizing() local profile = Ovale.db.profile local x, y = self:GetCenter() local parentX, parentY = self:GetParent():GetCenter() profile.apparence.offsetX = x - parentX profile.apparence.offsetY = y - parentY end local function frameOnEnter(self) local profile = Ovale.db.profile if not (profile.apparence.enableIcons and profile.apparence.verrouille) then self.obj.barre:Show() end end local function frameOnLeave(self) self.obj.barre:Hide() end local function frameOnUpdate(self) self.obj:OnUpdate() end local function Hide(self) self.frame:Hide() end local function Show(self) self.frame:Show() end local function OnAcquire(self) self.frame:SetParent(UIParent) end local function OnRelease(self) end local function OnWidthSet(self, width) local content = self.content local contentwidth = width - 34 if contentwidth < 0 then contentwidth = 0 end content:SetWidth(contentwidth) content.width = contentwidth end local function OnHeightSet(self, height) local content = self.content local contentheight = height - 57 if contentheight < 0 then contentheight = 0 end content:SetHeight(contentheight) content.height = contentheight end local function OnLayoutFinished(self, width, height) if (not width) then width = self.content:GetWidth() end self.content:SetWidth(width) self.content:SetHeight(height+50) end local function GetScore(self, spellId) for k,action in pairs(self.actions) do if action.spellId == spellId then if not action.waitStart then -- print("sort "..spellId.." parfait") return 1 else local now = API_GetTime() local lag = now - action.waitStart if lag>5 then -- print("sort "..spellId.." ignoré (>5s)") return nil elseif lag>1.5 then -- print("sort "..spellId.." trop lent !") return 0 elseif lag>0 then -- print("sort "..spellId.." un peu lent "..lag) return 1-lag/1.5 else -- print("sort "..spellId.." juste bon") return 1 end end end end -- print("sort "..spellId.." incorrect") return 0 end local function OnUpdate(self) -- Update current time. local now = API_GetTime() local profile = Ovale.db.profile -- Force a refresh if we've exceeded the minimum update interval since the last refresh. local forceRefresh = not self.lastUpdate or (now > self.lastUpdate + profile.apparence.updateInterval) -- Refresh the icons if we're forcing a refresh or if one of the units the script is tracking needs a refresh. local refresh = forceRefresh or next(Ovale.refreshNeeded) if not refresh then return end self.lastUpdate = now local state = OvaleState.state state:Initialize() if OvaleCompile:EvaluateScript() then Ovale:UpdateFrame() end local iconNodes = OvaleCompile:GetIconNodes() for k, node in ipairs(iconNodes) do -- Set the true target of "target" references in the icon's body. if node.params and node.params.target then state.defaultTarget = node.params.target else state.defaultTarget = "target" end -- Set the number of enemies on the battlefield, if given via "enemies=N". if node.params and node.params.enemies then state.enemies = node.params.enemies else state.enemies = nil end -- Refresh the action button for the node. if refresh then state:Log("+++ Icon %d", k) OvaleBestAction:StartNewAction(state) local timeSpan, _, element = OvaleBestAction:GetAction(node, state) local start = NextTime(timeSpan, state.currentTime) local action = self.actions[k] local profile = Ovale.db.profile if profile.apparence.enableIcons then self:UpdateActionIcon(state, node, action, element, start, now) end if profile.apparence.spellFlash.enabled then OvaleSpellFlash:Flash(state, node, element, start, now) end end end wipe(Ovale.refreshNeeded) OvaleDebug:UpdateTrace() Ovale:PrintOneTimeMessages() end local function UpdateActionIcon(self, state, node, action, element, start, now) local profile = Ovale.db.profile local icons = action.secure and action.secureIcons or action.icons now = now or API_GetTime() if element and element.type == "value" then local value if element.value and element.origin and element.rate then value = element.value + (now - element.origin) * element.rate end state:Log("GetAction: start=%s, value=%f", start, value) local actionTexture if node.params and node.params.texture then actionTexture = node.params.texture end icons[1]:SetValue(value, actionTexture) if #icons > 1 then icons[2]:Update(element, nil) end else local actionTexture, actionInRange, actionCooldownStart, actionCooldownDuration, actionUsable, actionShortcut, actionIsCurrent, actionEnable, actionType, actionId, actionTarget = OvaleBestAction:GetActionInfo(element, state) state:Log("GetAction: start=%s, id=%s", start, actionId) --[[ If "nored=1" is given as an action parameter, then just use the actual start time of the element itself. --]] if element and element.params and element.params.nored == 1 then start = actionCooldownStart + actionCooldownDuration if start < state.currentTime then start = state.currentTime end state:Log("Adjusting for 'nored': start=%s", start) end -- Dans le cas de canStopChannelling, on risque de demander d'interrompre le channelling courant, ce qui est stupide if start and state.currentSpellId and state.nextCast and actionType == "spell" and actionId == state.currentSpellId and start < state.nextCast then start = state.nextCast end if start and node.params.nocd and now < start - node.params.nocd then icons[1]:Update(element, nil) else icons[1]:Update(element, start, actionTexture, actionInRange, actionCooldownStart, actionCooldownDuration, actionUsable, actionShortcut, actionIsCurrent, actionEnable, actionType, actionId, actionTarget) end -- TODO: Scoring should allow for other actions besides spells. if actionType == "spell" then action.spellId = actionId else action.spellId = nil end if start and start <= now and actionUsable then action.waitStart = action.waitStart or now else action.waitStart = nil end if profile.apparence.moving and icons[1].cooldownStart and icons[1].cooldownEnd then local top=1-(now - icons[1].cooldownStart)/(icons[1].cooldownEnd-icons[1].cooldownStart) if top<0 then top = 0 elseif top>1 then top = 1 end icons[1]:SetPoint("TOPLEFT",self.frame,"TOPLEFT",(action.left + top*action.dx)/action.scale,(action.top - top*action.dy)/action.scale) if icons[2] then icons[2]:SetPoint("TOPLEFT",self.frame,"TOPLEFT",(action.left + (top+1)*action.dx)/action.scale,(action.top - (top+1)*action.dy)/action.scale) end end if (node.params.size ~= "small" and not node.params.nocd and profile.apparence.predictif) then if start then state:Log("****Second icon %s", start) local spellTarget if element then spellTarget = element.params.target end if not spellTarget or spellTarget == "target" then spellTarget = state.defaultTarget end state:ApplySpell(actionId, OvaleGUID:GetGUID(spellTarget)) local timeSpan, _, nextElement = OvaleBestAction:GetAction(node, state) start = NextTime(timeSpan, state.currentTime) icons[2]:Update(nextElement, start, OvaleBestAction:GetActionInfo(nextElement, state)) else icons[2]:Update(element, nil) end end end end local function UpdateFrame(self) local profile = Ovale.db.profile self.frame:SetPoint("CENTER", self.hider, "CENTER", profile.apparence.offsetX, profile.apparence.offsetY) self.frame:EnableMouse(not profile.apparence.clickThru) end local function UpdateIcons(self) for k, action in pairs(self.actions) do for i, icon in pairs(action.icons) do icon:Hide() end for i, icon in pairs(action.secureIcons) do icon:Hide() end end local profile = Ovale.db.profile self.frame:EnableMouse(not profile.apparence.clickThru) local left = 0 local maxHeight = 0 local maxWidth = 0 local top = 0 local BARRE = 8 local margin = profile.apparence.margin local iconNodes = OvaleCompile:GetIconNodes() for k, node in ipairs(iconNodes) do if not self.actions[k] then self.actions[k] = {icons={}, secureIcons={}} end local action = self.actions[k] local width, height, newScale local nbIcons if (node.params.size == "small") then newScale = profile.apparence.smallIconScale width = newScale * 36 + margin height = newScale * 36 + margin nbIcons = 1 else newScale = profile.apparence.iconScale width =newScale * 36 + margin height = newScale * 36 + margin if profile.apparence.predictif and node.params.type ~= "value" then nbIcons = 2 else nbIcons = 1 end end if (top + height > profile.apparence.iconScale * 36 + margin) then top = 0 left = maxWidth end action.scale = newScale if (profile.apparence.vertical) then action.left = top action.top = -left-BARRE-margin action.dx = width action.dy = 0 else action.left = left action.top = -top-BARRE-margin action.dx = 0 action.dy = height end action.secure = node.secure for l=1,nbIcons do local icon if not node.secure then if not action.icons[l] then action.icons[l] = API_CreateFrame("CheckButton", "Icon"..k.."n"..l, self.frame, OVALE .. "IconTemplate"); end icon = action.icons[l] else if not action.secureIcons[l] then action.secureIcons[l] = API_CreateFrame("CheckButton", "SecureIcon"..k.."n"..l, self.frame, "Secure" .. OVALE .. "IconTemplate"); end icon = action.secureIcons[l] end local scale = action.scale if l> 1 then scale = scale * profile.apparence.secondIconScale end icon:SetPoint("TOPLEFT",self.frame,"TOPLEFT",(action.left + (l-1)*action.dx)/scale,(action.top - (l-1)*action.dy)/scale) icon:SetScale(scale) icon:SetFontScale(profile.apparence.fontScale) icon:SetParams(node.params) icon:SetHelp(node.params.help) icon:SetRangeIndicator(profile.apparence.targetText) icon:EnableMouse(not profile.apparence.clickThru) icon.cdShown = (l == 1) if Masque then self.skinGroup:AddButton(icon) end if l==1 then icon:Show(); end end top = top + height if (top> maxHeight) then maxHeight = top end if (left + width > maxWidth) then maxWidth = left + width end end if (profile.apparence.vertical) then self.barre:SetWidth(maxHeight - margin) self.barre:SetHeight(BARRE) self.frame:SetWidth(maxHeight + profile.apparence.iconShiftY) self.frame:SetHeight(maxWidth+BARRE+margin + profile.apparence.iconShiftX) self.content:SetPoint("TOPLEFT", self.frame, "TOPLEFT", maxHeight + profile.apparence.iconShiftX, profile.apparence.iconShiftY) else self.barre:SetWidth(maxWidth - margin) self.barre:SetHeight(BARRE) self.frame:SetWidth(maxWidth) -- + profile.apparence.iconShiftX self.frame:SetHeight(maxHeight+BARRE+margin) -- + profile.apparence.iconShiftY self.content:SetPoint("TOPLEFT", self.frame, "TOPLEFT", maxWidth + profile.apparence.iconShiftX, profile.apparence.iconShiftY) end end local function Constructor() -- Create parent frame for Ovale that auto-hides/shows based on whether the Pet Battle UI is active. local hider = API_CreateFrame("Frame", OVALE .. "PetBattleFrameHider", UIParent, "SecureHandlerStateTemplate") hider:SetAllPoints(true) API_RegisterStateDriver(hider, "visibility", "[petbattle] hide; show") local frame = API_CreateFrame("Frame", nil, hider) local self = {} local profile = Ovale.db.profile self.Hide = Hide self.Show = Show self.OnRelease = OnRelease self.OnAcquire = OnAcquire self.LayoutFinished = OnLayoutFinished self.UpdateActionIcon = UpdateActionIcon self.UpdateFrame = UpdateFrame self.UpdateIcons = UpdateIcons self.ToggleOptions = ToggleOptions self.OnUpdate = OnUpdate self.GetScore = GetScore --<public-properties> self.type = "Frame" self.localstatus = {} self.actions = {} self.frame = frame self.hider = hider self.updateFrame = API_CreateFrame("Frame") self.barre = self.frame:CreateTexture(); self.content = API_CreateFrame("Frame", nil, self.updateFrame) if Masque then self.skinGroup = Masque:Group(OVALE) end self.lastUpdate = nil --Cheating with frame object which has an obj property --TODO: Frame Class self.obj = nil --</public-properties> frame.obj = self frame:SetWidth(100) frame:SetHeight(100) self:UpdateFrame() frame:SetMovable(true) frame:SetFrameStrata("MEDIUM") frame:SetScript("OnMouseDown", frameOnMouseDown) frame:SetScript("OnMouseUp", frameOnMouseUp) frame:SetScript("OnEnter", frameOnEnter) frame:SetScript("OnLeave", frameOnLeave) -- frame:SetScript("OnUpdate", frameOnUpdate) frame:SetScript("OnHide",frameOnClose) frame:SetAlpha(profile.apparence.alpha) self.updateFrame:SetScript("OnUpdate", frameOnUpdate) self.updateFrame.obj = self self.barre:SetTexture(0,0.8,0) self.barre:SetPoint("TOPLEFT",0,0) self.barre:Hide() --Container Support local content = self.content content.obj = self content:SetWidth(200) content:SetHeight(100) content:Hide() content:SetAlpha(profile.apparence.optionsAlpha) AceGUI:RegisterAsContainer(self) return self end --</public-methods> AceGUI:RegisterWidgetType(Type,Constructor,Version) end