데미지 팝업 순차 표시 및 전투 UI 반영 정리 #112

Merged
maple merged 1 commits from codex/dmg-pop-sequence-fix into main 2026-07-09 22:48:42 +09:00
16 changed files with 17924 additions and 4199 deletions
Showing only changes of commit a7a6b2123a - Show all commits

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -304,6 +304,7 @@ if dmg > 0 then
self:ApplyPoisonToMonster(m, poison)
end
end
self:ShowDmgPop(m.slot, dmg)
self:MonsterHitMotion(m.slot)
local killed = false
if m.hp <= 0 then
@@ -383,7 +384,7 @@ local burstEvery = self:AddPowerFieldTotal("poisonApplicationBurstEvery")
local burstDamage = self:AddPowerFieldTotal("poisonApplicationBurstDamage")
if burstEvery ~= nil and burstEvery > 0 and burstDamage ~= nil and burstDamage > 0 then
if (self.PoisonApplicationsThisCombat % burstEvery) == 0 then
self:DealDamageToAllMonsters(burstDamage)
self:DealDamageToAllMonsters(burstDamage, false)
end
end`, [
{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'target' },
@@ -440,6 +441,13 @@ return killCount > 0`, [
], 0, 'boolean'),
method('PlayAttackFx', `local m = self.Monsters[targetIndex]
if m == nil or m.alive ~= true or m.entity == nil or not isvalid(m.entity) then
local shown = damage
if m ~= nil and m.alive == true and m.vuln > 0 then
shown = math.floor(damage * 1.5)
end
if m ~= nil and m.alive == true and m.weak > 0 and self.ActiveAttackDamageVsWeakMultiplier ~= nil and self.ActiveAttackDamageVsWeakMultiplier > 1 then
shown = math.floor(shown * self.ActiveAttackDamageVsWeakMultiplier)
end
self:DealDamageToTarget(damage, pierce)
self.ActiveAttackDamageVsWeakMultiplier = 1
self:RenderCombat()
@@ -474,7 +482,6 @@ _TimerService:SetTimerOnce(function()
self.ActiveKillReward = 0
self.ActiveKillMaxHpGain = 0
self.ActiveAttackDamageVsWeakMultiplier = 1
self:ShowDmgPop(targetIndex, shown)
self:RenderCombat()
self:CheckCombatEnd()
end, 0.35)`, [
@@ -599,7 +606,7 @@ if dmg > 0 then
end
if self:HasRelic("centennialPuzzle") and self.FirstHpLossDone == false then
self.FirstHpLossDone = true
self:DrawCards(3)
self:DrawCards(3, true)
self:RenderHand(false)
end
end

View File

@@ -321,7 +321,7 @@ if self.NextTurnAddCards ~= nil then
end
local drawN = 5 + (self.NextTurnDraw or 0) + powerTurnDraw
self.NextTurnDraw = 0
self:DrawCards(drawN)
self:DrawCards(drawN, true)
self:RenderHand(true)
self:RenderCombat()
if powerTurnDiscard > 0 then

View File

@@ -784,6 +784,7 @@ if c.kind == "Attack" then
baseDmg = xEnergy * c.xDamagePerEnergy
end
local total = 0
local hitDamages = {}
local hitN = c.hits or 1
if c.otherHandAtLeast ~= nil and c.bonusHitsWhenOtherHandAtLeast ~= nil then
local otherHand = 0
@@ -796,7 +797,9 @@ if c.kind == "Attack" then
end
end
for h = 1, hitN do
total = total + self:CalcPlayerAttack(baseDmg)
local hitDamage = self:CalcPlayerAttack(baseDmg)
hitDamages[h] = hitDamage
total = total + hitDamage
end
local useAoe = c.aoe == true
if c.class == "shiv" and (self.ShivAoeThisCombat == true or self:HasPowerField("shivAoe") == true) then
@@ -841,11 +844,16 @@ if c.kind == "Attack" then
if targetIdx ~= nil and targetIdx > 0 then
local prev = self.TargetIndex
self.TargetIndex = targetIdx
local killed = self:DealDamageToTarget(total / hitN, c.pierce == true)
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

View File

@@ -25,7 +25,7 @@ for i = 1, #self.RunRelics do
elseif r.effect == "strength" then
self.PlayerStr = self.PlayerStr + r.value
elseif r.effect == "draw" then
self:DrawCards(r.value)
self:DrawCards(r.value, true)
self:RenderHand(false)
elseif r.effect == "heal" or r.effect == "healOnAttack" or r.effect == "healOnWin" then
self.PlayerHp = self.PlayerHp + r.value
@@ -151,7 +151,6 @@ if p.effect == "heal" then
self.PlayerHp = math.min(self.PlayerHp + p.value, self.PlayerMaxHp)
elseif p.effect == "damage" then
self:DealDamageToTarget(p.value, false)
self:ShowDmgPop(self.TargetIndex, p.value)
elseif p.effect == "strength" then
self.PlayerStr = self.PlayerStr + p.value
elseif p.effect == "block" then

View File

@@ -100,83 +100,106 @@ end
self:SetText("/ui/RunUIGroup/CombatHud/PlayerPanel/Buffs", pb)
self:RenderRun()`),
method('ShowDmgPop', `local slotKey = string.format("%d", math.floor(slot or 0))
local base = "/ui/RunUIGroup/CombatHud/DmgPop" .. slotKey
if self.DmgPopSlotQueue == nil then
self.DmgPopSlotQueue = {}
end
local popIndex = (self.DmgPopSlotQueue[slotKey] or 0) + 1
if popIndex > 5 then
popIndex = 1
end
self.DmgPopSlotQueue[slotKey] = popIndex
local base = "/ui/RunUIGroup/CombatHud/DmgPop" .. slotKey .. "_" .. tostring(popIndex)
local pop = _EntityService:GetEntityByPath(base)
if pop == nil then
return
end
self.DmgPopSeq = (self.DmgPopSeq or 0) + 1
local popSeq = self.DmgPopSeq
self:SetText(base, "")
local damageDigitRuids = { ${DAMAGE_DIGIT_RUIDS.map(luaStr).join(', ')} }
local shown = tostring(math.max(0, math.floor(amount)))
if string.len(shown) > ${DAMAGE_POP_MAX_DIGITS} then
shown = string.sub(shown, 1, ${DAMAGE_POP_MAX_DIGITS})
end
local digits = {}
for i = 1, string.len(shown) do
table.insert(digits, tonumber(string.sub(shown, i, i)) or 0)
end
local totalW = #digits * ${DAMAGE_POP_DIGIT_W} + math.max(0, #digits - 1) * ${DAMAGE_POP_DIGIT_SPACING}
local startX = -totalW / 2 + ${DAMAGE_POP_DIGIT_W} / 2
for i = 1, ${DAMAGE_POP_MAX_DIGITS} do
self:SetEntityEnabled(base .. "/Digit" .. tostring(i), false)
end
for i = 1, ${DAMAGE_POP_MAX_DIGITS} do
local digitPath = base .. "/Digit" .. tostring(i)
local digitEntity = _EntityService:GetEntityByPath(digitPath)
if digitEntity ~= nil and digitEntity.SpriteGUIRendererComponent ~= nil then
if digits[i] ~= nil then
digitEntity.SpriteGUIRendererComponent.ImageRUID = damageDigitRuids[digits[i] + 1]
digitEntity.SpriteGUIRendererComponent.Color = Color(1, 1, 1, 1)
if digitEntity.UITransformComponent ~= nil then
digitEntity.UITransformComponent.anchoredPosition = Vector2(startX + (i - 1) * (${DAMAGE_POP_DIGIT_W} + ${DAMAGE_POP_DIGIT_SPACING}), 0)
end
self:SetEntityEnabled(digitPath, true)
else
self:SetEntityEnabled(digitPath, false)
end
end
end
local popPos = nil
local m = self.Monsters[slot]
if m ~= nil and m.entity ~= nil and isvalid(m.entity) and m.entity.TransformComponent ~= nil then
local wp = m.entity.TransformComponent.WorldPosition
local screen = _UILogic:WorldToScreenPosition(Vector2(wp.x, wp.y + ${HEAD_OFFSET_Y + 0.45}))
popPos = _UILogic:ScreenToUIPosition(screen)
else
local slotEntity = _EntityService:GetEntityByPath("/ui/RunUIGroup/CombatHud/MonsterStatus" .. slotKey)
if slotEntity ~= nil and slotEntity.UITransformComponent ~= nil then
local sp = slotEntity.UITransformComponent.anchoredPosition
popPos = Vector2(sp.x, sp.y + 76)
end
end
if pop ~= nil and pop.UITransformComponent ~= nil then
if popPos ~= nil then
pop.UITransformComponent.anchoredPosition = popPos
else
pop.UITransformComponent.anchoredPosition = Vector2(0, 120)
end
end
self:SetEntityEnabled(base, true)
for i = 1, 6 do
_TimerService:SetTimerOnce(function()
if self.DmgPopSeq ~= popSeq then
return
end
local p = _EntityService:GetEntityByPath(base)
if p ~= nil and p.UITransformComponent ~= nil then
local cur = p.UITransformComponent.anchoredPosition
p.UITransformComponent.anchoredPosition = Vector2(cur.x, cur.y + 7)
end
end, 0.045 * i)
end
_TimerService:SetTimerOnce(function()
if self.DmgPopSeq ~= popSeq then
return
end
local startDelay = 0.2 * (popIndex - 1)
local function showNow()
self:SetEntityEnabled(base, false)
end, 0.48)`, [
self:SetText(base, "")
local damageDigitRuids = { ${DAMAGE_DIGIT_RUIDS.map(luaStr).join(', ')} }
local shown = tostring(math.max(0, math.floor(amount)))
if string.len(shown) > ${DAMAGE_POP_MAX_DIGITS} then
shown = string.sub(shown, 1, ${DAMAGE_POP_MAX_DIGITS})
end
local digits = {}
local digitPathsToEnable = {}
for i = 1, string.len(shown) do
table.insert(digits, tonumber(string.sub(shown, i, i)) or 0)
end
local totalW = #digits * ${DAMAGE_POP_DIGIT_W} + math.max(0, #digits - 1) * ${DAMAGE_POP_DIGIT_SPACING}
local startX = -totalW / 2 + ${DAMAGE_POP_DIGIT_W} / 2
for i = 1, ${DAMAGE_POP_MAX_DIGITS} do
self:SetEntityEnabled(base .. "/Digit" .. tostring(i), false)
end
for i = 1, ${DAMAGE_POP_MAX_DIGITS} do
local digitPath = base .. "/Digit" .. tostring(i)
local digitEntity = _EntityService:GetEntityByPath(digitPath)
if digitEntity ~= nil and digitEntity.SpriteGUIRendererComponent ~= nil then
if digits[i] ~= nil then
digitEntity.SpriteGUIRendererComponent.ImageRUID = damageDigitRuids[digits[i] + 1]
digitEntity.SpriteGUIRendererComponent.Color = Color(1, 1, 1, 1)
if digitEntity.UITransformComponent ~= nil then
digitEntity.UITransformComponent.anchoredPosition = Vector2(startX + (i - 1) * (${DAMAGE_POP_DIGIT_W} + ${DAMAGE_POP_DIGIT_SPACING}), 0)
end
table.insert(digitPathsToEnable, digitPath)
else
self:SetEntityEnabled(digitPath, false)
end
end
end
local popPos = nil
local m = self.Monsters[slot]
if m ~= nil and m.entity ~= nil and isvalid(m.entity) and m.entity.TransformComponent ~= nil then
local wp = m.entity.TransformComponent.WorldPosition
local screen = _UILogic:WorldToScreenPosition(Vector2(wp.x, wp.y + ${HEAD_OFFSET_Y + 0.45}))
popPos = _UILogic:ScreenToUIPosition(screen)
else
local slotEntity = _EntityService:GetEntityByPath("/ui/RunUIGroup/CombatHud/MonsterStatus" .. slotKey)
if slotEntity ~= nil and slotEntity.UITransformComponent ~= nil then
local sp = slotEntity.UITransformComponent.anchoredPosition
popPos = Vector2(sp.x, sp.y + 76)
end
end
if pop.UITransformComponent ~= nil then
if popPos ~= nil then
pop.UITransformComponent.anchoredPosition = popPos
else
pop.UITransformComponent.anchoredPosition = Vector2(0, 120)
end
end
self:SetEntityEnabled(base, true)
for i = 1, #digitPathsToEnable do
self:SetEntityEnabled(digitPathsToEnable[i], true)
end
for i = 1, 6 do
_TimerService:SetTimerOnce(function()
local p = _EntityService:GetEntityByPath(base)
if p ~= nil and p.UITransformComponent ~= nil then
local cur = p.UITransformComponent.anchoredPosition
p.UITransformComponent.anchoredPosition = Vector2(cur.x, cur.y + 7)
end
local alpha = 1 - (i / 6)
if alpha < 0 then alpha = 0 end
for di = 1, ${DAMAGE_POP_MAX_DIGITS} do
local digitEntity = _EntityService:GetEntityByPath(base .. "/Digit" .. tostring(di))
if digitEntity ~= nil and digitEntity.Enable == true and digitEntity.SpriteGUIRendererComponent ~= nil then
digitEntity.SpriteGUIRendererComponent.Color = Color(1, 1, 1, alpha)
end
end
end, 0.05 * i)
end
_TimerService:SetTimerOnce(function()
self:SetEntityEnabled(base, false)
end, 0.3)
end
if startDelay > 0 then
_TimerService:SetTimerOnce(function()
showNow()
end, startDelay)
else
showNow()
end`, [
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' },
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' },
]),
@@ -301,4 +324,5 @@ if self.AscensionLevel > 0 then
end
self:SetText("/ui/RunUIGroup/CombatHud/TopBar/Floor", floorText)
self:SetText("/ui/RunUIGroup/CombatHud/TopBar/Gold", "메소 " .. string.format("%d", self.Gold))`),
];

View File

@@ -106,6 +106,7 @@ self.HolyChargeCount = 0
self.DamagePowerStrengthUsed = false
self.DamageDealtThisTurn = 0
self.DmgPopSeq = 0
self.DmgPopSlotQueue = {}
self.FirstHpLossDone = false
self.ClayBlockNext = 0
self.DiscardSelectRemaining = 0

View File

@@ -49,9 +49,9 @@ end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], N
method('ShowMainMenu', `self.SelectedClass = ""
self:RenderAscension()
self:ShowState("menu")
self:SetText("/ui/DefaultGroup/MainMenu/Title", "메이플 덱 어드벤처")
self:SetText("/ui/DefaultGroup/MainMenu/Subtitle", "캐릭터를 고르고 덱을 만들어 모험을 시작하세요")
self:SetText("/ui/DefaultGroup/MainMenu/NewGameButton", "새 게임")
self:SetText("/ui/DefaultGroup/MainMenu/Title", "Maple Deck Adventure")
self:SetText("/ui/DefaultGroup/MainMenu/Subtitle", "Choose your character and begin your run")
self:SetText("/ui/DefaultGroup/MainMenu/NewGameButton", "New Game")
self:BindMenuButtons()`),
method('BindMenuButtons', `local buttonEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/MainMenu/NewGameButton")
if buttonEntity ~= nil and (buttonEntity.ButtonComponent ~= nil or buttonEntity:AddComponent("ButtonComponent") ~= nil) then
@@ -136,22 +136,28 @@ self:BindLobbyButtons()
self:BindMenuButtons()
self:GoLobbyMap()`),
method('RenderSoulLabel', `local soulPoints = self.SoulPoints or 0
self:SetText("/ui/LobbyUIGroup/LobbyHud/SoulLabel", "영혼 " .. string.format("%d", soulPoints))
self:SetText("/ui/LobbyUIGroup/SoulShopHud/Souls", "영혼 " .. string.format("%d", soulPoints))`),
self:SetText("/ui/LobbyUIGroup/LobbyHud/SoulLabel", "Soul " .. string.format("%d", soulPoints))
self:SetText("/ui/LobbyUIGroup/SoulShopHud/Souls", "Soul " .. string.format("%d", soulPoints))`),
method('BindLobbyButtons', `if self.LobbyBound == true then
return
end
self.LobbyBound = true
local function bindClick(path, handler)
local entity = _EntityService:GetEntityByPath(path)
if entity ~= nil and (entity.ButtonComponent ~= nil or entity:AddComponent("ButtonComponent") ~= nil) then
entity:ConnectEvent(ButtonClickEvent, handler)
end
local ascMinus = _EntityService:GetEntityByPath("/ui/LobbyUIGroup/LobbyHud/AscMinus")
if ascMinus ~= nil and (ascMinus.ButtonComponent ~= nil or ascMinus:AddComponent("ButtonComponent") ~= nil) then
ascMinus:ConnectEvent(ButtonClickEvent, function() self:AdjustAscension(-1) end)
end
bindClick("/ui/LobbyUIGroup/LobbyHud/AscMinus", function() self:AdjustAscension(-1) end)
bindClick("/ui/LobbyUIGroup/LobbyHud/AscPlus", function() self:AdjustAscension(1) end)
bindClick("/ui/LobbyUIGroup/BoardHud/Close", function() self:CloseBoard() end)
bindClick("/ui/LobbyUIGroup/SoulShopHud/Close", function() self:CloseSoulShop() end)`),
local ascPlus = _EntityService:GetEntityByPath("/ui/LobbyUIGroup/LobbyHud/AscPlus")
if ascPlus ~= nil and (ascPlus.ButtonComponent ~= nil or ascPlus:AddComponent("ButtonComponent") ~= nil) then
ascPlus:ConnectEvent(ButtonClickEvent, function() self:AdjustAscension(1) end)
end
local boardClose = _EntityService:GetEntityByPath("/ui/LobbyUIGroup/BoardHud/Close")
if boardClose ~= nil and (boardClose.ButtonComponent ~= nil or boardClose:AddComponent("ButtonComponent") ~= nil) then
boardClose:ConnectEvent(ButtonClickEvent, function() self:CloseBoard() end)
end
local soulClose = _EntityService:GetEntityByPath("/ui/LobbyUIGroup/SoulShopHud/Close")
if soulClose ~= nil and (soulClose.ButtonComponent ~= nil or soulClose:AddComponent("ButtonComponent") ~= nil) then
soulClose:ConnectEvent(ButtonClickEvent, function() self:CloseSoulShop() end)
end`),
method('ShowCodex', `self.CodexMode = true
self.ClassDeckMode = true
local close = _EntityService:GetEntityByPath("/ui/DeckUIGroup/DeckAllHud/Close")

