Merge pull request '타격 시퀀스와 카메라 쉐이크 타이밍 정리' (#114) from codex/attack-sequence-feel-fix into main
Reviewed-on: #114
This commit was merged in pull request #114.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -297,6 +297,7 @@ if m.block > 0 and pierce ~= true then
|
||||
m.block = m.block - absorbed
|
||||
dmg = dmg - absorbed
|
||||
end
|
||||
self:ShakeCombatCamera()
|
||||
m.hp = m.hp - dmg
|
||||
if dmg > 0 then
|
||||
local poison = self:AddPowerFieldTotal("attackPoison")
|
||||
@@ -333,6 +334,7 @@ end
|
||||
if m == nil then
|
||||
return false
|
||||
end
|
||||
self:ShakeCombatCamera()
|
||||
m.hp = m.hp - amount
|
||||
self:ShowDmgPop(m.slot, amount)
|
||||
self:MonsterHitMotion(m.slot)
|
||||
@@ -359,6 +361,7 @@ local m = alive[math.random(1, #alive)]
|
||||
if m == nil then
|
||||
return false
|
||||
end
|
||||
self:ShakeCombatCamera()
|
||||
m.hp = m.hp - amount
|
||||
self:ShowDmgPop(m.slot, amount)
|
||||
self:MonsterHitMotion(m.slot)
|
||||
@@ -393,6 +396,17 @@ end`, [
|
||||
method('DealDamageToAllMonsters', `if self.Monsters == nil then
|
||||
return false
|
||||
end
|
||||
local anyAlive = false
|
||||
for i = 1, #self.Monsters do
|
||||
local m = self.Monsters[i]
|
||||
if m ~= nil and m.alive == true then
|
||||
anyAlive = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if anyAlive == true and isAttack == true then
|
||||
self:ShakeCombatCamera()
|
||||
end
|
||||
local killCount = 0
|
||||
for i = 1, #self.Monsters do
|
||||
local m = self.Monsters[i]
|
||||
@@ -572,6 +586,7 @@ end
|
||||
if dmg > 0 and self.PlayerIntangible ~= nil and self.PlayerIntangible > 0 and dmg > 1 then
|
||||
dmg = 1
|
||||
end
|
||||
self:ShakeCombatCamera()
|
||||
if dmg > 0 then
|
||||
self.PlayerHp = self.PlayerHp - dmg
|
||||
local reactiveBlock = self:AddPowerFieldTotal("blockOnDamaged")
|
||||
|
||||
@@ -313,6 +313,108 @@ if amount < 0 then
|
||||
end
|
||||
self.PlayerBlock = self.PlayerBlock + amount
|
||||
return amount`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'base' }], 0, 'number'),
|
||||
method('RunAttackSequence', `if hitDamages == nil or #hitDamages <= 0 then
|
||||
return
|
||||
end
|
||||
local function countAliveMonsters()
|
||||
local n = 0
|
||||
if self.Monsters ~= nil then
|
||||
for mi = 1, #self.Monsters do
|
||||
local om = self.Monsters[mi]
|
||||
if om ~= nil and om.alive == true then
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return n
|
||||
end
|
||||
local function randomAliveMonsterIndex()
|
||||
local alive = {}
|
||||
if self.Monsters ~= nil then
|
||||
for mi = 1, #self.Monsters do
|
||||
local om = self.Monsters[mi]
|
||||
if om ~= nil and om.alive == true then
|
||||
table.insert(alive, mi)
|
||||
end
|
||||
end
|
||||
end
|
||||
if #alive <= 0 then
|
||||
return 0
|
||||
end
|
||||
return alive[math.random(1, #alive)]
|
||||
end
|
||||
self.FxBusy = true
|
||||
local hitIndex = 1
|
||||
local lockedTargetIndex = self.TargetIndex or 0
|
||||
local function finishSequence()
|
||||
self.FxBusy = false
|
||||
self.ActiveAttackDamageVsWeakMultiplier = 1
|
||||
self:RenderCombat()
|
||||
self:CheckCombatEnd()
|
||||
end
|
||||
local function step()
|
||||
if self.CombatOver == true then
|
||||
finishSequence()
|
||||
return
|
||||
end
|
||||
if countAliveMonsters() <= 0 then
|
||||
finishSequence()
|
||||
return
|
||||
end
|
||||
if hitIndex == 1 then
|
||||
self.DamageDealtThisTurn = (self.DamageDealtThisTurn or 0) + (roundPlannedDamage or 0)
|
||||
end
|
||||
local roundKilled = false
|
||||
if useAoe == true then
|
||||
roundKilled = self:DealDamageToAllMonsters(hitDamages[hitIndex] or 0, true)
|
||||
else
|
||||
local targetIdx = lockedTargetIndex
|
||||
if randomTargetEachHit == true then
|
||||
targetIdx = randomAliveMonsterIndex()
|
||||
end
|
||||
if targetIdx ~= nil and targetIdx > 0 then
|
||||
if randomTargetEachHit ~= true then
|
||||
local lockedMonster = nil
|
||||
if self.Monsters ~= nil then
|
||||
lockedMonster = self.Monsters[targetIdx]
|
||||
end
|
||||
if lockedMonster == nil or lockedMonster.alive ~= true then
|
||||
finishSequence()
|
||||
return
|
||||
end
|
||||
end
|
||||
local prev = self.TargetIndex
|
||||
self.TargetIndex = targetIdx
|
||||
roundKilled = self:DealDamageToTarget(hitDamages[hitIndex] or 0, pierce == true)
|
||||
self.TargetIndex = prev
|
||||
end
|
||||
end
|
||||
self:RenderCombat()
|
||||
self:CheckCombatEnd()
|
||||
hitIndex = hitIndex + 1
|
||||
if self.CombatOver == true then
|
||||
finishSequence()
|
||||
return
|
||||
end
|
||||
if hitIndex <= #hitDamages then
|
||||
_TimerService:SetTimerOnce(step, 0.2)
|
||||
return
|
||||
end
|
||||
if repeatOnKill == true and roundKilled == true and countAliveMonsters() > 0 then
|
||||
hitIndex = 1
|
||||
_TimerService:SetTimerOnce(step, 0.2)
|
||||
return
|
||||
end
|
||||
finishSequence()
|
||||
end
|
||||
step()`, [
|
||||
{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'hitDamages' },
|
||||
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'roundPlannedDamage' },
|
||||
{ Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'useAoe' },
|
||||
{ Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'randomTargetEachHit' },
|
||||
{ Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'pierce' },
|
||||
{ Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'repeatOnKill' },
|
||||
]),
|
||||
method('CountOtherHandSkills', `if self.Hand == nil then
|
||||
return 0
|
||||
end
|
||||
@@ -808,65 +910,7 @@ if c.kind == "Attack" then
|
||||
if c.class == "shiv" and self.ShivFirstDamageBonusUsed ~= true and self:HasPowerField("firstShivDamageBonus") == true then
|
||||
self.ShivFirstDamageBonusUsed = true
|
||||
end
|
||||
local function countAliveMonsters()
|
||||
local n = 0
|
||||
if self.Monsters ~= nil then
|
||||
for mi = 1, #self.Monsters do
|
||||
local om = self.Monsters[mi]
|
||||
if om ~= nil and om.alive == true then n = n + 1 end
|
||||
end
|
||||
end
|
||||
return n
|
||||
end
|
||||
local function randomAliveMonsterIndex()
|
||||
local alive = {}
|
||||
if self.Monsters ~= nil then
|
||||
for mi = 1, #self.Monsters do
|
||||
local om = self.Monsters[mi]
|
||||
if om ~= nil and om.alive == true then
|
||||
table.insert(alive, mi)
|
||||
end
|
||||
end
|
||||
end
|
||||
if #alive <= 0 then
|
||||
return 0
|
||||
end
|
||||
return alive[math.random(1, #alive)]
|
||||
end
|
||||
local function resolveAttackRound()
|
||||
local roundKilled = false
|
||||
if useAoe == true then
|
||||
local killed = self:DealDamageToAllMonsters(total, true)
|
||||
if killed == true then roundKilled = true end
|
||||
elseif c.randomTargetEachHit == true then
|
||||
for h = 1, hitN do
|
||||
local targetIdx = randomAliveMonsterIndex()
|
||||
if targetIdx ~= nil and targetIdx > 0 then
|
||||
local prev = self.TargetIndex
|
||||
self.TargetIndex = targetIdx
|
||||
local killed = self:DealDamageToTarget(hitDamages[h] or 0, c.pierce == true)
|
||||
self.TargetIndex = prev
|
||||
if killed == true then roundKilled = true end
|
||||
end
|
||||
end
|
||||
elseif hitN > 1 then
|
||||
for h = 1, hitN do
|
||||
local killed = self:DealDamageToTarget(hitDamages[h] or 0, c.pierce == true)
|
||||
if killed == true then roundKilled = true end
|
||||
end
|
||||
else
|
||||
local killed = self:DealDamageToTarget(total, c.pierce == true)
|
||||
if killed == true then roundKilled = true end
|
||||
end
|
||||
return roundKilled
|
||||
end
|
||||
local totalDamage = 0
|
||||
local roundKilled = false
|
||||
repeat
|
||||
roundKilled = resolveAttackRound()
|
||||
totalDamage = totalDamage + total
|
||||
until c.repeatOnKill ~= true or roundKilled ~= true or countAliveMonsters() <= 0
|
||||
self.DamageDealtThisTurn = (self.DamageDealtThisTurn or 0) + totalDamage
|
||||
self:RunAttackSequence(hitDamages, total, useAoe, c.randomTargetEachHit == true, c.pierce == true, c.repeatOnKill == true)
|
||||
end
|
||||
if c.block ~= nil then
|
||||
self:AddCardBlock(c.block + (self.HolyChargeCount or 0) * (c.blockPerHolyCharge or 0))
|
||||
|
||||
@@ -113,7 +113,6 @@ local pop = _EntityService:GetEntityByPath(base)
|
||||
if pop == nil then
|
||||
return
|
||||
end
|
||||
local startDelay = 0.2 * (popIndex - 1)
|
||||
local function showNow()
|
||||
self:SetEntityEnabled(base, false)
|
||||
self:SetText(base, "")
|
||||
@@ -193,13 +192,7 @@ local function showNow()
|
||||
self:SetEntityEnabled(base, false)
|
||||
end, 0.3)
|
||||
end
|
||||
if startDelay > 0 then
|
||||
_TimerService:SetTimerOnce(function()
|
||||
showNow()
|
||||
end, startDelay)
|
||||
else
|
||||
showNow()
|
||||
end`, [
|
||||
showNow()`, [
|
||||
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' },
|
||||
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' },
|
||||
]),
|
||||
@@ -211,6 +204,48 @@ else
|
||||
end
|
||||
self:SetEntityEnabled(base, true)
|
||||
_TimerService:SetTimerOnce(function() self:SetEntityEnabled(base, false) end, 0.6)`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' }]),
|
||||
method('ShakeCombatCamera', `local lp = _UserService.LocalPlayer
|
||||
local cam = nil
|
||||
if lp ~= nil then
|
||||
cam = lp.CameraComponent
|
||||
end
|
||||
if cam == nil then
|
||||
cam = _CameraService:GetCurrentCameraComponent()
|
||||
end
|
||||
if cam == nil then
|
||||
return
|
||||
end
|
||||
self.CombatCameraShakeSeq = (self.CombatCameraShakeSeq or 0) + 1
|
||||
local seq = self.CombatCameraShakeSeq
|
||||
local baseCameraOffset = Vector2(${CAM.cameraOffsetX}, ${CAM.cameraOffsetY})
|
||||
cam.CameraOffset = baseCameraOffset
|
||||
local offsets = {
|
||||
Vector2(-0.22, 0),
|
||||
Vector2(0.2, 0),
|
||||
Vector2(-0.12, 0),
|
||||
Vector2(0.06, 0),
|
||||
Vector2(0, 0),
|
||||
}
|
||||
for i = 1, #offsets do
|
||||
local delta = offsets[i]
|
||||
_TimerService:SetTimerOnce(function()
|
||||
local lp2 = _UserService.LocalPlayer
|
||||
local cam2 = nil
|
||||
if lp2 ~= nil then
|
||||
cam2 = lp2.CameraComponent
|
||||
end
|
||||
if cam2 == nil then
|
||||
cam2 = _CameraService:GetCurrentCameraComponent()
|
||||
end
|
||||
if cam2 == nil then
|
||||
return
|
||||
end
|
||||
if seq ~= self.CombatCameraShakeSeq then
|
||||
return
|
||||
end
|
||||
cam2.CameraOffset = Vector2(baseCameraOffset.x + delta.x, baseCameraOffset.y + delta.y)
|
||||
end, 0.028 * (i - 1))
|
||||
end`, []),
|
||||
method('PlayerAttackMotion', `local lp = _UserService.LocalPlayer
|
||||
if lp == nil then
|
||||
return
|
||||
|
||||
@@ -106,6 +106,7 @@ self.HolyChargeCount = 0
|
||||
self.DamagePowerStrengthUsed = false
|
||||
self.DamageDealtThisTurn = 0
|
||||
self.DmgPopSeq = 0
|
||||
self.CombatCameraShakeSeq = 0
|
||||
self.DmgPopSlotQueue = {}
|
||||
self.FirstHpLossDone = false
|
||||
self.ClayBlockNext = 0
|
||||
|
||||
@@ -36,6 +36,7 @@ function writeCodeblocks() {
|
||||
prop('number', 'TweenEventId', '0'),
|
||||
prop('number', 'CardHoverTweenId', '0'),
|
||||
prop('number', 'DmgPopSeq', '0'),
|
||||
prop('number', 'CombatCameraShakeSeq', '0'),
|
||||
prop('any', 'DmgPopSlotQueue'),
|
||||
prop('any', 'EndTurnHandler'),
|
||||
prop('any', 'NewGameHandler'),
|
||||
|
||||
Reference in New Issue
Block a user