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.

900 lines
32 KiB

local _, T = ...
local EV, L, U, S = T.Evie, T.L, T.Util, T.Shadows
local FollowerList, MissionRewards
local function GenBoardMask()
local m, MP = 0, CovenantMissionFrame.MissionTab.MissionPage
for i=0,12 do
local f = MP.Board.framesByBoardIndex[i]
if f and f.name and f:IsShown() then
m = m + 2^i
end
end
return m
end
local function GetIncomingAAMask(slot, bm)
local r, VS = 0, T.VSim
local f = CovenantMissionFrame.MissionTab.MissionPage.Board.framesByBoardIndex
local mid = CovenantMissionFrame.MissionTab.MissionPage.missionInfo.missionID
local board = U.GetMaskBoard(bm)
if slot < 5 then
for _,v in pairs(C_Garrison.GetMissionDeploymentInfo(mid).enemies) do
local i = v.boardIndex
local fi = f[i]
local s1 = fi.autoCombatSpells and fi.autoCombatSpells[1]
local aa = T.KnownSpells[VS:GetAutoAttack(v.role, i, mid, s1 and s1.autoCombatSpellID)]
if aa and VS.GetTargets(i, aa.target, board)[1] == slot then
r = bit.bor(r, 2^i)
end
end
else
for i=0, 4 do
if bm % 2^(i+1) >= 2^i then
local fi = f[i]
local v, s1 = fi.info, fi.autoCombatSpells and fi.autoCombatSpells[1]
local aa = T.KnownSpells[VS:GetAutoAttack(v.role, nil, nil, s1 and s1.autoCombatSpellID)]
if aa and VS.GetTargets(i, aa.target, board)[1] == slot then
r = bit.bor(r, 2^i)
end
end
end
end
return r
end
local function GetFollowerInfo(fid)
local fi = C_Garrison.GetFollowerInfo(fid)
fi.autoCombatSpells = C_Garrison.GetFollowerAutoCombatSpells(fid, fi.level)
fi.autoCombatantStats = C_Garrison.GetFollowerAutoCombatStats(fid)
return fi
end
local function Board_HasCompanion()
local f = CovenantMissionFrame.MissionTab.MissionPage.Board.framesByBoardIndex
for i=0,4 do
local ii = f[i].info
if ii and not ii.isAutoTroop then
return true
end
end
return false
end
local function Puck_OnEnter(self)
if not self.name then
if GameTooltip:IsOwned(self) then
GameTooltip:Hide()
end
return
end
local mid = CovenantMissionFrame.MissionTab.MissionPage.missionInfo.missionID
local bi, bm = self.boardIndex, GenBoardMask()
local info, acs = self.info
if bi > 4 then
for _,v in pairs(C_Garrison.GetMissionDeploymentInfo(mid).enemies) do
if v.boardIndex == bi then
info, acs = v, {currentHealth=v.health, maxHealth=v.maxHealth, attack=v.attack}
break
end
end
end
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
U.SetFollowerInfo(GameTooltip, info, self.autoCombatSpells, acs, mid, bi, bm, false)
local aa = GetIncomingAAMask(bi, bm)
if aa and aa > 0 then
local nc = NORMAL_FONT_COLOR
GameTooltip:AddLine(" ")
GameTooltip:AddLine(L"Incoming attacks:" .. " " .. U.FormatTargetBlips(aa, bm, nil, "240:60:0", false), nc.r, nc.g, nc.b)
end
GameTooltip:Show()
self:GetBoard():ShowHealthValues()
end
local function Puck_OnLeave(self)
if GameTooltip:IsOwned(self) then
GameTooltip:Hide()
end
self:GetBoard():HideHealthValues()
end
local function EnvironmentEffect_OnEnter(self)
local info = self.info
if not info then return end
local sid = info.autoCombatSpellID
local pfx = T.KnownSpells[sid] and "" or "|TInterface/EncounterJournal/UI-EJ-WarningTextIcon:0|t "
local mi = CovenantMissionFrame.MissionTab.MissionPage.missionInfo
GameTooltip:SetOwner(self, "ANCHOR_NONE")
GameTooltip:SetPoint("TOPLEFT", self, "BOTTOMLEFT", -8, 0)
GameTooltip:ClearLines()
GameTooltip:AddDoubleLine(pfx .. "|T" .. info.icon .. ":0:0:0:0:64:64:4:60:4:60|t " .. info.name, "|cffa8a8a8" .. (L"[CD: %dT]"):format(info.cooldown) .. "|r")
local guideLine = U.GetAbilityGuide(sid, -1, GenBoardMask(), false)
local od = U.GetAbilityDescriptionOverride(info and info.autoCombatSpellID, nil, mi and mi.missionScalar)
local dc = od and 0.60 or 0.95
GameTooltip:AddLine(info.description, dc, dc, dc, 1)
if od then
GameTooltip:AddLine(od, 0.45, 1, 0, 1)
end
if guideLine then
GameTooltip:AddLine(guideLine, 0.45, 1, 0, 1)
end
GameTooltip:Show()
end
local function EnvironmentEffect_OnLeave(self)
if GameTooltip:IsOwned(self) then
GameTooltip:Hide()
end
end
local function EnvironmentEffect_OnNameUpdate(self_name)
local ee = self_name:GetParent()
ee:SetHitRectInsets(0, min(-100, -self_name:GetStringWidth()), 0, 0)
end
local CAG, SetSimResultHint = {} do
local simArch, reSim, state, deadline
local function GetGroupTags()
local f = CovenantMissionFrame.MissionTab.MissionPage.Board.framesByBoardIndex
local mi = CovenantMissionFrame.MissionTab.MissionPage.missionInfo
local tag, htag = (mi.missionID) .. ":" .. (mi.missionScalar or 0), ""
for i=0,4 do
local ii = f[i].info
if ii then
local stats = C_Garrison.GetFollowerAutoCombatStats(ii.followerID)
tag = tag .. ":" .. i .. ":" .. stats.attack .. ":" .. ii.followerID
htag = htag .. ":" .. stats.currentHealth
end
end
htag = tag .. htag
return htag, tag
end
local function cmpBoardIndex(a,b)
return a.boardIndex < b.boardIndex
end
local function GetComputedGroupTags(g)
local mi = CovenantMissionFrame.MissionTab.MissionPage.missionInfo
local tag, htag = (mi.missionID) .. ":" .. (mi.missionScalar or 0), ""
local m = {}
for i=1,#g do
m[i] = g[i]
end
table.sort(m, cmpBoardIndex)
for i=1,#m do
local ii = m[i]
local stats = ii.stats
tag = tag .. ":" .. ii.boardIndex .. ":" .. stats.attack .. ":" .. ii.id
htag = htag .. ":" .. stats.currentHealth
end
htag = tag .. htag
return htag, tag
end
local function qdeadline(root)
return debugprofilestop() > deadline or (root.res.hadLosses and root.res.hadWins)
end
local function qdeadlineorloss(root)
return debugprofilestop() > deadline or root.res.hadLosses
end
local function isDone(res, endOnAnyLoss)
return res and (res.isFinished or ((res.hadWins or endOnAnyLoss) and res.hadLosses))
end
local function setupRetry()
if reSim then
state.reCount = state.reCount + reSim.res.n
state[reSim.res.hadLosses and "reLow" or "reHigh"], state.reTry, reSim = state.reTry, nil
end
if not (state.reRange and state.reLow < state.reRange) or (state.reHigh and state.reHigh - state.reLow < 2^-11) then
if state.reHigh then
state.reFinish = state.reStart + state.reRangeT*state.reHigh
end
reSim, state.reTry, state.reLow, state.reHigh, state.reTeam, state.reStart, state.reRange, state.reRangeT = nil
return
end
local rteam, tryRe = state.reTeam
if not rteam then
rteam = {}
for i=1, #state.team do
local s, d = state.team[i], {}
for k,v in pairs(s) do
d[k] = v
end
rteam[i], s, d = d, s.stats, {}
for k,v in pairs(s) do
d[k] = v
end
rteam[i].stats = d
end
state.reTeam, tryRe = rteam, state.reRange
else
tryRe = (state.reLow + state.reHigh)/2
end
for i=1,#rteam do
local rs, s = rteam[i].stats, state.team[i].stats
if s.currentHealth < s.maxHealth then
rs.currentHealth = math.min(s.maxHealth, math.floor(s.currentHealth + tryRe*s.maxHealth+0.5))
end
end
state.reTry, reSim = tryRe, T.VSim:New(state.reTeam, state.enemies, state.espell, state.mid, state.msc)
return true
end
function CAG:GatherMissionData()
local team, reRange, reRangeT = {}, 0, 0 do
local f = CovenantMissionFrame.MissionTab.MissionPage.Board.framesByBoardIndex
for i=0,4 do
local ii = f[i].info
if ii then
local acs = C_Garrison.GetFollowerAutoCombatStats(ii.followerID)
team[#team+1] = {boardIndex=i, role=ii.role, stats=acs, spells=f[i].autoCombatSpells}
if acs.currentHealth < acs.maxHealth then
reRange = math.max(reRange, 1 - acs.currentHealth/acs.maxHealth)
reRangeT = math.max(reRangeT, acs.minutesHealingRemaining or 0)
end
end
end
end
local mi = CovenantMissionFrame.MissionTab.MissionPage.missionInfo
local eei = C_Garrison.GetAutoMissionEnvironmentEffect(mi.missionID)
local mdi = C_Garrison.GetMissionDeploymentInfo(mi.missionID)
local espell = eei and eei.autoCombatSpellInfo
reRangeT = reRange > 0 and 60*reRangeT/reRange or nil
return {team=team, enemies=mdi.enemies, espell=espell, mid=mi.missionID, msc=mi.missionScalar,
reStart=reRange > 0 and GetServerTime() or nil, reRange=reRange > 0 and reRange or nil, reRangeT=reRangeT, reCount=reRange > 0 and 0 or nil, reLow=0}
end
function CAG:Start()
local tag, rtag = GetGroupTags()
if not state or state.tag ~= tag then
local os, md, ms = state, CAG:GatherMissionData()
state, md.tag, md.rtag, reSim = md, tag, rtag, nil
if os and os.rtag == rtag and os.reFinish and md.reStart then
md.reFinish, md.reStart, md.reRange, md.reRangeT, md.reLow = os.reFinish, nil, nil
end
simArch, ms = T.VSim:New(md.team, md.enemies, md.espell, md.mid, md.msc)
md.missingSpells, simArch.dropForks = ms and next(ms) and true or nil, true
deadline = debugprofilestop() + 40
simArch:Run(qdeadline)
return not isDone(simArch.res) or setupRetry()
end
return not (isDone(simArch.res) and not state.reStart)
end
function CAG:Run()
if not simArch then
return true
end
deadline = debugprofilestop() + 12
if reSim then
if isDone(reSim.res, true) then
return not setupRetry()
else
reSim:Run(qdeadlineorloss)
end
elseif isDone(simArch.res) then
simArch.outOfDateHealth = GetGroupTags() ~= (state and state.tag)
return not (simArch.res.hadLosses and setupRetry())
else
simArch:Run(qdeadline)
end
end
function CAG:GetResult()
local rc = state and state.reCount
if rc and reSim then
rc = rc + reSim.res.n
end
return simArch, state and state.missingSpells, state and (state.reFinish or state.reStart and true) or nil, rc, (state and state.reHigh and state.reHigh*state.reRangeT)
end
function CAG:Reset()
simArch, reSim, state = nil
end
function SetSimResultHint(g, sim, ms)
state = CAG:GatherMissionData()
local ng = {}
for slot, ii in pairs(g) do
local nge = {boardIndex=slot}
for k,v in pairs(ii) do
nge[k] = v
end
ng[#ng+1] = nge
end
state.tag, state.rtag = GetComputedGroupTags(ng)
simArch, state.team, state.missingSpells = sim, ng, ms and next(ms) and true or nil
end
EV.GARRISON_MISSION_NPC_CLOSED = CAG.Reset
end
local Tact = {} do
local pt, state, deadline = {}
local function cmpFollowerID(a,b)
return a.followerID < b.followerID
end
local function GetGroupTags()
local f = CovenantMissionFrame.MissionTab.MissionPage.Board.framesByBoardIndex
local mi = CovenantMissionFrame.MissionTab.MissionPage.missionInfo
local tag, htag = (mi.missionID) .. ":" .. (mi.missionScalar or 0), ""
local m = {}
for i=0,4 do
local ii = f[i].info
if ii and not ii.isAutoTroop then
m[#m+1] = ii
end
end
table.sort(m, cmpFollowerID)
for i=1,#m do
local ii = m[i]
local stats = C_Garrison.GetFollowerAutoCombatStats(ii.followerID) or ii.autoCombatantStats
tag = tag .. ":" .. i .. ":" .. stats.attack .. ":" .. ii.followerID
htag = htag .. ":" .. stats.currentHealth
end
htag = tag .. htag
return htag, tag
end
local function GetShuffleGroup(gid)
local rgid, g, um, maxScore, pen, wmask = gid, {}, 0, 4, 0, 0
for i=0,4 do
pt[i] = i
end
for i=1, #state.companions do
local p, li, ci = rgid % (6-i), 5-i, state.companions[i]
rgid = (rgid - p) / (6-i)
p, pt[li], pt[p] = pt[p], pt[p], pt[li]
g[p], um = ci, um + 2^p
if not ci.willLevel then
maxScore, wmask = maxScore + 5*ci.stats.maxHealth, wmask + 2^p
end
end
for p=0, 4-#state.companions do
p = pt[p]
local i = rgid % 3
rgid = (rgid - i) / 3
local ti = state.troops[i+1]
if ti then
g[p], pen = ti, pen + 1
end
end
return g, maxScore-pen, pen, wmask
end
local function GetHealthScore(rm)
return (rm[18] or 0)*5+4-state.cpenalty
end
local function interrupt(root, _forkID, _nForks)
local res = root.res
state.numFutures = state.numFutures + 1
if res.hadLosses or debugprofilestop() >= deadline or GetHealthScore(res.min) < state.bestScore then
return true
end
end
function Tact:Run()
if not state then
return true
end
deadline = debugprofilestop() + 15
repeat
local sim = state.csim
if not sim then
local ng = state.nextGroup
if ng >= state.numGroups then
state.finished = true
local g = state.bestGroup and GetShuffleGroup(state.bestGroup)
if g then
SetSimResultHint(g, state.bestSim, state.bestMiss)
end
return true, g
end
local ms, team, maxScore, troopPenalty, wmask = nil, GetShuffleGroup(ng)
if maxScore > state.bestScore then
sim, ms = T.VSim:New(team, state.enemies, state.espell, state.missionID, state.missionScalar)
sim.wmask, sim.dropForks = wmask, true
state.csim, state.cmiss, state.cgroup, state.cpenalty = sim, ms, ng, troopPenalty
state.numFutures = state.numFutures + 1
end
state.nextGroup = ng+1
end
if sim then
sim:Run(interrupt)
if sim.res.hadLosses then
state.csim = nil
elseif sim.res.isFinished then
local h = GetHealthScore(sim.res.min)
if h > state.bestScore then
state.bestScore, state.bestGroup, state.bestSim, state.bestMiss = h, state.cgroup, sim, state.cmiss
end
state.csim = nil
end
end
until debugprofilestop() > deadline
end
function Tact:GatherMissionData()
local mi = CovenantMissionFrame.MissionTab.MissionPage.missionInfo
local eei = C_Garrison.GetAutoMissionEnvironmentEffect(mi.missionID)
local mdi = C_Garrison.GetMissionDeploymentInfo(mi.missionID)
local espell = eei and eei.autoCombatSpellInfo
local ct, tt = {}, {} do
local f = CovenantMissionFrame.MissionTab.MissionPage.Board.framesByBoardIndex
local rewardXP = MissionRewards and MissionRewards.xpGain or 0
for i=0,4 do
local ii = f[i].info
if ii and not ii.isAutoTroop then
local willLevel = not ii.isMaxLevel and ii.xp and ii.levelXP and (ii.xp + rewardXP) >= ii.levelXP
local acs = C_Garrison.GetFollowerAutoCombatStats(ii.followerID) or ii.autoCombatantStats
ct[#ct+1] = {role=ii.role, stats=acs, spells=f[i].autoCombatSpells, id=ii.followerID, willLevel=willLevel}
end
end
for _, fi in ipairs(C_Garrison.GetAutoTroops(123)) do
tt[#tt+1] = {
role=fi.role,
stats=C_Garrison.GetFollowerAutoCombatStats(fi.followerID),
spells=C_Garrison.GetFollowerAutoCombatSpells(fi.followerID, fi.level),
id=fi.followerID,
}
end
end
return {missionID=mi.missionID, missionScalar=mi.missionScalar, enemies=mdi.enemies, espell=espell, companions=ct, troops=tt}
end
function Tact:Start()
if not Board_HasCompanion() then
return
end
local ht, it = GetGroupTags()
if state and not (it ~= state.itag or (state.finished and state.htag ~= ht)) then
return true
end
state = self:GatherMissionData()
local ng = 3^(5-#state.companions)
for i=1, #state.companions do
ng = ng * (6-i)
end
state.numGroups, state.nextGroup, state.bestGroup, state.bestScore = ng, 0, false, -1e12
state.numFutures, state.htag, state.itag = 0, ht, it
return true
end
function Tact:IsRunning()
if state and not state.finished then
return true, state.nextGroup, state.numGroups, state.numFutures, not not state.bestGroup
elseif state and GetGroupTags() == state.htag then
return false, true, not not state.bestGroup
else
return false, false, Board_HasCompanion()
end
end
function Tact:Interrupt()
if state and state.bestGroup then
local g = GetShuffleGroup(state.bestGroup)
SetSimResultHint(g, state.bestSim, state.bestMiss)
return g
end
end
function Tact:CheckBoard(later)
if later then
if not Tact.pendingBoardCheck then
C_Timer.After(0, Tact.CheckBoard)
end
Tact.pendingBoardCheck = true
return
end
Tact.pendingBoardCheck = nil
if state and not state.finished and state.itag ~= select(2, GetGroupTags()) then
state = nil
end
end
function Tact:Reset()
state = nil
end
end
local function Predictor_OnEnter(self)
GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT")
GameTooltip:SetText(ITEM_QUALITY_COLORS[5].hex .. L"Cursed Adventurer's Guide")
GameTooltip:AddLine(ITEM_UNIQUE, 1,1,1, 1)
GameTooltip:AddLine(L"Use: Read the guide, determining the fate of your adventuring party.", 0, 1, 0, 1)
GameTooltip:AddLine(L'"Do not believe its lies! Balance druids are not emergency rations."', 1, 0.835, 0.09, 1)
GameTooltip:Show()
end
local function Predictor_ShowResult(self, sim, incompleteModel, recoverUntil, recoverFutures, recoverHighBound)
GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT")
local res = sim.res
local rngModel = res.hadDrops or (res.hadWins and res.hadLosses)
local inProgress = not res.isFinished and not rngModel
local oodBuild = GetBuildInfo() ~= "9.0.5"
local hprefix = (oodBuild or incompleteModel) and "|TInterface/EncounterJournal/UI-EJ-WarningTextIcon:0|t " or ""
if inProgress then
hprefix = hprefix .. "|cffff3300" .. L"Preliminary:" .. "|r "
end
if rngModel then
GameTooltip:SetText(hprefix .. L"Curse of Uncertainty", 1, 0.20, 0)
else
GameTooltip:SetText(hprefix .. (sim.won and L"Victorious" or L"Defeated"), 1,1,1)
end
if incompleteModel then
GameTooltip:AddLine(L"Not all abilities have been taken into account.", 0.9,0.25,0.15)
elseif oodBuild then
GameTooltip:AddLine(L"The Guide may be out of date.", 0.9,0.25,0.15)
end
if inProgress then
GameTooltip:AddLine(L"Not all outcomes have been examined.", 0.9, 0.25, 0.15, 1)
end
if sim.outOfDateHealth then
GameTooltip:AddLine(L"Companion health has changed.", 0.9, 0.25, 0.15, 1)
end
if incompleteModel or inProgress or sim.outOfDateHealth then
GameTooltip:AddLine(" ")
end
local flavor = nil
if rngModel then
GameTooltip:AddLine(L"The guide shows you a number of possible futures. In some, the adventure ends in triumph; in others, a particularly horrible failure.", 1,1,1,1)
if not incompleteModel then
flavor = L'"With your luck, there is only one way this ends."'
end
else
local lo, hi, c = res.min, res.max, NORMAL_FONT_COLOR
local turns = lo[17] ~= hi[17] and lo[17] .. " - " .. hi[17] or lo[17]
if inProgress and not incompleteModel then
GameTooltip:AddLine(L"Futures considered:" .. " |cffffffff" .. BreakUpLargeNumbers(res.n or 0), c.r, c.g, c.b)
end
if turns then
GameTooltip:AddLine((sim.won and L"Turns taken: %s" or L"Turns survived: %s"):format("|cffffffff" .. turns .. "|r"), c.r, c.g, c.b)
end
if sim.won then
local troopCount, troopHealth1, troopHealth2, troopHealthMax = 0, 0, 0, 0
local rewardXP = MissionRewards.xpGain or 0
for i=0,4 do
local hmin, hmax = lo[i], hi[i]
local f = CovenantMissionFrame.MissionTab.MissionPage.Board.framesByBoardIndex[i]
local e = sim.board[i]
if f and f.name and f:IsShown() and f.info and hmin and e then
local fi = f.info
if fi.isAutoTroop then
troopCount, troopHealth1, troopHealth2, troopHealthMax = troopCount + 1, troopHealth1 + hmin, troopHealth2 + hmax, troopHealthMax + (e.maxHP or 0)
else
local chp = hmin == hmax and hmin or ((hmin == 0 and "|cffff40200|r" or hmin) .. " |cffffffff-|r " .. hmax)
local isUp = fi.xp and fi.levelXP and not fi.isMaxLevel and (fi.xp + rewardXP) >= fi.levelXP and "|A:bags-greenarrow:0:0|a" or ""
GameTooltip:AddDoubleLine(f.name .. isUp, chp .. "/" .. e.maxHP, 1,1,1, hmax > 0 and 0 or 1, hmax > 0 and 1 or 0.3, 0.15)
end
end
end
if troopCount > 0 then
local hmin, hmax = troopHealth1, troopHealth2
local chp = hmin == hmax and hmin or ((hmin == 0 and "|cffff40200|r" or hmin) .. " |cffffffff-|r " .. hmax)
GameTooltip:AddDoubleLine(FOLLOWERLIST_LABEL_TROOPS, chp .. "/" .. troopHealthMax, 1,1,1, troopHealth2 > 0 and 0 or 1, troopHealth2 > 0 and 1 or 0.3, 0.15)
end
else
local hmin, hmax, maxHP = lo[15], hi[15], 0
for i=5,12 do
local e = sim.board[i]
maxHP = maxHP + (e and e.maxHP or 0)
end
local chp = hmin == hmax and hmin or (hmin .. " - " .. hmax)
hmin, hmax = math.ceil(hmin/maxHP*100), math.ceil(hmax/maxHP*100)
local cr = hmin == hmax and hmin or (hmin .. "% - " .. hmax)
GameTooltip:AddLine((L"Remaining enemy health: %s"):format("|cffffffff" .. chp .. " (" .. cr .. "%)|r"), c.r, c.g, c.b)
end
if not incompleteModel then
if inProgress then
flavor = L'"... or not. Better read this thing to the end."'
elseif lo[sim.won and 13 or 15] == 0 then
flavor = sim.won and L'"Snatch victory from the jaws of defeat!"' or L'"So close, and yet so far."'
else
flavor = L'"Was there ever any doubt?"'
end
if not sim.won then
flavor = "\n" .. flavor
end
end
end
if res.hadLosses and recoverUntil and not inProgress then
if recoverUntil == true then
local nc = NORMAL_FONT_COLOR
if recoverFutures > 0 or recoverHighBound then
GameTooltip:AddLine(" ")
end
GameTooltip:AddLine(L"Checking health recovery...", 0.45, 1, 0)
if recoverFutures > 0 then
GameTooltip:AddDoubleLine(L"Futures considered:", BreakUpLargeNumbers(recoverFutures), nc.r, nc.g, nc.b, 1,1,1)
end
if recoverHighBound then
local mi = CovenantMissionFrame.MissionTab.MissionPage.missionInfo
local cc = recoverHighBound < (mi and mi.offerEndTime and mi.offerEndTime-GetTime() or 0) and "|cffffffff" or "|cffe86000"
GameTooltip:AddLine((L"Would win if started in: %s"):format("|cffffffff <= |r" .. cc .. U.GetTimeStringFromSeconds(recoverHighBound, false, true, true) .. "|r"), nc.r, nc.g, nc.b)
end
else
local rl = math.max(0, recoverUntil - GetServerTime())
local mi = CovenantMissionFrame.MissionTab.MissionPage.missionInfo
local cc = rl < (mi and mi.offerEndTime and mi.offerEndTime-GetTime() or 0) and "|cffffffff" or "|cffe86000"
GameTooltip:AddLine((L"Would win if started in: %s"):format(cc .. U.GetTimeStringFromSeconds(rl, false, true, true) .. "|r"), 0.45, 1, 0.15)
end
end
if flavor then
GameTooltip:AddLine(flavor, 1, 0.835, 0.09, 1)
end
GameTooltip:Show()
end
local function Predictor_OnUpdate(self, elapsed)
local rcd, isDone = (self.rsCooldown or 0) - elapsed, CAG:Run()
if isDone then
self:SetScript("OnUpdate", nil)
end
if (rcd < 0 or isDone) and GameTooltip:IsOwned(self) then
Predictor_ShowResult(self, CAG:GetResult())
rcd = 0.125
end
self.rsCooldown = rcd
end
local function Predictor_OnClick(self)
if CAG:Start() then
self.rsCooldown = 0.125
self:SetScript("OnUpdate", Predictor_OnUpdate)
end
Predictor_ShowResult(self, CAG:GetResult())
end
local function Predictor_OnLeave(self)
if GameTooltip:IsOwned(self) then
GameTooltip:Hide()
end
end
local function MissionGroup_OnUpdate()
Tact:CheckBoard(true)
local o = GameTooltip:IsVisible() and GameTooltip:GetOwner() or GetMouseFocus()
if o and not o:IsForbidden() and o.GetScript then
local l, p, t = 3, o, CovenantMissionFrame.MissionTab.MissionPage.Board
while p and p ~= t and l > 0 and p.GetParent and p.IsForbidden and not p:IsForbidden() do
l, p = l-1, p:GetParent()
end
if p == t then
o:GetScript("OnEnter")(o)
end
end
FollowerList:SyncToBoard()
end
local function MissionRewards_OnShow(self)
local MP = CovenantMissionFrame.MissionTab.MissionPage
local mi = MP.missionInfo
local d = mi and mi.duration
MP.Stage.Title:SetText(mi and mi.name or "")
self.Rewards:SetRewards(mi and mi.xp, mi and mi.rewards)
self.Duration:SetText(d and L"Duration:" .. " |cffffffff" .. d or "")
local xp = mi and mi.xp or 0
for i=1,mi and mi.rewards and #mi.rewards or 0 do
local r = mi.rewards[i]
if r.followerXP then
xp = xp + r.followerXP
end
end
self.xpGain = xp
if FollowerList then
FollowerList:SyncXPGain(xp)
end
end
local function HealAllButton_OnUpdate(self, elapsed)
local tl = (self.timeLeft or 0) - elapsed
if tl > 0 then
self.timeLeft = tl
return
end
self.timeLeft = 0.125
local ff = CovenantMissionFrameFollowers
if ff:GetScript("OnUpdate") and not ff:IsShown() then
local m = ff.followers and "CalculateHealAllFollowersCost" or "OnShow"
ff[m](ff)
end
end
local function MissionView_OnShow()
if not FollowerList then
FollowerList = T.CreateObject("FollowerList", CovenantMissionFrame)
FollowerList:ClearAllPoints()
FollowerList:SetPoint("BOTTOM", CovenantMissionFrameFollowers, "BOTTOM", 0, 8)
end
FollowerList:Refresh(MissionRewards and MissionRewards.xpGain)
FollowerList:Show()
CovenantMissionFrameFollowers:Hide()
CovenantMissionFrameFollowers.MaterialFrame:SetParent(FollowerList)
CovenantMissionFrameFollowers.HealAllButton:SetParent(FollowerList)
end
local function MissionView_OnHide()
if FollowerList then
FollowerList:Hide()
end
CovenantMissionFrameFollowers:Show()
CovenantMissionFrameFollowers.MaterialFrame:SetParent(CovenantMissionFrameFollowers)
CovenantMissionFrameFollowers.HealAllButton:SetParent(CovenantMissionFrameFollowers)
end
local function MissionView_GetGroup()
local g, hc, zh = {}, false, false
local f = CovenantMissionFrame.MissionTab.MissionPage.Board.framesByBoardIndex
for i=0,5 do
local fi = f[i]
if fi and fi.name and fi.info and fi:IsShown() then
g[i], hc = fi.info.followerID, hc or not fi.info.isAutoTroop
if not (zh or fi.info.isAutoTroop) then
local cs = C_Garrison.GetFollowerAutoCombatStats(g[i])
zh = (cs and cs.currentHealth or 0) == 0
end
end
end
return g, hc, zh
end
local function MissionPage_OnClick(self, button)
if button == "RightButton" then
local mid = CovenantMissionFrame.MissionTab.MissionPage.missionInfo.missionID
local g, hc = MissionView_GetGroup()
if mid and hc then
U.StoreMissionGroup(mid, g, true)
end
end
GarrisonMissionPage_OnClick(self, button)
end
local function MissionPageFollower_OnMouseUp(self, frame, button)
if button == "RightButton" and not (frame.GetInfo and frame:GetInfo() or frame.info) then
return MissionPage_OnClick(self:GetMissionPage(), button)
end
return CovenantMissionFrame.OnMouseUpMissionFollower(self, frame, button)
end
local function MissionStart_OnClick(_self, button)
local mid = CovenantMissionFrame.MissionTab.MissionPage.missionInfo.missionID
local g, hc, zh = MissionView_GetGroup()
if not hc then
return
elseif button == "RightButton" and not zh then
U.StartMissionWithDelay(mid, g)
else
U.StoreMissionGroup(mid, g, true)
PlaySound(39514)
end
CovenantMissionFrame:CloseMission()
end
local function MissionStart_OnEnter(self)
if self:IsEnabled() then
local send = NORMAL_FONT_COLOR_CODE .. L"Send Tentative Parties" .. "|r"
GameTooltip:SetOwner(self, "ANCHOR_TOP", 0, 3)
GameTooltip:SetText(L"Assign Tentative Party")
GameTooltip:AddLine((L"Tentative parties can be changed until you click %s."):format(send), 1,1,1,1)
if select(3, MissionView_GetGroup()) then
GameTooltip:AddLine("|n|cffff8000" .. COVENANT_MISSIONS_COMPANIONS_MISSING_HEALTH, 1, 0.5, 0)
else
GameTooltip:AddLine("|n|TInterface/TUTORIALFRAME/UI-TUTORIAL-FRAME:14:12:0:-1:512:512:10:70:330:410|t " .. L"Start the adventure", 0.5, 0.8, 1)
end
GameTooltip:Show()
elseif self.tooltip then
GameTooltip:SetOwner(self, "ANCHOR_TOP", 0, 3);
GameTooltip:SetText(self.tooltip, RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b, RED_FONT_COLOR.a, true);
GameTooltip:Show();
end
end
local function Shuffler_OnEnter(self, source)
GameTooltip:Hide()
GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT")
GameTooltip:SetText(ITEM_QUALITY_COLORS[5].hex .. L"Cursed Tactical Guide")
local sb = T.CreateObject("SharedTooltipProgressBar")
sb:Hide()
local isRunning, a1, a2, a3, a4 = Tact:IsRunning()
if isRunning then
local nc = NORMAL_FONT_COLOR
GameTooltip:AddDoubleLine(L"Futures considered:", BreakUpLargeNumbers(a3), 1,1,1, nc.r, nc.g, nc.b)
if a4 then
GameTooltip:AddLine(L"Use: Interrupt the guide's deliberations.", 0, 1, 0, 1)
end
sb:Activate(GameTooltip, a1, a2)
return
elseif (a1 and not a2) or source == "update" then -- finished, no group
GameTooltip:AddLine(L"Victory could not be guaranteed.", 1,1,1)
else -- not running, not finished
GameTooltip:AddLine(ITEM_UNIQUE, 1,1,1, 1)
GameTooltip:AddLine(L"Use: Let the book select troops and battle tactics.", 0, 1, 0, 1)
local c = a2 and WHITE_FONT_COLOR or RED_FONT_COLOR -- can start?
GameTooltip:AddLine(L"Requires a companion in the party", c.r, c.g, c.b)
GameTooltip:AddLine(L'"Chapter 1: Mages Must Melee."', 1, 0.835, 0.09, 1)
end
GameTooltip:Show()
end
local function Shuffler_OnLeave(self)
if GameTooltip:IsOwned(self) then
GameTooltip:Hide()
end
end
local function Shuffler_AssignGroup(g)
local f = CovenantMissionFrame.MissionTab.MissionPage.Board.framesByBoardIndex
for i=0,4 do
if f[i].info then
CovenantMissionFrame:RemoveFollowerFromMission(f[i], true)
end
end
for slot, ii in pairs(g) do
CovenantMissionFrame:AssignFollowerToMission(f[slot], GetFollowerInfo(ii.id))
end
end
local function Shuffler_OnUpdate(self)
local fin, g = Tact:Run()
if fin then
self:SetScript("OnUpdate", nil)
if g then
Shuffler_AssignGroup(g)
Shuffler_OnLeave(self)
return
end
end
if GameTooltip:IsOwned(self) then
Shuffler_OnEnter(self, "update")
end
end
local function Shuffler_OnClick(self)
local ir, _, _, hg = Tact:IsRunning()
if ir then
local g = hg and Tact:Interrupt()
if g then
Shuffler_AssignGroup(g)
end
elseif Tact:Start() then
self:SetScript("OnUpdate", Shuffler_OnUpdate)
Shuffler_OnUpdate(self)
end
PlaySound(SOUNDKIT.U_CHAT_SCROLL_BUTTON)
end
local function Shuffler_OnHide()
Tact:Reset()
end
local function HookOnShow(f, h)
f:HookScript("OnShow", h)
if f:IsVisible() then
h(f)
end
end
function EV:I_ADVENTURES_UI_LOADED()
local MP = CovenantMissionFrame.MissionTab.MissionPage
for i=0,12 do
local f = MP.Board.framesByBoardIndex[i]
f:SetScript("OnEnter", Puck_OnEnter)
f:SetScript("OnLeave", Puck_OnLeave)
for i=1,2 do
f.AbilityButtons[i]:EnableMouse(false)
f.AbilityButtons[i]:SetMouseMotionEnabled(false)
end
end
MP.CloseButton:SetScript("OnKeyDown", function(self, key)
self:SetPropagateKeyboardInput(key ~= "ESCAPE")
if key == "ESCAPE" then
self:Click()
end
end)
local cag = T.CreateObject("IconButton", MP.Board, 64, "Interface/Icons/INV_Misc_Book_01")
cag:RegisterForClicks("LeftButtonUp", "RightButtonUp")
cag:SetPoint("BOTTOMLEFT", 24, 4)
cag:SetScript("OnEnter", Predictor_OnEnter)
cag:SetScript("OnLeave", Predictor_OnLeave)
cag:SetScript("OnClick", Predictor_OnClick)
local cat = T.CreateObject("IconButton", MP.Board, 32, "Interface/Icons/INV_Misc_Book_06")
cat:RegisterForClicks("LeftButtonUp", "RightButtonUp")
cat:SetPoint("TOPLEFT", cag, "TOPRIGHT", 4, 0)
cat:SetScript("OnEnter", Shuffler_OnEnter)
cat:SetScript("OnLeave", Shuffler_OnLeave)
cat:SetScript("OnClick", Shuffler_OnClick)
cat:SetScript("OnHide", Shuffler_OnHide)
MP.Stage.EnvironmentEffectFrame:SetScript("OnEnter", EnvironmentEffect_OnEnter)
MP.Stage.EnvironmentEffectFrame:SetScript("OnLeave", EnvironmentEffect_OnLeave)
hooksecurefunc(MP.Stage.EnvironmentEffectFrame.Name, "SetText", EnvironmentEffect_OnNameUpdate)
hooksecurefunc(CovenantMissionFrame, "AssignFollowerToMission", MissionGroup_OnUpdate)
hooksecurefunc(CovenantMissionFrame, "RemoveFollowerFromMission", MissionGroup_OnUpdate)
MP:SetScript("OnClick", MissionPage_OnClick)
HookOnShow(CovenantMissionFrame, function()
CovenantMissionFrame:RegisterCallback(CovenantMission.Event.OnFollowerFrameMouseUp, MissionPageFollower_OnMouseUp, CovenantMissionFrame)
end)
MP.StartMissionButton:SetScript("OnClick", MissionStart_OnClick)
MP.StartMissionButton:SetScript("OnEnter", MissionStart_OnEnter)
MP.StartMissionButton:RegisterForClicks("LeftButtonUp", "RightButtonUp")
MP.StartMissionButton:SetText(L"Assign Party")
CovenantMissionFrame.GetSystemSpecificStartMissionFailureMessage = function() end
local s = CovenantMissionFrame.MissionTab.MissionPage.Stage
s.Title:SetPoint("LEFT", s.Header, "LEFT", 100, 9)
local ir = T.CreateObject("InlineRewardBlock", s.MouseOverTitleFrame)
MissionRewards = ir
ir:SetPoint("LEFT", s.Header, "LEFT", 100, -16)
ir.Duration = ir:CreateFontString(nil, "OVERLAY", "GameFontNormal")
ir.Duration:SetPoint("LEFT", ir, "RIGHT", 4, 0)
hooksecurefunc(CovenantMissionFrame, "SetTitle", function()
MissionRewards_OnShow(ir)
end)
hooksecurefunc(CovenantMissionFrame:GetMissionPage(), "Show", MissionView_OnShow)
MP.Board:HookScript("OnHide", MissionView_OnHide)
MP.Board:HookScript("OnShow", MissionView_OnShow)
hooksecurefunc(CovenantMissionFrameFollowers, "UpdateFollowers", function()
if MP.Board:IsVisible() and not (S[T.MissionList] and S[T.MissionList]:IsVisible()) then
MissionView_OnShow()
end
end)
local h = CreateFrame("Frame", nil, CovenantMissionFrameFollowers.HealAllButton)
h:SetScript("OnUpdate", HealAllButton_OnUpdate)
CovenantMissionFrameFollowers:HookScript("OnShow", function() h:Hide() end)
CovenantMissionFrameFollowers:HookScript("OnHide", function() h:Show() end)
MP.Stage.Title:SetWidth(320)
return "remove"
end