View File

@@ -41,24 +41,36 @@ if lp ~= nil then
end
self:RenderSoulLabel()`, [{ Type: "number", DefaultValue: null, SyncDirection: 0, Attributes: [], Name: "n" }]),
method('BuySoulUnlock', `local d = nil
if self.SoulShopDef ~= nil then d = self.SoulShopDef[slot] end
if d == nil then return end
if self.SoulUnlocks ~= nil and self.SoulUnlocks[d.key] == true then
self:Toast("이미 보유 중입니다")
if self.SoulShopDef ~= nil then
d = self.SoulShopDef[slot]
end
if d == nil then
return
end
if (self.SoulPoints or 0) < d.cost then
self:Toast("영혼이 부족합니다")
local unlockKey = d.key
local unlockCost = d.cost
local unlockName = d.name or "Unlock"
if unlockKey == nil or unlockCost == nil then
return
end
self.SoulPoints = self.SoulPoints - d.cost
if self.SoulUnlocks == nil then self.SoulUnlocks = {} end
self.SoulUnlocks[d.key] = true
if self.SoulUnlocks ~= nil and self.SoulUnlocks[unlockKey] == true then
self:Toast("Already owned")
return
end
if (self.SoulPoints or 0) < unlockCost then
self:Toast("Not enough soul")
return
end
self.SoulPoints = self.SoulPoints - unlockCost
if self.SoulUnlocks == nil then
self.SoulUnlocks = {}
end
self.SoulUnlocks[unlockKey] = true
local lp = _UserService.LocalPlayer
if lp ~= nil then
self:SaveSouls(self.SoulPoints, self:SerializeUnlocks(), lp.PlayerComponent.UserId)
end
self:Toast(d.name .. " 해금!")
self:Toast(unlockName .. " unlocked")
self:RenderSoulLabel()
self:RenderSoulShop()`, [{ Type: "number", DefaultValue: null, SyncDirection: 0, Attributes: [], Name: "slot" }]),
method('RenderSoulShop', `local defs = self.SoulShopDef or {}
@@ -68,16 +80,20 @@ for i = 1, 4 do
if d == nil then
self:SetEntityEnabled(base, false)
else
local itemName = d.name or "Unlock"
local itemDesc = d.desc or ""
local itemKey = d.key
local itemCost = d.cost or 0
self:SetEntityEnabled(base, true)
self:SetText(base .. "/Name", d.name)
self:SetText(base .. "/Desc", d.desc)
local owned = self.SoulUnlocks ~= nil and self.SoulUnlocks[d.key] == true
self:SetText(base .. "/Name", itemName)
self:SetText(base .. "/Desc", itemDesc)
local owned = itemKey ~= nil and self.SoulUnlocks ~= nil and self.SoulUnlocks[itemKey] == true
if owned then
self:SetText(base .. "/Status", "보유 중")
elseif (self.SoulPoints or 0) >= d.cost then
self:SetText(base .. "/Status", tostring(d.cost) .. " 영혼 · 구매")
self:SetText(base .. "/Status", "Owned")
elseif (self.SoulPoints or 0) >= itemCost then
self:SetText(base .. "/Status", tostring(itemCost) .. " soul · buy")
else
self:SetText(base .. "/Status", tostring(d.cost) .. " 영혼 · 부족")
self:SetText(base .. "/Status", tostring(itemCost) .. " soul · low")
end
end
end`),

View File

@@ -36,6 +36,7 @@ function writeCodeblocks() {
prop('number', 'TweenEventId', '0'),
prop('number', 'CardHoverTweenId', '0'),
prop('number', 'DmgPopSeq', '0'),
prop('any', 'DmgPopSlotQueue'),
prop('any', 'EndTurnHandler'),
prop('any', 'NewGameHandler'),
prop('any', 'WarriorSelectHandler'),

View File

@@ -24,7 +24,7 @@
"enable": true,
"visible": true,
"localize": true,
"displayOrder": 4,
"displayOrder": 6,
"pathConstraints": "//",
"revision": 1,
"origin": {

View File

@@ -24,7 +24,7 @@
"enable": true,
"visible": true,
"localize": true,
"displayOrder": 5,
"displayOrder": 1,
"pathConstraints": "//",
"revision": 1,
"origin": {

View File

@@ -24,7 +24,7 @@
"enable": true,
"visible": true,
"localize": true,
"displayOrder": 1,
"displayOrder": 2,
"pathConstraints": "//",
"revision": 1,
"origin": {

File diff suppressed because it is too large Load Diff

View File

@@ -24,7 +24,7 @@
"enable": true,
"visible": true,
"localize": true,
"displayOrder": 2,
"displayOrder": 3,
"pathConstraints": "//",
"revision": 1,
"origin": {