import { method, RUN_LENGTH, GOLD_PER_WIN, CARD_PRICE, REST_HEAL, RELIC_PRICE, ACT_COUNT, ACT_MAPS, LOBBY_MAP, LOBBY_SPAWN } from '../lib/codeblock.mjs'; import { CARDS, ENEMIES, CLASSES, JOBS, SOUL_UNLOCKS, CARDFRAMES, RARITIES, MAP_ROWS, MAP_COLS, CHEST_CLOSED_RUID, CHEST_OPEN_RUID, NODEICONS, CHARS, CAM, RELICS, POTIONS, luaSoulShopTable, frameRuid, luaFramesTable, luaNodeIconsTable, luaRelicsTable, luaPotionsTable, luaIntentsArray, luaEnemiesTable, luaStr, luaJobsTable, luaCardsTable, luaDeckTable } from '../lib/data.mjs'; import { UI_FILE, COMMON_FILE, UI_ROOT, GENERATED_UI_SECTIONS, UI_APPEND_ORDER, DISABLED_STOCK_CONTROLS, TRANSPARENT, DARK, GOLD, ATTACK, DEFEND, SKILL, DAMAGE_DIGIT_RUIDS, DAMAGE_POP_MAX_DIGITS, DAMAGE_POP_DIGIT_W, DAMAGE_POP_DIGIT_H, DAMAGE_POP_DIGIT_SPACING, MAX_MONSTERS, HEAD_OFFSET_Y, HP_BAR_W, WHITE, CARD_NAME_TEXT, CARD_DESC_TEXT, cardFaceLayout, CARD_W, CARD_H, CARD_SPACING, CARD_XS, ALIGN_CENTER, ALIGN_BOTTOM_CENTER, guid, transform, sprite, button, text, scrollLayoutGroup, popupLayerFor, uiOrderFor, displayOrderFor, applySortingOverride, entity, uiPath, sectionRoot, isGeneratedUiEntity, appendUiSection } from '../lib/ui-helpers.mjs'; export const combatMethods = [ method('CanPlayCardNow', `if c == nil then return false end if c.playableWhenDrawPileEmpty == true and self.DrawPile ~= nil and #self.DrawPile > 0 then self:Toast("뽑을 카드 더미가 비어 있을 때만 사용할 수 있습니다.") return false end return true`, [{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'c' }], 0, 'boolean'), method('PlayCard', `if self:IsDiscardSelecting() == true then self:SelectDiscardSlot(slot) return end if self:IsRetainSelecting() == true then self:SelectRetainSlot(slot) return end if self:IsReserveSelecting() == true then self:SelectReserveSlot(slot) return end if self.CombatOver == true or self.FxBusy == true or self.TurnBusy == true then return end if self.Hand == nil then return end local cardId = self.Hand[slot] if cardId == nil then return end local c = self.Cards[cardId] if c == nil then return end if c.unplayable == true then self:Toast("사용할 수 없는 카드입니다") return end if self:CanPlayCardNow(c) ~= true then return end local cost = c.cost or 0 local skillFree = false local skillRepeat = 0 if self.HandCostZeroThisTurn == true then cost = 0 elseif c.useAllEnergy == true then cost = self.Energy end if c.kind == "Skill" and self.NextSkillCostZero == true then cost = 0 skillFree = true end if c.kind == "Skill" and self.SkillCostReductionThisTurn ~= nil and self.SkillCostReductionThisTurn > 0 then cost = math.max(0, cost - self.SkillCostReductionThisTurn) end if self.CombatCardCostReduction ~= nil and self.CombatCardCostReduction[cardId] ~= nil then cost = math.max(0, cost - self.CombatCardCostReduction[cardId]) end if c.kind == "Skill" and self.NextSkillRepeatCount ~= nil and self.NextSkillRepeatCount > 0 then skillRepeat = self.NextSkillRepeatCount end if self.Energy < cost then self:Toast("에너지가 부족합니다") return end self.Energy = self.Energy - cost self.ActiveKillReward = c.rewardOnKill or 0 self:ResolveCardEffects(cardId, slot, c, false, cost) local function applyCardPlayHooks() if self:HasPowerField("cardPlayedBlock") == true then self:AddCardBlock(self:AddPowerFieldTotal("cardPlayedBlock")) end if c.cardPlayedDamage ~= nil and c.cardPlayedDamage > 0 then self:DealDirectDamageToTarget(c.cardPlayedDamage) end if c.cardPlayedRandomDamage ~= nil and c.cardPlayedRandomDamage > 0 then self:DealDirectDamageToRandomMonster(c.cardPlayedRandomDamage) end end applyCardPlayHooks() if skillRepeat > 0 then local remaining = (self.NextSkillRepeatCount or 0) - skillRepeat if remaining < 0 then remaining = 0 end self.NextSkillRepeatCount = remaining for i = 1, skillRepeat do self:ResolveCardEffects(cardId, slot, c, false, cost) applyCardPlayHooks() end end if c.kind == "Attack" then self.TurnAttackCardsPlayed = (self.TurnAttackCardsPlayed or 0) + 1 end if skillFree == true then if c.nextSkillCostZero ~= true then self.NextSkillCostZero = false end end if self.ActiveKillReward ~= nil and self.ActiveKillReward <= 0 then self.ActiveKillReward = 0 end if c.combatCostReductionOnPlay ~= nil and c.combatCostReductionOnPlay > 0 then if self.CombatCardCostReduction == nil then self.CombatCardCostReduction = {} end self.CombatCardCostReduction[cardId] = (self.CombatCardCostReduction[cardId] or 0) + c.combatCostReductionOnPlay end table.remove(self.Hand, slot) if c.exhaust == true then if self.ExhaustPile == nil then self.ExhaustPile = {} end table.insert(self.ExhaustPile, cardId) elseif c.kind ~= "Power" then table.insert(self.DiscardPile, cardId) end self:RenderHand(false) self:RenderPiles() self:RenderCombat() if self:BeginDiscardSelection(c) == true then return end if self:BeginReserveSelection(c) == true then return end self:RenderHand(false) self:RenderPiles() self:RenderCombat() self:CheckCombatEnd()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), method('OnCardButton', `if self:IsDiscardSelecting() == true then self:SelectDiscardSlot(slot) elseif self:IsRetainSelecting() == true then self:SelectRetainSlot(slot) elseif self:IsReserveSelecting() == true then self:SelectReserveSlot(slot) end`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), method('FindMonsterAtTouch', `local best = 0 local bestDist = 200 for i = 1, #self.Monsters do local m = self.Monsters[i] if m.alive == true and m.entity ~= nil and isvalid(m.entity) and m.entity.TransformComponent ~= nil then local wp = m.entity.TransformComponent.WorldPosition local sp = _UILogic:WorldToScreenPosition(Vector2(wp.x, wp.y + 0.7)) local dx = sp.x - touchPoint.x local dy = sp.y - touchPoint.y local d = math.sqrt(dx * dx + dy * dy) if d < bestDist then bestDist = d best = i end end end return best`, [{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'touchPoint' }], 0, 'number'), method('RenderTargetFrames', `local dragActive = self.DragTargetIndex ~= nil and self.DragTargetIndex > 0 local shownTarget = self.TargetIndex if dragActive == true then shownTarget = self.DragTargetIndex end for i = 1, #self.Monsters do local m = self.Monsters[i] local active = false if m ~= nil and m.alive == true and i == shownTarget then active = true end self:SetEntityEnabled("/ui/RunUIGroup/CombatHud/MonsterStatus" .. tostring(i) .. "/TargetMarker", active and dragActive) self:SetEntityEnabled("/ui/RunUIGroup/CombatHud/MonsterStatus" .. tostring(i) .. "/TargetMarker/Label", active and dragActive) end`), method('OnCardDragBegin', `if self.CombatOver == true or self.FxBusy == true or self.TurnBusy == true then return end if self.Hand == nil or self.Hand[slot] == nil then return end if self.CardHoverTweenId ~= nil and self.CardHoverTweenId ~= 0 then _TimerService:ClearTimer(self.CardHoverTweenId) self.CardHoverTweenId = 0 end for i = 1, 10 do local e = _EntityService:GetEntityByPath("/ui/RunUIGroup/CardHand/Card" .. tostring(i)) if e ~= nil and e.UITransformComponent ~= nil then e.UITransformComponent.UIScale = Vector3(1, 1, 1) e.UITransformComponent.anchoredPosition = Vector2(self:GetHandSlotX(i), 0) end end self.DragSlot = slot self.DragTargetIndex = 0 self:RenderTargetFrames()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), method('OnCardDrag', `if self.DragSlot ~= slot then return end local e = _EntityService:GetEntityByPath("/ui/RunUIGroup/CardHand/Card" .. tostring(slot)) if e ~= nil and e.UITransformComponent ~= nil then local ui = _UILogic:ScreenToUIPosition(touchPoint) e.UITransformComponent.anchoredPosition = Vector2(ui.x, ui.y + 360) end local cardId = self.Hand[slot] local c = nil if cardId ~= nil then c = self.Cards[cardId] end if c ~= nil and c.kind == "Attack" then local best = self:FindMonsterAtTouch(touchPoint) if best ~= self.DragTargetIndex then self.DragTargetIndex = best self:RenderTargetFrames() end end`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }, { Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'touchPoint' }, ]), method('OnCardDragEnd', `if self.DragSlot ~= slot then return end self.DragSlot = 0 local e = _EntityService:GetEntityByPath("/ui/RunUIGroup/CardHand/Card" .. tostring(slot)) if e ~= nil and e.UITransformComponent ~= nil then e.UITransformComponent.anchoredPosition = Vector2(self:GetHandSlotX(slot), 0) e.UITransformComponent.UIScale = Vector3(1, 1, 1) end self:ResolveCardDrop(slot, touchPoint)`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }, { Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'touchPoint' }, ]), method('ResolveCardDrop', `if self:IsDiscardSelecting() == true then self:SelectDiscardSlot(slot) return end if self:IsRetainSelecting() == true then self:SelectRetainSlot(slot) return end if self:IsReserveSelecting() == true then self:SelectReserveSlot(slot) return end if self.CombatOver == true or self.FxBusy == true or self.TurnBusy == true then return end local cardId = self.Hand[slot] if cardId == nil then return end local c = self.Cards[cardId] if c == nil then return end if c.kind == "Attack" then local best = self.DragTargetIndex or 0 if best <= 0 then best = self:FindMonsterAtTouch(touchPoint) end self.DragTargetIndex = 0 if best > 0 then self.TargetIndex = best self:PlayCard(slot) self:RenderTargetFrames() else self:RenderTargetFrames() end else self.DragTargetIndex = 0 self:RenderTargetFrames() local ui = _UILogic:ScreenToUIPosition(touchPoint) if ui.y > -180 then self:PlayCard(slot) end end`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }, { Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'touchPoint' }, ]), method('Toast', `log(message)`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'message' }]), method('DealDamageToTarget', `local m = self.Monsters[self.TargetIndex] if m == nil or m.alive ~= true then m = nil for i = 1, #self.Monsters do if self.Monsters[i].alive == true then m = self.Monsters[i]; self.TargetIndex = i; break end end end if m == nil then return false end local dmg = amount if m.vuln > 0 then dmg = math.floor(dmg * 1.5) end if m.weak > 0 and self.ActiveAttackDamageVsWeakMultiplier ~= nil and self.ActiveAttackDamageVsWeakMultiplier > 1 then dmg = math.floor(dmg * self.ActiveAttackDamageVsWeakMultiplier) end if m.block > 0 and pierce ~= true then local absorbed = math.min(m.block, dmg) m.block = m.block - absorbed dmg = dmg - absorbed end m.hp = m.hp - dmg if dmg > 0 then local poison = self:AddPowerFieldTotal("attackPoison") if poison ~= nil and poison > 0 then self:ApplyPoisonToMonster(m, poison) end end self:MonsterHitMotion(m.slot) local killed = false if m.hp <= 0 then m.hp = 0 self:KillMonster(m.slot) killed = true end return killed`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' }, { Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'pierce' }, ], 0, 'boolean'), method('DealDirectDamageToTarget', `local m = self.Monsters[self.TargetIndex] if m == nil or m.alive ~= true then m = nil for i = 1, #self.Monsters do if self.Monsters[i].alive == true then m = self.Monsters[i]; self.TargetIndex = i; break end end end if m == nil then return false end m.hp = m.hp - amount self:ShowDmgPop(m.slot, amount) self:MonsterHitMotion(m.slot) local killed = false if m.hp <= 0 then m.hp = 0 self:KillMonster(m.slot) killed = true end return killed`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' }, ], 0, 'boolean'), method('DealDirectDamageToRandomMonster', `local alive = {} for i = 1, #self.Monsters do local m = self.Monsters[i] if m ~= nil and m.alive == true then table.insert(alive, m) end end if #alive <= 0 then return false end local m = alive[math.random(1, #alive)] if m == nil then return false end m.hp = m.hp - amount self:ShowDmgPop(m.slot, amount) self:MonsterHitMotion(m.slot) local killed = false if m.hp <= 0 then m.hp = 0 self:KillMonster(m.slot) killed = true end return killed`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' }, ], 0, 'boolean'), method('ApplyPoisonToMonster', `if target == nil or target.alive ~= true or amount == nil or amount <= 0 then return end if target.artifact ~= nil and target.artifact > 0 then target.artifact = target.artifact - 1 return end target.poison = (target.poison or 0) + amount self.PoisonApplicationsThisCombat = (self.PoisonApplicationsThisCombat or 0) + 1 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) end end`, [ { Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'target' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' }, ]), method('DealDamageToAllMonsters', `if self.Monsters == nil then return false end local killCount = 0 for i = 1, #self.Monsters do local m = self.Monsters[i] if m ~= nil and m.alive == true then local dmg = amount if m.vuln > 0 then dmg = math.floor(dmg * 1.5) end if m.block > 0 then local absorbed = math.min(m.block, dmg) m.block = m.block - absorbed dmg = dmg - absorbed end m.hp = m.hp - dmg if dmg > 0 then self.DamageDealtThisTurn = (self.DamageDealtThisTurn or 0) + dmg end self:ShowDmgPop(i, dmg) self:MonsterHitMotion(i) if m.hp <= 0 then m.hp = 0 self:KillMonster(m.slot) killCount = killCount + 1 end end end if killCount > 0 and self.ActiveKillReward ~= nil and self.ActiveKillReward > 0 then self.BonusRewardScreens = (self.BonusRewardScreens or 0) + (killCount * self.ActiveKillReward) end self:RenderCombat() self:CheckCombatEnd() return killCount > 0`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' }, ], 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 self:DealDamageToTarget(damage, pierce) self.ActiveAttackDamageVsWeakMultiplier = 1 self:RenderCombat() self:CheckCombatEnd() return end self.FxBusy = true local fx = _EntityService:GetEntityByPath("/ui/RunUIGroup/CombatHud/SkillFx") if fx ~= nil then if fx.SpriteGUIRendererComponent ~= nil and image ~= nil and image ~= "" then fx.SpriteGUIRendererComponent.ImageRUID = image end if fx.UITransformComponent ~= nil and m.entity.TransformComponent ~= nil then local wp = m.entity.TransformComponent.WorldPosition local sp = _UILogic:WorldToScreenPosition(Vector2(wp.x, wp.y + 0.7)) fx.UITransformComponent.anchoredPosition = _UILogic:ScreenToUIPosition(sp) end fx.Enable = true end _TimerService:SetTimerOnce(function() if fx ~= nil then fx.Enable = false end self.FxBusy = false local shown = damage local mt = self.Monsters[targetIndex] if mt ~= nil and mt.alive == true and mt.vuln > 0 then shown = math.floor(damage * 1.5) end if mt ~= nil and mt.alive == true and mt.weak > 0 and self.ActiveAttackDamageVsWeakMultiplier ~= nil and self.ActiveAttackDamageVsWeakMultiplier > 1 then shown = math.floor(shown * self.ActiveAttackDamageVsWeakMultiplier) end local killed = self:DealDamageToTarget(damage, pierce) if killed == true and self.ActiveKillReward ~= nil and self.ActiveKillReward > 0 then self.BonusRewardScreens = (self.BonusRewardScreens or 0) + self.ActiveKillReward end self.ActiveKillReward = 0 self.ActiveAttackDamageVsWeakMultiplier = 1 self:ShowDmgPop(targetIndex, shown) self:RenderCombat() self:CheckCombatEnd() end, 0.35)`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'targetIndex' }, { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'image' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'damage' }, { Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'pierce' }, ]), method('PlayAoeFx', `self.FxBusy = true local fx = _EntityService:GetEntityByPath("/ui/RunUIGroup/CombatHud/SkillFx") if fx ~= nil then if fx.SpriteGUIRendererComponent ~= nil and image ~= nil and image ~= "" then fx.SpriteGUIRendererComponent.ImageRUID = image end if fx.UITransformComponent ~= nil then fx.UITransformComponent.anchoredPosition = Vector2(300, 60) end fx.Enable = true end _TimerService:SetTimerOnce(function() if fx ~= nil then fx.Enable = false end self.FxBusy = false local killCount = 0 for i = 1, #self.Monsters do local m = self.Monsters[i] if m ~= nil and m.alive == true then local dmg = damage if m.vuln > 0 then dmg = math.floor(dmg * 1.5) end if m.weak > 0 and self.ActiveAttackDamageVsWeakMultiplier ~= nil and self.ActiveAttackDamageVsWeakMultiplier > 1 then dmg = math.floor(dmg * self.ActiveAttackDamageVsWeakMultiplier) end if m.block > 0 then local absorbed = math.min(m.block, dmg) m.block = m.block - absorbed dmg = dmg - absorbed end m.hp = m.hp - dmg if dmg > 0 then local poison = self:AddPowerFieldTotal("attackPoison") if poison ~= nil and poison > 0 then self:ApplyPoisonToMonster(m, poison) end end self:ShowDmgPop(i, dmg) self:MonsterHitMotion(i) if m.hp <= 0 then m.hp = 0 self:KillMonster(m.slot) killCount = killCount + 1 end end end if killCount > 0 and self.ActiveKillReward ~= nil and self.ActiveKillReward > 0 then self.BonusRewardScreens = (self.BonusRewardScreens or 0) + (killCount * self.ActiveKillReward) end self.ActiveKillReward = 0 self.ActiveAttackDamageVsWeakMultiplier = 1 self:RenderCombat() self:CheckCombatEnd() end, 0.35)`, [ { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'image' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'damage' }, ]), method('KillMonster', `local m = self.Monsters[slot] if m == nil then return end m.alive = false if m.entity ~= nil and isvalid(m.entity) then local ent = m.entity _TimerService:SetTimerOnce(function() if isvalid(ent) then ent:SetVisible(false) end end, 0.4) end self:SetEntityEnabled("/ui/RunUIGroup/CombatHud/MonsterStatus" .. tostring(slot), false) for i = 1, #self.Monsters do if self.Monsters[i].alive == true then self.TargetIndex = i; break end end`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), method('DealDamageToPlayer', `local dmg = amount if self.PlayerBlock > 0 then local absorbed = math.min(self.PlayerBlock, dmg) self.PlayerBlock = self.PlayerBlock - absorbed dmg = dmg - absorbed end if dmg > 0 and self.PlayerIntangible ~= nil and self.PlayerIntangible > 0 and dmg > 1 then dmg = 1 end if dmg > 0 then self.PlayerHp = self.PlayerHp - dmg local reflect = self.PlayerThorns or 0 if self:HasRelic("bronzeScales") then reflect = reflect + 3 end if reflect > 0 and attackerSlot ~= nil and attackerSlot > 0 then local am = self.Monsters[attackerSlot] if am ~= nil and am.alive == true then am.hp = am.hp - reflect self:ShowDmgPop(am.slot, reflect) self:MonsterHitMotion(am.slot) if am.hp <= 0 then am.hp = 0 self:KillMonster(am.slot) end end end if self:HasRelic("selfFormingClay") then self.ClayBlockNext = self.ClayBlockNext + 3 end if self:HasRelic("centennialPuzzle") and self.FirstHpLossDone == false then self.FirstHpLossDone = true self:DrawCards(3) self:RenderHand(false) end end if self.PlayerHp < 0 then self.PlayerHp = 0 end`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'attackerSlot' }, ]), method('EnemyTurn', `self.TurnBusy = true self:EnemyActStep(1)`), method('EnemyActStep', `local idx = 0 for i = fromIndex, #self.Monsters do if self.Monsters[i].alive == true then idx = i; break end end if idx == 0 or self.PlayerHp <= 0 then self:FinishEnemyTurn() return end local m = self.Monsters[idx] local base = "/ui/RunUIGroup/CombatHud/MonsterStatus" .. tostring(idx) self:SetEntityEnabled(base .. "/ActFrame", true) _TimerService:SetTimerOnce(function() local poisonTicks = 1 local bonusTicks = self:AddPowerFieldTotal("extraPoisonTicks") if bonusTicks ~= nil and bonusTicks > 0 then poisonTicks = poisonTicks + bonusTicks end for pt = 1, poisonTicks do if m.poison ~= nil and m.poison > 0 then m.hp = m.hp - m.poison self:ShowDmgPop(idx, m.poison) self:MonsterHitMotion(idx) m.poison = m.poison - 1 if m.hp <= 0 then m.hp = 0 self:KillMonster(m.slot) self:RenderCombat() self:SetEntityEnabled(base .. "/ActFrame", false) _TimerService:SetTimerOnce(function() self:EnemyActStep(idx + 1) end, 0.15) return end end end m.block = 0 local intent = m.intents[m.intentIdx] if intent ~= nil then if intent.kind == "Attack" then self:MonsterLunge(idx) local atk = intent.value + m.str if self.EnemyStrengthLossThisTurn ~= nil and self.EnemyStrengthLossThisTurn > 0 then atk = atk - self.EnemyStrengthLossThisTurn if atk < 0 then atk = 0 end end if m.weak > 0 then atk = math.floor(atk * 0.75) end if self.PlayerVuln > 0 then atk = math.floor(atk * 1.5) end local before = self.PlayerHp self:DealDamageToPlayer(atk, idx) self:ShowPlayerDmgPop(before - self.PlayerHp) self:PlayerHitMotion() elseif intent.kind == "Defend" then m.block = m.block + intent.value elseif intent.kind == "Debuff" then if intent.effect == "weak" then self.PlayerWeak = self.PlayerWeak + intent.value elseif intent.effect == "vuln" then self.PlayerVuln = self.PlayerVuln + intent.value end elseif intent.kind == "AddCard" then local cnt = intent.count or 1 for ci = 1, cnt do table.insert(self.DiscardPile, intent.card) end self:RenderPiles() local cn = intent.card local cc = self.Cards[intent.card] if cc ~= nil then cn = cc.name end self:Toast(m.name .. ": " .. cn .. " 추가!") end end if #m.intents > 0 then m.intentIdx = math.random(1, #m.intents) end if m.weak > 0 then m.weak = m.weak - 1 end if m.vuln > 0 then m.vuln = m.vuln - 1 end self:RenderCombat() self:SetEntityEnabled(base .. "/ActFrame", false) _TimerService:SetTimerOnce(function() self:EnemyActStep(idx + 1) end, 0.15) end, 0.45)`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'fromIndex' }]), method('FinishEnemyTurn', `self.TurnBusy = false self:CheckCombatEnd() if self.CombatOver == true then return end _TimerService:SetTimerOnce(function() self:StartPlayerTurn() end, 0.45)`), method('ClearCombatCards', `self.DrawPile = {} self.DiscardPile = {} self.ExhaustPile = {} self.Hand = {} self.DiscardSelectRemaining = 0 self.DiscardSelectTotal = 0 self.DiscardPostShiv = 0 self.DiscardShivPerPick = 0 self.RetainSelectActive = false self.TurnAttackCardsPlayed = 0 self.TurnDiscardedCards = 0 self.ReserveSelectActive = false self.NextTurnBlock = 0 self.NextTurnDraw = 0 self.NextTurnKeepBlock = false self.NextTurnAttackMultiplier = 1 self.TurnAttackMultiplier = 1 self.NextTurnSelectPrompt = "" self.NextTurnSelectCopies = 0 self.NextTurnAddCards = {} self:UpdateDiscardPrompt() self:RenderHand(false) self:RenderPiles()`), method('CheckCombatEnd', `local anyAlive = false for i = 1, #self.Monsters do if self.Monsters[i].alive == true then anyAlive = true; break end end if anyAlive == false then self.CombatOver = true self:ClearCombatCards() self.Gold = self.Gold + math.floor(${GOLD_PER_WIN} * self:AscGoldMult()) self:ApplyRelics("combatEnd") self:ApplyRelics("combatReward") self:MaybeDropPotion() self:RenderRun() local node = self.MapNodes[self.CurrentNodeId] if node ~= nil and node.type == "elite" then self.Gold = self.Gold + 15 local nid = self:PickNewRelic() if nid ~= "" then self:AddRelic(nid) local nr = self.Relics[nid] if nr ~= nil then self:Toast("유물 획득: " .. nr.name) end end end if node ~= nil and node.type == "boss" then if self:CanAdvanceJob() == true and self.Floor < self.RunLength then self:ShowJobChoice() else if self.PlayerJob ~= "" then self:AwardSouls(1) end local bid = self:PickNewRelic() if bid ~= "" then self:AddRelic(bid) local br = self.Relics[bid] if br ~= nil then self:Toast("유물 획득: " .. br.name) end end self:ContinueAfterBoss() end else self:OfferReward() end elseif self.PlayerHp <= 0 then self.CombatOver = true self:EndRun("패배...") end`), method('ContinueAfterBoss', `if self.Floor < self.RunLength then self.Floor = self.Floor + 1 self.CurrentNodeId = "" self.CurrentEnemyId = "" self:GenerateMap() self:RenderRun() self:TeleportToActMap() self:ShowMap() else self:EndRun("런 클리어!") end`), ];