08b515afd9
EN: 2.0.0.143 CN: 2.0.0.142 JP: 2.0.0.145 KR: 2.0.0.150
1098 lines
28 KiB
Lua
1098 lines
28 KiB
Lua
local TimerManager = require("GameCore.Timer.TimerManager")
|
|
local GuideStepRunner = {}
|
|
GuideStepRunner.__index = GuideStepRunner
|
|
local nDefaultSystemTimeout = 3
|
|
local nPlayerSkipDelay = 20
|
|
local nEventPollInterval = 0.1
|
|
local Guard = function(fn, ...)
|
|
if fn == nil then
|
|
return true
|
|
end
|
|
return xpcall(fn, debug.traceback, ...)
|
|
end
|
|
function GuideStepRunner.Start(objParent, objGroup)
|
|
local obj = setmetatable({}, GuideStepRunner)
|
|
objParent._objStepRunner = obj
|
|
obj:_Start(objParent, objGroup)
|
|
return obj
|
|
end
|
|
function GuideStepRunner:_Start(objParent, objGroup)
|
|
self._objParent = objParent
|
|
self._objGroup = objGroup
|
|
self._nGroupId = objGroup.nGroupId
|
|
self._nGuideType = objGroup.nGuideType or AllEnum.GuideType.Dorm
|
|
self._bNeedCheckpoint = objGroup.bNeedCheckpoint == true
|
|
self._nGroupGen = 1
|
|
self._nStepGen = 0
|
|
self._nPresentGen = 0
|
|
self._nConfirmGen = 0
|
|
self._bStopped = false
|
|
self._bExiting = false
|
|
self._bReported = false
|
|
self._bInTick = false
|
|
self._bDirty = false
|
|
self._tbListen = {}
|
|
self._mapCtx = {
|
|
mapData = {},
|
|
nGroupId = self._nGroupId
|
|
}
|
|
function self._mapCtx.Set(mapCtx, sKey, anyVal)
|
|
mapCtx.mapData[sKey] = anyVal
|
|
end
|
|
function self._mapCtx.Get(mapCtx, sKey)
|
|
return mapCtx.mapData[sKey]
|
|
end
|
|
local ok, tbSteps = Guard(objGroup.GetSteps, objGroup)
|
|
if not ok or type(tbSteps) ~= "table" or #tbSteps == 0 then
|
|
printError("[DormGuide] GetSteps 失败 group=" .. tostring(self._nGroupId))
|
|
self:Stop("abort", false)
|
|
return
|
|
end
|
|
self._tbSteps = tbSteps
|
|
self._nIndex = 1
|
|
if objGroup.OnStart ~= nil then
|
|
local bOk = Guard(objGroup.OnStart, objGroup, self._mapCtx)
|
|
if not bOk or self._bStopped then
|
|
if not self._bStopped then
|
|
self:Stop("abort", false)
|
|
end
|
|
return
|
|
end
|
|
end
|
|
self:_BeginStep()
|
|
end
|
|
function GuideStepRunner:_Alive(nGroup, nStep, nPresent)
|
|
if self._bStopped or self._bExiting then
|
|
return false
|
|
end
|
|
if nGroup ~= nil and nGroup ~= self._nGroupGen then
|
|
return false
|
|
end
|
|
if nStep ~= nil and nStep ~= self._nStepGen then
|
|
return false
|
|
end
|
|
if nPresent ~= nil and nPresent ~= self._nPresentGen then
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
function GuideStepRunner:_Call(fn, ...)
|
|
local ok, a, b = Guard(fn, ...)
|
|
if not ok then
|
|
printError("[DormGuide] group=" .. tostring(self._nGroupId) .. " step=" .. tostring(self._nIndex) .. " phase=" .. tostring(self._sPhase) .. "\n" .. tostring(a))
|
|
return false
|
|
end
|
|
return true, a, b
|
|
end
|
|
function GuideStepRunner:_CancelTick()
|
|
if self._objTick ~= nil then
|
|
TimerManager.Remove(self._objTick)
|
|
self._objTick = nil
|
|
end
|
|
end
|
|
function GuideStepRunner:_ArmTick(nInterval, fn)
|
|
if self._bStopped or self._bCompleting and fn == nil then
|
|
return
|
|
end
|
|
self:_CancelTick()
|
|
local nGroup = self._nGroupGen
|
|
local nStep = self._nStepGen
|
|
local timer
|
|
timer = TimerManager.Add(1, nInterval, self, function()
|
|
timer:Cancel(false)
|
|
if self._objTick == timer then
|
|
self._objTick = nil
|
|
end
|
|
if self._bStopped or self._nGroupGen ~= nGroup or self._nStepGen ~= nStep then
|
|
return
|
|
end
|
|
if fn ~= nil then
|
|
fn()
|
|
else
|
|
self:_Tick()
|
|
end
|
|
end, true, true, true, nil)
|
|
self._objTick = timer
|
|
end
|
|
function GuideStepRunner:_Schedule(fn)
|
|
self:_ArmTick(0, fn)
|
|
end
|
|
function GuideStepRunner:_Wake()
|
|
if self._bStopped or self._bExiting or self._bCompleting or self._bActionRunning then
|
|
return
|
|
end
|
|
if self._bInTick then
|
|
self._bDirty = true
|
|
return
|
|
end
|
|
self:_Tick()
|
|
end
|
|
function GuideStepRunner:_UnbindStep()
|
|
for sName, fn in pairs(self._tbListen) do
|
|
EventManager.Remove(sName, self, fn)
|
|
end
|
|
self._tbListen = {}
|
|
end
|
|
function GuideStepRunner:_Bind(sName)
|
|
if sName == nil or self._tbListen[sName] ~= nil then
|
|
return
|
|
end
|
|
local nGroup, nStep = self._nGroupGen, self._nStepGen
|
|
local fn = function(_, ...)
|
|
if self:_Alive(nGroup, nStep) then
|
|
self:_OnEvent(sName, ...)
|
|
end
|
|
end
|
|
self._tbListen[sName] = fn
|
|
EventManager.Add(sName, self, fn)
|
|
end
|
|
function GuideStepRunner:_BindList(tbName)
|
|
if tbName == nil then
|
|
return
|
|
end
|
|
for _, sName in ipairs(tbName) do
|
|
self:_Bind(sName)
|
|
end
|
|
end
|
|
function GuideStepRunner:_OnEvent(sName, ...)
|
|
if self._bStopped or self._bExiting then
|
|
return
|
|
end
|
|
local step = self._mapStep
|
|
if step == nil then
|
|
return
|
|
end
|
|
local bCare = false
|
|
local tbArg = table.pack(...)
|
|
local Match = function(mapWait)
|
|
if mapWait == nil or mapWait.Events == nil then
|
|
return false
|
|
end
|
|
for _, sWait in ipairs(mapWait.Events) do
|
|
if sWait == sName then
|
|
if mapWait.Match ~= nil then
|
|
local ok, bHit = self:_Call(mapWait.Match, self._mapCtx, table.unpack(tbArg, 1, tbArg.n))
|
|
return ok and bHit == true
|
|
end
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
if Match(step.WaitTrigger) or Match(step.Present) or Match(step.End) or Match(step.Prepare) or Match(step.Start) then
|
|
bCare = true
|
|
end
|
|
if step.tbHint ~= nil then
|
|
for _, sHint in ipairs(step.tbHint) do
|
|
if sHint == sName then
|
|
bCare = true
|
|
end
|
|
end
|
|
end
|
|
if not bCare then
|
|
return
|
|
end
|
|
self:_Wake()
|
|
end
|
|
function GuideStepRunner:_Now()
|
|
return TimerManager.GetUnscaledTime()
|
|
end
|
|
function GuideStepRunner:_Frozen()
|
|
if self._objParent ~= nil and self._objParent.IsRunnerBudgetFrozen ~= nil then
|
|
return self._objParent:IsRunnerBudgetFrozen()
|
|
end
|
|
return false
|
|
end
|
|
function GuideStepRunner:_TimeoutReached(nStart, nLimit)
|
|
if nLimit == nil or nLimit <= 0 then
|
|
return false
|
|
end
|
|
if not self:_IsPlayerWait() and self:_Frozen() then
|
|
if not self._bFrozen then
|
|
self._bFrozen = true
|
|
self._nFreezeAt = self:_Now()
|
|
end
|
|
return false
|
|
end
|
|
if self._bFrozen then
|
|
self._nStartShift = (self._nStartShift or 0) + (self:_Now() - (self._nFreezeAt or self:_Now()))
|
|
self._bFrozen = false
|
|
end
|
|
local nStartUse = nStart + (self._nStartShift or 0)
|
|
return nLimit <= self:_Now() - nStartUse
|
|
end
|
|
function GuideStepRunner:_SetPhase(sPhase)
|
|
if self._sPhase == sPhase then
|
|
return
|
|
end
|
|
if not self._bCompleting then
|
|
self:_CancelTick()
|
|
end
|
|
self._sPhase = sPhase
|
|
self._nConfirmGen = self._nConfirmGen + 1
|
|
self._bSkipAsking = false
|
|
self._objParent:CloseRunnerSkip()
|
|
self._nMiss = 0
|
|
self:_ResetBudget()
|
|
end
|
|
function GuideStepRunner:_IsPlayerWait()
|
|
local step = self._mapStep
|
|
if step == nil then
|
|
return false
|
|
end
|
|
if self._sPhase == "trigger" then
|
|
return step.WaitTrigger ~= nil and step.WaitTrigger.bPlayerWait == true
|
|
end
|
|
if self._sPhase ~= "end" then
|
|
return false
|
|
end
|
|
local sKind = step.Present ~= nil and step.Present.sKind or "None"
|
|
if sKind == "Mask" then
|
|
return not self._bClickUsed
|
|
end
|
|
return step.bPlayerWait == true
|
|
end
|
|
function GuideStepRunner:_NextMissInterval()
|
|
self._nMiss = (self._nMiss or 0) + 1
|
|
if self._nMiss <= 3 then
|
|
return 0
|
|
end
|
|
local nInterval = 0.05
|
|
local nPow = self._nMiss - 3
|
|
for _ = 2, nPow do
|
|
nInterval = nInterval * 2
|
|
if nInterval >= nEventPollInterval then
|
|
return nEventPollInterval
|
|
end
|
|
end
|
|
return nInterval
|
|
end
|
|
function GuideStepRunner:_HoldForEvent(mapWait)
|
|
local nInterval = nEventPollInterval
|
|
if mapWait == nil then
|
|
nInterval = math.min(self:_NextMissInterval(), nEventPollInterval)
|
|
elseif mapWait.Interval ~= nil then
|
|
nInterval = mapWait.Interval
|
|
end
|
|
self:_ArmTick(nInterval, nil)
|
|
end
|
|
function GuideStepRunner:_ResetBudget()
|
|
self._nBudgetStart = self:_Now()
|
|
self._nStartShift = 0
|
|
self._bFrozen = false
|
|
self._bSkipAvailable = false
|
|
if self._objParent ~= nil then
|
|
self._objParent:SetRunnerSkipVisible(false)
|
|
end
|
|
end
|
|
function GuideStepRunner:_BudgetHit(nLimit)
|
|
if self:_IsPlayerWait() then
|
|
if not self._bSkipAvailable and self:_TimeoutReached(self._nBudgetStart or self:_Now(), nPlayerSkipDelay) then
|
|
self._bSkipAvailable = true
|
|
self._objParent:SetRunnerSkipVisible(true)
|
|
end
|
|
return false
|
|
end
|
|
return self:_TimeoutReached(self._nBudgetStart or self:_Now(), nLimit)
|
|
end
|
|
function GuideStepRunner:_ApplyPolicy(sStage)
|
|
local step = self._mapStep
|
|
if step == nil or step.InputPolicy == nil or self._objParent == nil then
|
|
return
|
|
end
|
|
local mapPolicy = step.InputPolicy
|
|
if type(mapPolicy) == "function" then
|
|
local ok, mapRet = self:_Call(mapPolicy, self._mapCtx, sStage)
|
|
mapPolicy = ok and mapRet or nil
|
|
elseif mapPolicy.enter ~= nil or mapPolicy.click ~= nil or mapPolicy.present ~= nil or mapPolicy.request ~= nil then
|
|
mapPolicy = mapPolicy[sStage]
|
|
end
|
|
if mapPolicy ~= nil and self._objParent.ApplyRunnerPolicy ~= nil then
|
|
self._objParent:ApplyRunnerPolicy(mapPolicy)
|
|
end
|
|
end
|
|
function GuideStepRunner:_Decide()
|
|
local step = self._mapStep
|
|
if step.ShouldRun == nil then
|
|
return "run"
|
|
end
|
|
local ok, sRet = self:_Call(step.ShouldRun, self._mapCtx)
|
|
if not ok then
|
|
return "abort"
|
|
end
|
|
if sRet ~= "run" and sRet ~= "skip" and sRet ~= "abort" and sRet ~= "finish" then
|
|
return "run"
|
|
end
|
|
return sRet
|
|
end
|
|
function GuideStepRunner:_LeaveByDecide(sDecide)
|
|
if sDecide == "skip" then
|
|
self:_FinishStep("skip")
|
|
return true
|
|
end
|
|
if sDecide == "abort" then
|
|
self:_FinishStep("abort")
|
|
return true
|
|
end
|
|
if sDecide == "finish" then
|
|
self:_FinishStep("finish")
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
function GuideStepRunner:_CacheValid()
|
|
local tr = self._trCache
|
|
if tr == nil or tr == false then
|
|
return false
|
|
end
|
|
if tr.gameObject == nil then
|
|
return false
|
|
end
|
|
local step = self._mapStep
|
|
if step.CacheValid ~= nil then
|
|
local ok, bOk = self:_Call(step.CacheValid, self._mapCtx, tr, self._mapCacheId)
|
|
if not ok or bOk ~= true then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
function GuideStepRunner:_DropCache()
|
|
self._trCache = nil
|
|
self._mapCacheId = nil
|
|
end
|
|
function GuideStepRunner:_StorePoll(anyRet)
|
|
if anyRet == false then
|
|
return "fail"
|
|
end
|
|
if anyRet == nil then
|
|
self:_DropCache()
|
|
return "miss"
|
|
end
|
|
self._trCache = anyRet
|
|
local step = self._mapStep
|
|
if step.CacheId ~= nil then
|
|
local ok, mapId = self:_Call(step.CacheId, self._mapCtx, anyRet)
|
|
self._mapCacheId = ok and mapId or nil
|
|
else
|
|
self._mapCacheId = {
|
|
nInst = anyRet.gameObject ~= nil and anyRet.gameObject:GetInstanceID() or 0
|
|
}
|
|
end
|
|
if not self:_CacheValid() then
|
|
self:_DropCache()
|
|
return "miss"
|
|
end
|
|
return "hit"
|
|
end
|
|
function GuideStepRunner:_PollOnce()
|
|
local step = self._mapStep
|
|
if step.Poll == nil then
|
|
return "none"
|
|
end
|
|
local ok, anyRet = self:_Call(step.Poll, self._mapCtx)
|
|
if not ok then
|
|
return "fail"
|
|
end
|
|
return self:_StorePoll(anyRet)
|
|
end
|
|
function GuideStepRunner:_BeginStep()
|
|
if self._bStopped then
|
|
return
|
|
end
|
|
if self._nIndex > #self._tbSteps then
|
|
local bReport = not self._bNeedCheckpoint or self._bReported
|
|
self:Stop("finish", bReport)
|
|
return
|
|
end
|
|
self._bExiting = false
|
|
self._bCompleting = false
|
|
self._nStepGen = self._nStepGen + 1
|
|
self._nPresentGen = 0
|
|
self._nOpId = 0
|
|
self._bPreFindDone = false
|
|
self._bEntered = false
|
|
self._bPrepared = false
|
|
self._bPreparing = false
|
|
self._sPrepareVer = nil
|
|
self._bPresented = false
|
|
self._bStepExited = false
|
|
self._bActionRunning = false
|
|
self._bSkipAsking = false
|
|
self._bClickUsed = false
|
|
self._nMiss = 0
|
|
self:_DropCache()
|
|
self:_ResetBudget()
|
|
self._mapStep = self._tbSteps[self._nIndex]
|
|
local mapEnd = self._mapStep.End
|
|
if mapEnd ~= nil and (mapEnd.sMode == "Event" or mapEnd.sMode == "Condition") and type(mapEnd.Ready) ~= "function" then
|
|
printError("[DormGuide] End.Ready 未配置 group=" .. tostring(self._nGroupId) .. " step=" .. tostring(self._nIndex))
|
|
self:Stop("abort", false)
|
|
return
|
|
end
|
|
self._sPhase = "decide"
|
|
local step = self._mapStep
|
|
self:_UnbindStep()
|
|
self:_BindList(step.tbHint)
|
|
if step.WaitTrigger ~= nil then
|
|
self:_BindList(step.WaitTrigger.Events)
|
|
end
|
|
if step.Present ~= nil then
|
|
self:_BindList(step.Present.Events)
|
|
end
|
|
if step.End ~= nil then
|
|
self:_BindList(step.End.Events)
|
|
end
|
|
if step.Prepare ~= nil then
|
|
self:_BindList(step.Prepare.Events)
|
|
end
|
|
if step.Start ~= nil then
|
|
self:_BindList(step.Start.Events)
|
|
self._sPhase = "start"
|
|
print("[DormGuide] " .. tostring(self._nGroupId) .. " wait start " .. tostring(step.sName))
|
|
else
|
|
print("[DormGuide] " .. tostring(self._nGroupId) .. " step " .. tostring(self._nIndex) .. " " .. tostring(step.sName))
|
|
end
|
|
self:_Wake()
|
|
end
|
|
function GuideStepRunner:_SystemLimit(mapWait)
|
|
if mapWait ~= nil and mapWait.Timeout ~= nil then
|
|
return mapWait.Timeout
|
|
end
|
|
local step = self._mapStep
|
|
if step ~= nil and step.nSystemTimeout ~= nil then
|
|
return step.nSystemTimeout
|
|
end
|
|
return nDefaultSystemTimeout
|
|
end
|
|
function GuideStepRunner:_OnWaitTimeout(mapWait)
|
|
printError("[DormGuide] timeout group=" .. tostring(self._nGroupId) .. " step=" .. tostring(self._nIndex) .. " name=" .. tostring(self._mapStep.sName) .. " phase=" .. tostring(self._sPhase) .. " limit=" .. tostring(self:_SystemLimit(mapWait)) .. " reported=" .. tostring(self._bReported))
|
|
self:_FinishStep("abort")
|
|
end
|
|
function GuideStepRunner:_Tick()
|
|
if not self:_Alive() or self._bCompleting then
|
|
return
|
|
end
|
|
self._bInTick = true
|
|
local nGuard = 0
|
|
while self:_Alive() and not self._bCompleting and nGuard < 16 do
|
|
nGuard = nGuard + 1
|
|
local sBefore = self._sPhase
|
|
local ok, bWait = self:_Call(self._RunPhase, self)
|
|
if not ok and self:_Alive() then
|
|
self:_FinishStep("abort")
|
|
end
|
|
if not self:_Alive() or bWait or self._sPhase == sBefore then
|
|
break
|
|
end
|
|
end
|
|
self._bInTick = false
|
|
if 16 <= nGuard and self:_Alive() and not self._bCompleting then
|
|
self._bDirty = true
|
|
end
|
|
if self._bDirty and self:_Alive() then
|
|
self._bDirty = false
|
|
if not self._bCompleting then
|
|
self:_Schedule()
|
|
end
|
|
end
|
|
end
|
|
function GuideStepRunner:_RunPhase()
|
|
local sPhase = self._sPhase
|
|
if sPhase == "decide" or sPhase == "show" then
|
|
if self:_LeaveByDecide(self:_Decide()) then
|
|
return true
|
|
end
|
|
if sPhase == "decide" then
|
|
if self._mapStep.Poll ~= nil and not self._bPreFindDone then
|
|
self._sPhase = "prefind"
|
|
else
|
|
self._sPhase = "enter"
|
|
end
|
|
else
|
|
self:_SetPhase("presentReady")
|
|
end
|
|
return false
|
|
end
|
|
if sPhase == "prefind" then
|
|
self._bPreFindDone = true
|
|
local sRet = self:_PollOnce()
|
|
if sRet == "fail" then
|
|
self:_OnFail("poll")
|
|
return true
|
|
end
|
|
self._sPhase = "enter"
|
|
return false
|
|
end
|
|
if sPhase == "enter" then
|
|
if not self._bEntered then
|
|
self:_ApplyPolicy("enter")
|
|
end
|
|
if not self._bEntered and self._mapStep.OnEnter ~= nil then
|
|
local ok = self:_Call(self._mapStep.OnEnter, self._mapCtx)
|
|
if not ok or not self:_Alive() then
|
|
if self:_Alive() then
|
|
self:_FinishStep("abort")
|
|
end
|
|
return true
|
|
end
|
|
end
|
|
self._bEntered = true
|
|
if self._mapStep.Prepare ~= nil then
|
|
self:_SetPhase("prepare")
|
|
else
|
|
self:_SetPhase("trigger")
|
|
end
|
|
return false
|
|
end
|
|
if sPhase == "prepare" then
|
|
return self:_RunPrepare()
|
|
end
|
|
if sPhase == "start" then
|
|
return self:_RunStart()
|
|
end
|
|
if sPhase == "trigger" then
|
|
return self:_RunTrigger()
|
|
end
|
|
if sPhase == "find" then
|
|
return self:_RunFind()
|
|
end
|
|
if sPhase == "presentReady" then
|
|
return self:_RunPresentReady()
|
|
end
|
|
if sPhase == "present" then
|
|
return self:_RunPresent()
|
|
end
|
|
if sPhase == "end" or sPhase == "request" then
|
|
return self:_RunEnd()
|
|
end
|
|
return true
|
|
end
|
|
function GuideStepRunner:_RunStart()
|
|
local mapStart = self._mapStep.Start
|
|
local bReady = true
|
|
if mapStart ~= nil and mapStart.Ready ~= nil then
|
|
local ok, bRet = self:_Call(mapStart.Ready, self._mapCtx)
|
|
if not ok then
|
|
self:_FinishStep("abort")
|
|
return true
|
|
end
|
|
bReady = bRet == true
|
|
end
|
|
if bReady then
|
|
self:_SetPhase("decide")
|
|
print("[DormGuide] " .. tostring(self._nGroupId) .. " step " .. tostring(self._nIndex) .. " " .. tostring(self._mapStep.sName))
|
|
return false
|
|
end
|
|
if self:_BudgetHit(self:_SystemLimit(mapStart)) then
|
|
self:_OnWaitTimeout(mapStart)
|
|
return true
|
|
end
|
|
self:_HoldForEvent(mapStart)
|
|
return true
|
|
end
|
|
function GuideStepRunner:_RunPrepare()
|
|
if self:_LeaveByDecide(self:_Decide()) then
|
|
return true
|
|
end
|
|
local mapPrepare = self._mapStep.Prepare
|
|
if mapPrepare.Ready ~= nil then
|
|
local ok, bReady = self:_Call(mapPrepare.Ready, self._mapCtx)
|
|
if not ok then
|
|
self:_FinishStep("abort")
|
|
return true
|
|
end
|
|
if bReady ~= true then
|
|
if self:_BudgetHit(self:_SystemLimit(mapPrepare)) then
|
|
self:_OnWaitTimeout(mapPrepare)
|
|
return true
|
|
end
|
|
self:_Schedule()
|
|
return true
|
|
end
|
|
end
|
|
if self._bPrepared then
|
|
local sNow
|
|
if mapPrepare.Version ~= nil then
|
|
local bOkVer, anyNow = self:_Call(mapPrepare.Version, self._mapCtx)
|
|
if bOkVer then
|
|
sNow = anyNow
|
|
end
|
|
end
|
|
if mapPrepare.Version == nil or sNow == self._sPrepareVer then
|
|
self:_SetPhase("trigger")
|
|
return false
|
|
end
|
|
self._bPrepared = false
|
|
end
|
|
if self._bPreparing then
|
|
return true
|
|
end
|
|
local sVer
|
|
if mapPrepare.Version ~= nil then
|
|
local ok, anyVer = self:_Call(mapPrepare.Version, self._mapCtx)
|
|
if ok then
|
|
sVer = anyVer
|
|
end
|
|
end
|
|
if self._sPrepareVer ~= nil and sVer == self._sPrepareVer then
|
|
self._bPrepared = true
|
|
self:_SetPhase("trigger")
|
|
return false
|
|
end
|
|
self._bPreparing = true
|
|
local ok, bRan = self:_Call(mapPrepare.Run, self._mapCtx)
|
|
self._bPreparing = false
|
|
if not self:_Alive() then
|
|
return true
|
|
end
|
|
if not ok then
|
|
self:_FinishStep("abort")
|
|
return true
|
|
end
|
|
if bRan == false then
|
|
if self:_BudgetHit(self:_SystemLimit(mapPrepare)) then
|
|
self:_OnWaitTimeout(mapPrepare)
|
|
else
|
|
self:_Schedule()
|
|
end
|
|
return true
|
|
end
|
|
self._bPrepared = true
|
|
self._sPrepareVer = sVer
|
|
if mapPrepare.Version ~= nil then
|
|
local ok2, anyVer = self:_Call(mapPrepare.Version, self._mapCtx)
|
|
if ok2 then
|
|
self._sPrepareVer = anyVer
|
|
end
|
|
end
|
|
self:_SetPhase("trigger")
|
|
return false
|
|
end
|
|
function GuideStepRunner:_RunTrigger()
|
|
if self:_LeaveByDecide(self:_Decide()) then
|
|
return true
|
|
end
|
|
local mapWait = self._mapStep.WaitTrigger
|
|
if mapWait == nil or mapWait.Ready == nil then
|
|
self:_SetPhase("find")
|
|
return false
|
|
end
|
|
local ok, bReady = self:_Call(mapWait.Ready, self._mapCtx)
|
|
if not ok then
|
|
self:_FinishStep("abort")
|
|
return true
|
|
end
|
|
if bReady == true then
|
|
self._nMiss = 0
|
|
self:_SetPhase("find")
|
|
return false
|
|
end
|
|
if self:_BudgetHit(self:_SystemLimit(mapWait)) then
|
|
self:_OnWaitTimeout(mapWait)
|
|
return true
|
|
end
|
|
self:_HoldForEvent(mapWait)
|
|
return true
|
|
end
|
|
function GuideStepRunner:_RunFind()
|
|
if self:_LeaveByDecide(self:_Decide()) then
|
|
return true
|
|
end
|
|
if self._mapStep.Poll == nil then
|
|
self:_SetPhase("show")
|
|
return false
|
|
end
|
|
if self:_CacheValid() then
|
|
self:_SetPhase("show")
|
|
return false
|
|
end
|
|
self:_DropCache()
|
|
local sRet = self:_PollOnce()
|
|
if sRet == "fail" then
|
|
self:_OnFail("poll")
|
|
return true
|
|
end
|
|
if sRet == "hit" then
|
|
self._nMiss = 0
|
|
self:_SetPhase("show")
|
|
return false
|
|
end
|
|
if self:_BudgetHit(self:_SystemLimit(self._mapStep.Find)) then
|
|
self:_OnWaitTimeout(self._mapStep.Find)
|
|
return true
|
|
end
|
|
self:_HoldForEvent(nil)
|
|
return true
|
|
end
|
|
function GuideStepRunner:_RunPresentReady()
|
|
local mapPresent = self._mapStep.Present
|
|
if mapPresent == nil or mapPresent.sKind == "None" or mapPresent.sKind == "Avg" or mapPresent.sKind == "Dict" then
|
|
self:_SetPhase("present")
|
|
return false
|
|
end
|
|
if not self:_CacheValid() then
|
|
self:_DropCache()
|
|
self._bPresented = false
|
|
self:_SetPhase("find")
|
|
return false
|
|
end
|
|
if self:_LeaveByDecide(self:_Decide()) then
|
|
return true
|
|
end
|
|
if not self._bShown then
|
|
if mapPresent.Show ~= nil then
|
|
local ok = self:_Call(mapPresent.Show, self._mapCtx, self._trCache)
|
|
if not ok or not self:_Alive() then
|
|
if self:_Alive() then
|
|
self:_FinishStep("abort")
|
|
end
|
|
return true
|
|
end
|
|
end
|
|
self._bShown = true
|
|
if not self:_CacheValid() then
|
|
self:_DropCache()
|
|
self._bShown = false
|
|
self:_SetPhase("find")
|
|
return false
|
|
end
|
|
end
|
|
if not self._trCache.gameObject.activeInHierarchy then
|
|
if self:_BudgetHit(self:_SystemLimit(mapPresent)) then
|
|
self:_OnWaitTimeout(mapPresent)
|
|
else
|
|
self:_HoldForEvent(mapPresent)
|
|
end
|
|
return true
|
|
end
|
|
if mapPresent.Ready ~= nil then
|
|
local ok, bReady = self:_Call(mapPresent.Ready, self._mapCtx, self._trCache)
|
|
if not ok then
|
|
self:_FinishStep("abort")
|
|
return true
|
|
end
|
|
if bReady ~= true then
|
|
if self:_BudgetHit(self:_SystemLimit(mapPresent)) then
|
|
self:_OnWaitTimeout(mapPresent)
|
|
return true
|
|
end
|
|
self:_Schedule()
|
|
return true
|
|
end
|
|
end
|
|
self:_SetPhase("present")
|
|
return false
|
|
end
|
|
function GuideStepRunner:_RunPresent()
|
|
if self._bPresented then
|
|
self:_SetPhase("end")
|
|
return false
|
|
end
|
|
local mapPresent = self._mapStep.Present or {sKind = "None"}
|
|
local sKind = mapPresent.sKind or "None"
|
|
if sKind == "None" then
|
|
self._bPresented = true
|
|
self:_SetPhase("end")
|
|
return false
|
|
end
|
|
if sKind == "Avg" or sKind == "Dict" then
|
|
self._nPresentGen = self._nPresentGen + 1
|
|
local nGroup, nStep, nPresent = self._nGroupGen, self._nStepGen, self._nPresentGen
|
|
local fnEnd = function(bNatural)
|
|
if not self:_Alive(nGroup, nStep, nPresent) then
|
|
return
|
|
end
|
|
if bNatural ~= true then
|
|
self:_FinishStep("abort")
|
|
return
|
|
end
|
|
self:_RequestComplete("present")
|
|
end
|
|
local ok, bStart
|
|
if sKind == "Avg" then
|
|
ok, bStart = self:_Call(self._objParent.PlayRunnerAvg, self._objParent, mapPresent, fnEnd)
|
|
else
|
|
ok, bStart = self:_Call(self._objParent.PlayRunnerDict, self._objParent, mapPresent, fnEnd)
|
|
end
|
|
if not self:_Alive() then
|
|
return true
|
|
end
|
|
if not ok or bStart ~= true then
|
|
printError("[DormGuide] 呈现启动失败 group=" .. tostring(self._nGroupId) .. " kind=" .. sKind)
|
|
self:_FinishStep("abort")
|
|
return true
|
|
end
|
|
self._bPresented = true
|
|
self:_SetPhase("end")
|
|
self:_ApplyPolicy("present")
|
|
if not self._bCompleting then
|
|
self:_HoldForEvent(mapPresent)
|
|
end
|
|
return true
|
|
end
|
|
if not self:_CacheValid() then
|
|
self:_DropCache()
|
|
self._bShown = false
|
|
self:_SetPhase("find")
|
|
return false
|
|
end
|
|
self._nPresentGen = self._nPresentGen + 1
|
|
self._bClickUsed = false
|
|
local nGroup, nStep, nPresent = self._nGroupGen, self._nStepGen, self._nPresentGen
|
|
local fnClick = function()
|
|
self:_OnClick(nGroup, nStep, nPresent)
|
|
end
|
|
local ok, bShown = self:_Call(self._objParent.PlayStepMask, self._objParent, self._trCache, mapPresent.mapMask or {}, fnClick)
|
|
if not ok or bShown ~= true then
|
|
self:_FinishStep("abort")
|
|
return true
|
|
end
|
|
self._bPresented = true
|
|
self:_SetPhase("end")
|
|
self:_ApplyPolicy("click")
|
|
self:_HoldForEvent(self._mapStep.End)
|
|
return true
|
|
end
|
|
function GuideStepRunner:_OnClick(nGroup, nStep, nPresent)
|
|
if not self:_Alive(nGroup, nStep, nPresent) then
|
|
return
|
|
end
|
|
if self._bClickUsed or self._bCompleting or self._bSkipAsking or self._sPhase == "request" then
|
|
return
|
|
end
|
|
if not self:_CacheValid() then
|
|
self._objParent:ReleaseStepVisual(true)
|
|
self._bPresented = false
|
|
self._bShown = false
|
|
self:_DropCache()
|
|
self:_SetPhase("find")
|
|
self:_Wake()
|
|
return
|
|
end
|
|
if self:_LeaveByDecide(self:_Decide()) then
|
|
return
|
|
end
|
|
self._bClickUsed = true
|
|
self._bActionRunning = true
|
|
self._nPresentGen = self._nPresentGen + 1
|
|
local mapEnd = self._mapStep.End or {sMode = "Click"}
|
|
if mapEnd.sMode ~= "Click" then
|
|
self:_SetPhase("request")
|
|
self:_ApplyPolicy("request")
|
|
end
|
|
self._objParent:ReleaseStepVisual(true)
|
|
local ok, mapRet
|
|
if self._mapStep.Action ~= nil then
|
|
ok, mapRet = self:_Call(self._mapStep.Action, self._mapCtx, self._trCache)
|
|
else
|
|
ok, mapRet = self:_Call(self._objParent.InvokeRunnerTarget, self._objParent, self._trCache)
|
|
end
|
|
self._bActionRunning = false
|
|
if not self:_Alive() then
|
|
return
|
|
end
|
|
if not ok or type(mapRet) ~= "table" then
|
|
self:_FinishStep("abort")
|
|
return
|
|
end
|
|
self._nOpId = mapRet.nOpId or 0
|
|
self._mapCtx:Set("nOpId", self._nOpId)
|
|
if mapRet.sState ~= "accepted" and mapRet.sState ~= "pending" and mapRet.sState ~= "done" then
|
|
self:_FinishStep("abort")
|
|
return
|
|
end
|
|
if mapEnd.sMode == "Click" and (mapRet.sState == "accepted" or mapRet.sState == "done") then
|
|
self:_RequestComplete("click")
|
|
return
|
|
end
|
|
if mapRet.sState == "done" or mapEnd.Ready ~= nil and self:_EndReady() then
|
|
self:_RequestComplete("click")
|
|
return
|
|
end
|
|
if not self:_Alive() then
|
|
return
|
|
end
|
|
self:_SetPhase("request")
|
|
self:_ApplyPolicy("request")
|
|
self:_Schedule()
|
|
end
|
|
function GuideStepRunner:_EndReady()
|
|
local mapEnd = self._mapStep.End
|
|
if mapEnd == nil or mapEnd.Ready == nil then
|
|
return false
|
|
end
|
|
local ok, bReady = self:_Call(mapEnd.Ready, self._mapCtx)
|
|
if not ok then
|
|
self:_FinishStep("abort")
|
|
return false
|
|
end
|
|
return bReady == true
|
|
end
|
|
function GuideStepRunner:_RunEnd()
|
|
local mapEnd = self._mapStep.End
|
|
local mapPresent = self._mapStep.Present or {}
|
|
if mapPresent.sKind == "Mask" and self._sPhase == "end" and not self._bClickUsed and (not self:_CacheValid() or not self._trCache.gameObject.activeInHierarchy) then
|
|
self._objParent:ReleaseStepVisual(true)
|
|
self._bPresented = false
|
|
self._bShown = false
|
|
self:_DropCache()
|
|
self:_SetPhase("find")
|
|
return false
|
|
end
|
|
local bPresentWait = mapPresent.sKind == "Avg" or mapPresent.sKind == "Dict"
|
|
local bWaitClick = mapPresent.sKind == "Mask" and not self._bClickUsed
|
|
if not bPresentWait and not bWaitClick and self:_EndReady() then
|
|
self:_RequestComplete("end")
|
|
return true
|
|
end
|
|
if not self:_Alive() then
|
|
return true
|
|
end
|
|
local mapWait = mapEnd or mapPresent
|
|
if self:_BudgetHit(self:_SystemLimit(mapWait)) then
|
|
self:_OnWaitTimeout(mapWait)
|
|
return true
|
|
end
|
|
self:_HoldForEvent(mapWait)
|
|
return true
|
|
end
|
|
function GuideStepRunner:_RequestComplete(sWhy)
|
|
if self._bStopped or self._bExiting or self._bCompleting then
|
|
return
|
|
end
|
|
self._bCompleting = true
|
|
local nGroup, nStep = self._nGroupGen, self._nStepGen
|
|
self:_Schedule(function()
|
|
if self._bStopped or self._nGroupGen ~= nGroup or self._nStepGen ~= nStep then
|
|
return
|
|
end
|
|
self._bExiting = false
|
|
self:_FinishStep("success")
|
|
end)
|
|
end
|
|
function GuideStepRunner:_OnFail(sWhy)
|
|
local step = self._mapStep
|
|
local sHow = "abort"
|
|
if step ~= nil and step.OnFail ~= nil then
|
|
sHow = step.OnFail
|
|
end
|
|
printError("[DormGuide] fail group=" .. tostring(self._nGroupId) .. " step=" .. tostring(self._nIndex) .. " " .. tostring(sWhy))
|
|
if sHow == "continue" then
|
|
self:_FinishStep("skip")
|
|
return
|
|
end
|
|
self:_FinishStep("abort")
|
|
end
|
|
function GuideStepRunner:AskSkip()
|
|
if not self:_Alive() or self._bCompleting or self._bSkipAsking or not self._bSkipAvailable then
|
|
return
|
|
end
|
|
self._bSkipAsking = true
|
|
self._nConfirmGen = self._nConfirmGen + 1
|
|
local nGroup, nStep, nConfirm = self._nGroupGen, self._nStepGen, self._nConfirmGen
|
|
local IsCurrent = function()
|
|
return self:_Alive(nGroup, nStep) and self._nConfirmGen == nConfirm and not self._bCompleting
|
|
end
|
|
self._objParent:OpenRunnerSkip(nGroup, nStep, nConfirm, function()
|
|
if IsCurrent() then
|
|
self:Stop("jump", true)
|
|
end
|
|
end, function()
|
|
if IsCurrent() then
|
|
self._bSkipAsking = false
|
|
self:_Wake()
|
|
end
|
|
end)
|
|
end
|
|
function GuideStepRunner:_ExitStep(sReason, bKeepBarrier)
|
|
if self._bStepExited then
|
|
return
|
|
end
|
|
self._bStepExited = true
|
|
self:_CancelTick()
|
|
self:_UnbindStep()
|
|
self._nConfirmGen = self._nConfirmGen + 1
|
|
if self._objParent ~= nil then
|
|
self._objParent:ReleaseStepVisual(bKeepBarrier)
|
|
self._objParent:CancelRunnerPresent()
|
|
self._objParent:CloseRunnerSkip()
|
|
self._objParent:SetRunnerSkipVisible(false)
|
|
end
|
|
local step = self._mapStep
|
|
if step ~= nil and step.OnExit ~= nil then
|
|
self:_Call(step.OnExit, self._mapCtx, sReason)
|
|
end
|
|
end
|
|
function GuideStepRunner:_FinishStep(sReason)
|
|
if self._bStopped or self._bExiting then
|
|
return
|
|
end
|
|
self._bExiting = true
|
|
local step = self._mapStep
|
|
if sReason == "success" and step ~= nil and step.bReportDone then
|
|
self:_Report()
|
|
end
|
|
if sReason == "skip" and step ~= nil and step.bReportOnSkip then
|
|
self:_Report()
|
|
end
|
|
if sReason == "finish" then
|
|
self:_Report()
|
|
end
|
|
self:_ExitStep(sReason, sReason ~= "abort" and sReason ~= "finish")
|
|
if self._bStopped then
|
|
return
|
|
end
|
|
if sReason == "abort" then
|
|
self._bExiting = false
|
|
self:Stop("abort", false)
|
|
return
|
|
end
|
|
if sReason == "finish" then
|
|
self._bExiting = false
|
|
self:Stop("finish", false)
|
|
return
|
|
end
|
|
self._nIndex = self._nIndex + 1
|
|
self._bExiting = false
|
|
self._bShown = false
|
|
self:_BeginStep()
|
|
end
|
|
function GuideStepRunner:_Report()
|
|
if self._bReported or self._objParent == nil then
|
|
return
|
|
end
|
|
if self._objParent.isDebugTest then
|
|
self._bReported = true
|
|
return
|
|
end
|
|
self._bReported = true
|
|
self._objParent:SendGuideStep(self._nGroupId, -1, self._nGuideType)
|
|
end
|
|
function GuideStepRunner:Stop(sReason, bReport)
|
|
if self._bStopped then
|
|
return
|
|
end
|
|
self._bStopped = true
|
|
self._bExiting = true
|
|
self._nGroupGen = self._nGroupGen + 1
|
|
self._nPresentGen = self._nPresentGen + 1
|
|
self:_ExitStep(sReason, false)
|
|
local objParent = self._objParent
|
|
if self._objGroup ~= nil and self._objGroup.OnClear ~= nil then
|
|
Guard(self._objGroup.OnClear, self._objGroup, self._mapCtx, sReason)
|
|
end
|
|
if sReason == "jump" or bReport then
|
|
self:_Report()
|
|
end
|
|
if sReason == "abort" and PlayerData.Guide ~= nil and PlayerData.Guide.NoteDormAbort ~= nil then
|
|
PlayerData.Guide:NoteDormAbort(self._nGroupId)
|
|
end
|
|
self._objParent = nil
|
|
if objParent ~= nil then
|
|
objParent._objStepRunner = nil
|
|
objParent:FinishRunnerGroup(sReason == "finish" or sReason == "jump")
|
|
end
|
|
end
|
|
return GuideStepRunner
|