Files
StellaSora_DataLua/lua/game/ui/activity/goldenspy/goldenspyhookctrl.lua
T
SL1900 e0549005e1 Update - 1.9.0.103
EN: 1.9.0.103
CN: 1.9.0.104
JP: 1.9.0.106
KR: 1.9.0.108
2026-04-18 00:15:00 +09:00

607 lines
18 KiB
Lua

local GoldenSpyHookCtrl = class("GoldenSpyHookCtrl", BaseCtrl)
local LoopType = CS.DG.Tweening.LoopType
GoldenSpyHookCtrl._mapNodeConfig = {
trHookPivot = {
sNodeName = "HookPivot",
sComponentName = "RectTransform"
},
trLine = {
sNodeName = "hook_line",
sComponentName = "RectTransform"
},
trHookEnd = {
sNodeName = "hook",
sComponentName = "RectTransform"
},
rtMaxLengthArea = {
sNodeName = "MaxLengthArea",
sComponentName = "RectTransform"
},
itemParent = {
sNodeName = "itemParent",
sComponentName = "RectTransform"
},
hook_open = {},
hook_normal = {},
hook_catched = {}
}
GoldenSpyHookCtrl.DefaultLineSize = 100
GoldenSpyHookCtrl.DefaultHookPosY = -90
GoldenSpyHookCtrl.HOOK_SWING_EASE = Ease.Linear
GoldenSpyHookCtrl.HOOK_EXTEND_EASE = Ease.Linear
GoldenSpyHookCtrl.HOOK_RETRACT_EASE = Ease.Linear
local HookStage = {
STATE_SWINGING = 1,
STATE_EXTENDING = 2,
STATE_RETRACTING = 3
}
function GoldenSpyHookCtrl:Init(levelId, floorId, floorCtrl, levelData, floorData)
self.levelId = levelId
self.floorId = floorId
self.floorCtrl = floorCtrl
self.levelData = levelData
self.floorData = floorData
self:DestroyItemInHook()
local levelCfg = ConfigTable.GetData("GoldenSpyLevel", self.levelId)
local configCfg = ConfigTable.GetData("GoldenSpyConfig", levelCfg.ConfigId)
self.HOOK_SWING_LEFT_ANGLE = -configCfg.MaxAngle
self.HOOK_SWING_RIGHT_ANGLE = configCfg.MaxAngle
self.HOOK_SWING_DURATION = configCfg.AngleTime
self._swingTweener = nil
self._extendTweener = nil
self._retractTweener = nil
self._hookLength = 0
self._hookState = 0
self._onExtendComplete = nil
self._onRetractComplete = nil
self._swingGoingRight = true
self._swingAtRight = false
self.curCatchedItem = nil
self._mapNode.trHookPivot.localEulerAngles = Vector3(0, 0, 0)
self._mapNode.trHookEnd.localPosition = Vector3(0, self.DefaultHookPosY, 0)
self:ApplyHookLength()
self._mapNode.hook_open:SetActive(false)
self._mapNode.hook_normal:SetActive(true)
self._mapNode.hook_catched:SetActive(false)
end
function GoldenSpyHookCtrl:DestroyItemInHook()
local itemParent = self._mapNode.itemParent
if itemParent == nil then
return
end
local nChildCount = itemParent.transform.childCount
for i = 1, nChildCount do
local child = itemParent.transform:GetChild(i - 1)
destroy(child.gameObject)
end
end
function GoldenSpyHookCtrl:ApplyHookLength()
local tr = self._mapNode.trHookEnd
if tr then
tr.localPosition = Vector3(0, -self._hookLength + self.DefaultHookPosY, 0)
end
local rtLine = self._mapNode.trLine
if rtLine and not rtLine:IsNull() then
rtLine.sizeDelta = Vector2(rtLine.sizeDelta.x, self._hookLength + self.DefaultLineSize)
end
end
function GoldenSpyHookCtrl:SetHookLength(length)
self._hookLength = math.max(0, length)
self:ApplyHookLength()
end
function GoldenSpyHookCtrl:_DoSwingPhase(fromAngle, toAngle, onPhaseComplete)
local pivot = self._mapNode.trHookPivot
if pivot == nil or onPhaseComplete == nil then
return
end
self._mapNode.hook_open:SetActive(false)
self._mapNode.hook_normal:SetActive(true)
self._mapNode.hook_catched:SetActive(false)
self._swingGoingRight = toAngle == self.HOOK_SWING_RIGHT_ANGLE
pivot.localEulerAngles = Vector3(0, 0, fromAngle)
local realAngle = math.abs(toAngle - fromAngle)
local realDuration = realAngle / 1.0 / math.abs(self.HOOK_SWING_RIGHT_ANGLE * 2) * self.HOOK_SWING_DURATION
if self._swingTweener ~= nil then
self._swingTweener:Kill()
self._swingTweener = nil
end
self._swingTweener = pivot:DOLocalRotate(Vector3(0, 0, toAngle), realDuration, RotateMode.Fast):SetEase(self.HOOK_SWING_EASE):SetUpdate(true):OnComplete(function()
onPhaseComplete()
end)
end
function GoldenSpyHookCtrl:_ChainSwing()
local pivot = self._mapNode.trHookPivot
if pivot == nil then
return
end
local leftAngle = self.HOOK_SWING_LEFT_ANGLE
local rightAngle = self.HOOK_SWING_RIGHT_ANGLE
if self._swingAtRight then
self._swingGoingRight = false
self:_DoSwingPhase(rightAngle, leftAngle, function()
self._swingAtRight = false
self:_ChainSwing()
end)
else
self._swingGoingRight = true
self:_DoSwingPhase(leftAngle, rightAngle, function()
self._swingAtRight = true
self:_ChainSwing()
end)
end
end
function GoldenSpyHookCtrl:StartSwing(leftAngle, rightAngle)
if self._hookState == HookStage.STATE_EXTENDING or self._hookState == HookStage.STATE_RETRACTING then
return
end
self:StopSwing()
self:StopExtend()
self:StopRetract()
self._mapNode.hook_open:SetActive(false)
self._mapNode.hook_normal:SetActive(true)
self._mapNode.hook_catched:SetActive(false)
local pivot = self._mapNode.trHookPivot
if pivot == nil then
return
end
self._hookLength = 0
self:ApplyHookLength()
self._hookState = HookStage.STATE_SWINGING
leftAngle = leftAngle or self.HOOK_SWING_LEFT_ANGLE
rightAngle = rightAngle or self.HOOK_SWING_RIGHT_ANGLE
self._swingAtRight = false
self._swingGoingRight = true
self:_DoSwingPhase(0, rightAngle, function()
self._swingAtRight = true
self:_ChainSwing()
end)
EventManager.Hit("GoldenSpyHookResumeSwing")
end
function GoldenSpyHookCtrl:PauseSwing()
self:StopSwing()
if self._hookState == HookStage.STATE_SWINGING then
end
end
function GoldenSpyHookCtrl:ResumeSwing(fromCurrent)
local pivot = self._mapNode.trHookPivot
if pivot == nil then
return
end
local leftAngle = self.HOOK_SWING_LEFT_ANGLE
local rightAngle = self.HOOK_SWING_RIGHT_ANGLE
if fromCurrent then
self:StopExtend()
self:StopRetract()
self:StopSwing()
local curZ = self:_NormalizeAngleZ(pivot.localEulerAngles.z)
if self._swingGoingRight then
self._swingAtRight = false
self._swingGoingRight = true
self:_DoSwingPhase(curZ, rightAngle, function()
self._swingAtRight = true
self:_ChainSwing()
end)
else
self._swingAtRight = true
self._swingGoingRight = false
self:_DoSwingPhase(curZ, leftAngle, function()
self._swingAtRight = false
self:_ChainSwing()
end)
end
else
self:StartSwing()
return
end
self._hookState = HookStage.STATE_SWINGING
EventManager.Hit("GoldenSpyHookResumeSwing")
end
function GoldenSpyHookCtrl:_NormalizeAngleZ(z)
while 180 < z do
z = z - 360
end
while z < -180 do
z = z + 360
end
return z
end
function GoldenSpyHookCtrl:StopSwing()
if self._swingTweener ~= nil then
self._swingTweener:Kill()
self._swingTweener = nil
end
end
function GoldenSpyHookCtrl:StartExtend(speed, radius, factor, onComplete, onCatched, onCatchedComplete)
self:StopSwing()
self:StopExtend()
self:StopRetract()
EventManager.Hit("GoldenSpyHookStartExtend")
local tr = self._mapNode.trHookEnd
if tr == nil then
return
end
local maxLength = self:GetCurrentMaxLength()
self._onExtendComplete = onComplete
self._hookState = HookStage.STATE_EXTENDING
local fromLen = self._hookLength
local duration = (maxLength - fromLen) / speed
if duration <= 0 then
self._hookLength = maxLength
self:ApplyHookLength()
self._hookState = 0
if self._onExtendComplete then
self._onExtendComplete()
self._onExtendComplete = nil
end
return
end
self._mapNode.hook_open:SetActive(true)
self._mapNode.hook_normal:SetActive(false)
self._mapNode.hook_catched:SetActive(false)
self._extendTweener = DOTween.To(function()
return self._hookLength
end, function(v)
self._hookLength = v
self:ApplyHookLength()
local items = self.floorCtrl.tbItem
for _, item in ipairs(items) do
local worldPos = self:GetHookEndWorldPosition()
if self:CheckCatchIntersect(worldPos, radius, item.Ctrl) then
if onCatched then
onCatched(item.Ctrl)
end
local itemCfg = item.Ctrl:GetItemCfg()
if itemCfg.ItemType == GameEnum.GoldenSpyItem.Boom then
item.Ctrl:Boom(nil)
self:StartRetract(speed, function()
self:ResumeSwing(true)
if onCatchedComplete then
onCatchedComplete(item.Ctrl)
end
end)
return
end
self.curCatchedItem = item.Ctrl
if itemCfg.ItemType == GameEnum.GoldenSpyItem.Companion or itemCfg.ItemType == GameEnum.GoldenSpyItem.Patrol then
item.Ctrl:onCatch()
end
self.curCatchedItem.gameObject.transform:SetParent(self._mapNode.itemParent)
self.curCatchedItem.gameObject.transform.localPosition = Vector3.zero
local itemNormalWeight = item.Ctrl:GetWeight()
for _, v in ipairs(self:GetBuffData()) do
local buffCfg = ConfigTable.GetData("GoldenSpyBuffCard", v.buffId)
if buffCfg ~= nil and buffCfg.EffectType == GameEnum.GoldenSpyBuffEffect.ReduceItemWeight then
local curFloor = self.levelData:GetCurFloor()
if buffCfg.BuffType == GameEnum.GoldenSpyBuffType.TemporaryBuff then
if v.bActive and table.indexof(v.tbActiveFloor, curFloor) > 0 and itemCfg.ItemType == buffCfg.Params[1] then
itemNormalWeight = itemNormalWeight - buffCfg.Params[2]
end
elseif buffCfg.BuffType == GameEnum.GoldenSpyBuffType.DelayBuff then
if v.bActive and table.indexof(v.tbActiveFloor, curFloor) > 0 and itemCfg.ItemType == buffCfg.Params[1] then
itemNormalWeight = itemNormalWeight - buffCfg.Params[2]
end
elseif buffCfg.BuffType == GameEnum.GoldenSpyBuffType.PermanentBuff then
if v.bActive and itemCfg.ItemType == buffCfg.Params[1] then
itemNormalWeight = itemNormalWeight - buffCfg.Params[2]
end
elseif buffCfg.BuffType == GameEnum.GoldenSpyBuffType.SkillCountBuff then
end
end
end
itemNormalWeight = math.max(0, itemNormalWeight)
local nbackSpeed = speed + factor * itemNormalWeight
if NovaAPI.IsEditorPlatform() then
print("GoldenSpyLevelCtrl:backSpeed=", nbackSpeed)
end
self:StartRetract(nbackSpeed, function()
self._mapNode.hook_open:SetActive(false)
self._mapNode.hook_normal:SetActive(true)
self._mapNode.hook_catched:SetActive(false)
if onCatchedComplete then
onCatchedComplete(self.curCatchedItem)
end
self.curCatchedItem.gameObject:SetActive(false)
self.curCatchedItem = nil
end)
self:StopExtend()
break
end
end
end, maxLength, duration):SetEase(self.HOOK_EXTEND_EASE):SetUpdate(true):OnComplete(function()
self._extendTweener = nil
self._hookState = 0
self:StartRetract(speed, function()
self:ResumeSwing(true)
if onCatchedComplete then
onCatchedComplete(self.curCatchedItem)
end
self.curCatchedItem = nil
end)
if self._onExtendComplete then
self._onExtendComplete()
self._onExtendComplete = nil
end
end)
end
function GoldenSpyHookCtrl:GetHookEndWorldPosition()
local rootTr = self.gameObject.transform
local trEnd = self._mapNode.trHookEnd
local localPt = rootTr:InverseTransformPoint(trEnd.position)
return Vector3(localPt.x, localPt.y, 0)
end
function GoldenSpyHookCtrl:StopExtend()
if self._extendTweener ~= nil then
self._extendTweener:Kill()
self._extendTweener = nil
end
if self._hookState == HookStage.STATE_EXTENDING then
self._hookState = 0
end
self._onExtendComplete = nil
end
function GoldenSpyHookCtrl:PauseExtend()
if self._extendTweener ~= nil then
self._extendTweener:Pause()
end
if self._hookState == HookStage.STATE_EXTENDING then
end
end
function GoldenSpyHookCtrl:ResumeExtend()
if self._extendTweener ~= nil then
self._extendTweener:Play()
end
if self._hookState == HookStage.STATE_EXTENDING then
end
end
function GoldenSpyHookCtrl:StartRetract(speed, onComplete)
self:StopSwing()
self:StopExtend()
self:StopRetract()
local tr = self._mapNode.trHookEnd
if tr == nil then
if onComplete then
onComplete()
end
return
end
self._mapNode.hook_open:SetActive(false)
self._mapNode.hook_normal:SetActive(false)
self._mapNode.hook_catched:SetActive(true)
self._onRetractComplete = onComplete
self._hookState = HookStage.STATE_RETRACTING
local fromLen = self._hookLength
local duration = fromLen / speed
if duration <= 0 then
self._hookLength = 0
self:ApplyHookLength()
self._hookState = 0
if self._onRetractComplete then
self._onRetractComplete()
self._onRetractComplete = nil
end
return
end
self._retractTweener = DOTween.To(function()
return self._hookLength
end, function(v)
self._hookLength = v
self:ApplyHookLength()
end, 0, duration):SetEase(self.HOOK_RETRACT_EASE):SetUpdate(true):OnComplete(function()
self._retractTweener = nil
self._hookState = 0
if self._onRetractComplete then
self._onRetractComplete()
self._onRetractComplete = nil
end
end):OnKill(function()
self._retractTweener = nil
if self._hookState == HookStage.STATE_RETRACTING then
self._hookState = 0
end
end)
end
function GoldenSpyHookCtrl:StopRetract()
if self._retractTweener ~= nil then
self._retractTweener:Kill()
self._retractTweener = nil
end
if self._hookState == HookStage.STATE_RETRACTING then
self._hookState = 0
end
self._onRetractComplete = nil
end
function GoldenSpyHookCtrl:SetRetractSpeed(newSpeed)
if not self:IsRetracting() then
return
end
local onComplete = self._onRetractComplete
self:StopRetract()
self:StartRetract(newSpeed, onComplete)
end
function GoldenSpyHookCtrl:PauseRetract()
if self._retractTweener ~= nil then
self._retractTweener:Pause()
end
if self._hookState == HookStage.STATE_RETRACTING then
end
end
function GoldenSpyHookCtrl:ResumeRetract()
if self._retractTweener ~= nil then
self._retractTweener:Play()
end
if self._hookState == HookStage.STATE_RETRACTING then
end
end
function GoldenSpyHookCtrl:GetHookAngle()
local pivot = self._mapNode.trHookPivot
if pivot == nil then
return 0
end
return pivot.localEulerAngles.z
end
function GoldenSpyHookCtrl:GetMaxLengthForAngle(angleDeg)
local rect = self._mapNode.rtMaxLengthArea
local trLine = self._mapNode.trLine
if rect == nil or rect:IsNull() or trLine == nil or trLine:IsNull() then
return 0
end
local lineLocalPos = rect:InverseTransformPoint(trLine.position)
local sx, sy = lineLocalPos.x, lineLocalPos.y
local left = 0
local bottom = 0
local right = rect.sizeDelta.x
local top = rect.sizeDelta.y
local rad = math.rad(angleDeg)
local worldDir = Vector3(math.sin(rad), -math.cos(rad), 0)
local localDir = rect:InverseTransformDirection(worldDir)
local dx, dy = localDir.x, localDir.y
local lenSq = dx * dx + dy * dy
if lenSq < 1.0E-12 then
return 0
end
local invLen = 1 / math.sqrt(lenSq)
dx, dy = dx * invLen, dy * invLen
local tMin
if math.abs(dx) > 1.0E-6 then
local tL = (left - sx) / dx
if 0 < tL then
local y = sy + tL * dy
if bottom <= y and top >= y then
tMin = tMin == nil and tL or math.min(tMin, tL)
end
end
local tR = (right - sx) / dx
if 0 < tR then
local y = sy + tR * dy
if bottom <= y and top >= y then
tMin = tMin == nil and tR or math.min(tMin, tR)
end
end
end
if math.abs(dy) > 1.0E-6 then
local tB = (bottom - sy) / dy
if 0 < tB then
local x = sx + tB * dx
if left <= x and right >= x then
tMin = tMin == nil and tB or math.min(tMin, tB)
end
end
local tT = (top - sy) / dy
if 0 < tT then
local x = sx + tT * dx
if left <= x and right >= x then
tMin = tMin == nil and tT or math.min(tMin, tT)
end
end
end
return math.max(0, tMin or 0)
end
function GoldenSpyHookCtrl:GetCurrentMaxLength()
return self:GetMaxLengthForAngle(self:GetHookAngle())
end
function GoldenSpyHookCtrl:_CircleCircleIntersect(hx, hy, hookR, ix, iy, itemR)
local dx, dy = ix - hx, iy - hy
local dist2 = dx * dx + dy * dy
local sumR = hookR + (itemR or 0)
return dist2 <= sumR * sumR
end
function GoldenSpyHookCtrl:_CircleRectIntersect(hx, hy, hookR, cx, cy, width, height)
local hw = (width or 0) * 0.5
local hh = (height or 0) * 0.5
local px = hx
if px < cx - hw then
px = cx - hw
elseif px > cx + hw then
px = cx + hw
end
local py = hy
if py < cy - hh then
py = cy - hh
elseif py > cy + hh then
py = cy + hh
end
local dx, dy = hx - px, hy - py
local dist2 = dx * dx + dy * dy
return dist2 <= hookR * hookR
end
function GoldenSpyHookCtrl:CheckCatchIntersect(hookWorldPos, hookRadius, item)
if hookWorldPos == nil or not item then
return false
end
local hx, hy = hookWorldPos.x, hookWorldPos.y
local hitArea = item:GetHitArea()
local ix, iy = hitArea.center.x, hitArea.center.y
if hitArea.nType == AllEnum.GoldenSpyHitAreaType.Circle then
return self:_CircleCircleIntersect(hx, hy, hookRadius, ix, iy, (hitArea.width or 0) / 2)
elseif hitArea.nType == AllEnum.GoldenSpyHitAreaType.Rectangle then
return self:_CircleRectIntersect(hx, hy, hookRadius, ix, iy, hitArea.width or 0, hitArea.height or 0)
end
return self:_CircleCircleIntersect(hx, hy, hookRadius, ix, iy, item.radius or 0)
end
function GoldenSpyHookCtrl:CheckCatchInRadius(hookWorldPos, radius, items)
if not (hookWorldPos ~= nil and items) or #items == 0 then
return nil
end
for i = 1, #items do
local item = items[i]
if self:CheckCatchIntersect(hookWorldPos, radius, item) then
return item
end
end
return nil
end
function GoldenSpyHookCtrl:GetHookLength()
return self._hookLength
end
function GoldenSpyHookCtrl:GetState()
return self._hookState
end
function GoldenSpyHookCtrl:IsSwinging()
return self._hookState == HookStage.STATE_SWINGING
end
function GoldenSpyHookCtrl:IsExtending()
return self._hookState == HookStage.STATE_EXTENDING
end
function GoldenSpyHookCtrl:IsRetracting()
return self._hookState == HookStage.STATE_RETRACTING
end
function GoldenSpyHookCtrl:DropItem()
self.curCatchedItem = nil
end
function GoldenSpyHookCtrl:OnDestroy()
self:StopSwing()
self:StopExtend()
self:StopRetract()
end
function GoldenSpyHookCtrl:GetBuffData()
return self.levelData:GetBuffData()
end
function GoldenSpyHookCtrl:Pause()
if self:IsSwinging() then
self:PauseSwing()
end
if self:IsExtending() then
self:PauseExtend()
end
if self:IsRetracting() then
self:PauseRetract()
end
end
function GoldenSpyHookCtrl:Continue()
if self:IsSwinging() then
self:ResumeSwing(true)
end
if self:IsExtending() then
self:ResumeExtend()
end
if self:IsRetracting() then
self:ResumeRetract()
end
end
function GoldenSpyHookCtrl:Exit()
self:StopSwing()
self:StopExtend()
self:StopRetract()
end
return GoldenSpyHookCtrl