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 handMethods = [ method('ApplyDrawTrigger', `if self.Monsters == nil then return end local drawDamage = self:AddPowerFieldTotal("drawDamage") + (self.DrawDamageThisTurn or 0) local drawPoison = self:AddPowerFieldTotal("drawPoison") + (self.DrawPoisonThisTurn or 0) if (drawDamage ~= nil and drawDamage > 0) or (drawPoison ~= nil and drawPoison > 0) then for mi = 1, #self.Monsters do local m2 = self.Monsters[mi] if m2 ~= nil and m2.alive == true then local dmg = drawDamage or 0 if m2.vuln > 0 then dmg = math.floor(dmg * 1.5) end if m2.block > 0 then local absorbed = math.min(m2.block, dmg) m2.block = m2.block - absorbed dmg = dmg - absorbed end if drawPoison ~= nil and drawPoison > 0 then self:ApplyPoisonToMonster(m2, drawPoison) end if dmg > 0 then m2.hp = m2.hp - dmg self.DamageDealtThisTurn = (self.DamageDealtThisTurn or 0) + dmg end self:ShowDmgPop(mi, dmg) self:MonsterHitMotion(mi) if m2.hp <= 0 then m2.hp = 0 self:KillMonster(m2.slot) end end end self:RenderCombat() self:CheckCombatEnd() end`), method('GetHandSlotX', `local n = 0 if self.Hand ~= nil then n = #self.Hand end if n <= 0 then return 0 end local spacing = 175 if n > 8 then spacing = math.floor(1400 / n) end local startX = -((n - 1) * spacing) / 2 return startX + (slot - 1) * spacing`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }], 0, 'number'), method('RenderHand', `local n = #self.Hand local spacing = 175 if n > 8 then spacing = math.floor(1400 / n) end local startX = -((n - 1) * spacing) / 2 local drawStart = Vector2(-590, 8) for i = 1, 10 do \tlocal cardEntity = _EntityService:GetEntityByPath("/ui/RunUIGroup/CardHand/Card" .. tostring(i)) \tif cardEntity ~= nil then \t\tlocal cardId = self.Hand[i] \t\tif cardId == nil then \t\t\tcardEntity.Enable = false \t\telse \t\t\tcardEntity.Enable = true \t\t\tif cardEntity.UITransformComponent ~= nil then cardEntity.UITransformComponent.UIScale = Vector3(1, 1, 1) end \t\t\tself:ApplyCardVisual(i, cardId) \t\t\tlocal tx = self:GetHandSlotX(i) \t\t\tif animate == true then \t\t\t\tself:AnimateCardFrom(i, drawStart, Vector2(tx, 0), 0.16 + i * 0.03) \t\t\telse \t\t\t\tif cardEntity.UITransformComponent ~= nil then cardEntity.UITransformComponent.anchoredPosition = Vector2(tx, 0) end \t\t\tend \t\tend \tend end self:RenderPiles()`, [{ Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'animate' }]), method('ApplyCardFace', `local c = self.Cards[cardId] if c == nil then c = { name = cardId, cost = 0, desc = "", kind = "Skill", class = "warrior", rarity = "normal" } end local e = _EntityService:GetEntityByPath(base) if e ~= nil and e.SpriteGUIRendererComponent ~= nil then if e.UITransformComponent ~= nil then e.UITransformComponent.UIScale = Vector3(1, 1, 1) end local frames = self.CardFrames[self.ClassToFrame[c.class] or "warrior"] local ruid = nil if frames ~= nil then ruid = frames[c.rarity or "normal"] end if ruid ~= nil then e.SpriteGUIRendererComponent.ImageRUID = ruid e.SpriteGUIRendererComponent.Color = Color(1, 1, 1, 1) end end self:SetText(base .. "/Cost", string.format("%d", c.cost)) self:SetText(base .. "/Name", c.name) self:SetText(base .. "/Desc", self:FormatCardDescription(c.desc)) local art = _EntityService:GetEntityByPath(base .. "/Art") if art ~= nil then if c.image ~= nil and c.image ~= "" then art.Enable = true if art.SpriteGUIRendererComponent ~= nil then art.SpriteGUIRendererComponent.ImageRUID = c.image end else art.Enable = false end end`, [ { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'base' }, { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'cardId' }, ]), method('SetCardHover', `local prefix = "" local count = 0 local xs = {} local baseY = 0 local hoverIndex = 0 local push = 110 if string.find(path, "/ui/RunUIGroup/CardHand/Card") == 1 then if self.DragSlot ~= nil and self.DragSlot > 0 then return end prefix = "/ui/RunUIGroup/CardHand/Card" count = 0 if self.Hand ~= nil then count = #self.Hand end for i = 1, count do xs[i] = self:GetHandSlotX(i) end baseY = 0 hoverIndex = tonumber(string.match(path, "Card(%d+)")) or 0 elseif string.find(path, "/ui/RunUIGroup/RewardHud/Reward") == 1 then prefix = "/ui/RunUIGroup/RewardHud/Reward" count = 3 xs = { -300, 0, 300 } baseY = 0 hoverIndex = tonumber(string.match(path, "Reward(%d+)")) or 0 elseif string.find(path, "/ui/RunUIGroup/ShopHud/Card") == 1 then prefix = "/ui/RunUIGroup/ShopHud/Card" count = 3 xs = { -300, 0, 300 } baseY = 20 hoverIndex = tonumber(string.match(path, "Card(%d+)")) or 0 end if count <= 0 then return end if self.CardHoverTweenId ~= nil and self.CardHoverTweenId ~= 0 then _TimerService:ClearTimer(self.CardHoverTweenId) self.CardHoverTweenId = 0 end local items = {} for i = 1, count do local e = _EntityService:GetEntityByPath(prefix .. tostring(i)) if e ~= nil and e.UITransformComponent ~= nil then local tr = e.UITransformComponent local tx = xs[i] local ty = baseY local sc = 1 if hover == true and hoverIndex > 0 then if i == hoverIndex and e.Enable == true then sc = 1.5 elseif i < hoverIndex then tx = tx - push elseif i > hoverIndex then tx = tx + push end end table.insert(items, { tr = tr, sx = tr.anchoredPosition.x, sy = tr.anchoredPosition.y, ss = tr.UIScale.x, tx = tx, ty = ty, ts = sc }) end end local elapsed = 0 local duration = 0.12 local eventId = 0 eventId = _TimerService:SetTimerRepeat(function() elapsed = elapsed + 1 / 60 local t = math.min(elapsed / duration, 1) local eased = _TweenLogic:Ease(0, 1, 1, EaseType.SineEaseOut, t) for i = 1, #items do local it = items[i] local x = it.sx + (it.tx - it.sx) * eased local y = it.sy + (it.ty - it.sy) * eased local s = it.ss + (it.ts - it.ss) * eased it.tr.anchoredPosition = Vector2(x, y) it.tr.UIScale = Vector3(s, s, 1) end if t >= 1 then _TimerService:ClearTimer(eventId) if self.CardHoverTweenId == eventId then self.CardHoverTweenId = 0 end end end, 1 / 60) self.CardHoverTweenId = eventId`, [ { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'path' }, { Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'hover' }, ]), method('ApplyCardVisual', `self:ApplyCardFace("/ui/RunUIGroup/CardHand/Card" .. tostring(slot), cardId)`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }, { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'cardId' }, ]), method('SetText', `local entity = _EntityService:GetEntityByPath(path) if entity ~= nil and entity.TextComponent ~= nil then \tentity.TextComponent.Text = value end`, [ { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'path' }, { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'value' }, ]), method('FormatNumber', `if value == nil then return "" end local n = tonumber(value) if n == nil then return tostring(value) end if math.abs(n - math.floor(n)) < 0.00001 then return string.format("%d", math.floor(n)) end return tostring(n)`, [{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'value' }], 0, 'string'), method('AnimateCardFrom', `local cardEntity = _EntityService:GetEntityByPath("/ui/RunUIGroup/CardHand/Card" .. tostring(slot)) if cardEntity == nil or cardEntity.UITransformComponent == nil then \treturn end local tr = cardEntity.UITransformComponent tr.anchoredPosition = fromPos local elapsed = 0 local eventId = 0 eventId = _TimerService:SetTimerRepeat(function() \telapsed = elapsed + 1 / 60 \tlocal t = math.min(elapsed / duration, 1) \tlocal eased = _TweenLogic:Ease(0, 1, 1, EaseType.SineEaseOut, t) \ttr.anchoredPosition = Vector2(fromPos.x + (toPos.x - fromPos.x) * eased, fromPos.y + (toPos.y - fromPos.y) * eased) \tif t >= 1 then \t\t_TimerService:ClearTimer(eventId) \tend end, 1 / 60)`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }, { Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'fromPos' }, { Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'toPos' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'duration' }, ]), method('AnimateDiscardCards', `if cardIds == nil or slots == nil then \treturn end local target = Vector2(590, 8) local duration = 0.18 for i = 1, #cardIds do \tlocal slot = slots[i] or i \tlocal e = _EntityService:GetEntityByPath("/ui/RunUIGroup/CardHand/Card" .. tostring(slot)) \tif e ~= nil then \t\te.Enable = true \t\tself:ApplyCardFace("/ui/RunUIGroup/CardHand/Card" .. tostring(slot), cardIds[i]) \t\tif e.UITransformComponent ~= nil then \t\t\tlocal sx = 0 \t\t\tif startXs ~= nil and startXs[i] ~= nil then sx = startXs[i] else sx = self:GetHandSlotX(slot) end \t\t\te.UITransformComponent.anchoredPosition = Vector2(sx, 0) \t\t\te.UITransformComponent.UIScale = Vector3(1, 1, 1) \t\tend \tend end local elapsed = 0 local eventId = 0 eventId = _TimerService:SetTimerRepeat(function() \telapsed = elapsed + 1 / 60 \tlocal t = math.min(elapsed / duration, 1) \tlocal eased = _TweenLogic:Ease(0, 1, 1, EaseType.SineEaseIn, t) \tfor i = 1, #cardIds do \t\tlocal slot = slots[i] or i \t\tlocal e = _EntityService:GetEntityByPath("/ui/RunUIGroup/CardHand/Card" .. tostring(slot)) \t\tif e ~= nil and e.UITransformComponent ~= nil then \t\t\tlocal sx = 0 \t\t\tif startXs ~= nil and startXs[i] ~= nil then sx = startXs[i] else sx = self:GetHandSlotX(slot) end \t\t\tlocal x = sx + (target.x - sx) * eased \t\t\tlocal y = 0 + (target.y - 0) * eased \t\t\tlocal s = 1 - 0.25 * eased \t\t\te.UITransformComponent.anchoredPosition = Vector2(x, y) \t\t\te.UITransformComponent.UIScale = Vector3(s, s, 1) \t\tend \tend \tif t >= 1 then \t\t_TimerService:ClearTimer(eventId) \t\tfor i = 1, #cardIds do \t\t\tlocal slot = slots[i] or i \t\t\tlocal e = _EntityService:GetEntityByPath("/ui/RunUIGroup/CardHand/Card" .. tostring(slot)) \t\t\tif e ~= nil then \t\t\t\tif self.Hand ~= nil and self.Hand[slot] ~= nil then \t\t\t\t\te.Enable = true \t\t\t\t\tself:ApplyCardVisual(slot, self.Hand[slot]) \t\t\t\t\tif e.UITransformComponent ~= nil then \t\t\t\t\t\te.UITransformComponent.anchoredPosition = Vector2(self:GetHandSlotX(slot), 0) \t\t\t\t\t\te.UITransformComponent.UIScale = Vector3(1, 1, 1) \t\t\t\t\tend \t\t\t\telse \t\t\t\t\te.Enable = false \t\t\t\tend \t\t\tend \t\tend \tend end, 1 / 60)`, [ { Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'cardIds' }, { Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'startXs' }, { Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slots' }, ]), method('AddCardBlock', `local amount = base or 0 if amount > 0 and self.PlayerDex ~= nil then amount = amount + self.PlayerDex end if self.BlockGainMultiplier ~= nil and self.BlockGainMultiplier > 1 then amount = amount * self.BlockGainMultiplier end if amount < 0 then amount = 0 end self.PlayerBlock = self.PlayerBlock + amount return amount`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'base' }], 0, 'number'), method('CountOtherHandSkills', `if self.Hand == nil then return 0 end local n = 0 for i = 1, #self.Hand do if i ~= slot then local hc = self.Cards[self.Hand[i]] if hc ~= nil and hc.kind == "Skill" then n = n + 1 end end end return n`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }], 0, 'number'), method('AttackBaseForCard', `local base2 = c.damage or 0 local otherHand = 0 if self.Hand ~= nil then otherHand = #self.Hand - 1 if otherHand < 0 then otherHand = 0 end end if c.damagePerOtherHandCard ~= nil then base2 = base2 + otherHand * c.damagePerOtherHandCard end if c.damagePerAttackPlayedThisTurn ~= nil then base2 = base2 + (self.TurnAttackCardsPlayed or 0) * c.damagePerAttackPlayedThisTurn end if c.damagePerDiscardedThisTurn ~= nil then base2 = base2 + (self.TurnDiscardedCards or 0) * c.damagePerDiscardedThisTurn end if c.damagePerSkillInHand ~= nil then base2 = base2 + self:CountOtherHandSkills(slot) * c.damagePerSkillInHand end if c.damagePerCardDrawnThisCombat ~= nil then base2 = base2 + (self.CardsDrawnThisCombat or 0) * c.damagePerCardDrawnThisCombat end if c.kind == "Attack" and (self.TurnCardsPlayedThisTurn or 0) == 0 and c.firstCardDamageBonus ~= nil then base2 = base2 + c.firstCardDamageBonus end if c.class == "shiv" then if self:HasPowerField("shivDamageBonus") == true then base2 = base2 + self:AddPowerFieldTotal("shivDamageBonus") end if self.ShivFirstDamageBonusUsed ~= true and self:HasPowerField("firstShivDamageBonus") == true then base2 = base2 + self:AddPowerFieldTotal("firstShivDamageBonus") end end if base2 < 0 then base2 = 0 end return base2`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }, { Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'c' }, ], 0, 'number'), method('CalcPlayerAttack', `local base2 = base self.FightAttackCount = self.FightAttackCount + 1 if self.FightAttackCount == 1 and self:HasRelic("akabeko") then base2 = base2 + 8 end local dmg = base2 + self.PlayerStr if self:HasRelic("penNib") and self.FightAttackCount % 10 == 0 then dmg = dmg * 2 end if self.PlayerWeak > 0 then dmg = math.floor(dmg * 0.75) end if self.TurnAttackMultiplier ~= nil and self.TurnAttackMultiplier > 1 then dmg = dmg * self.TurnAttackMultiplier end if dmg > 0 and dmg < 5 and self:HasRelic("boot") then dmg = 5 end if dmg < 0 then dmg = 0 end return dmg`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'base' }], 0, 'number'), method('QueueNextTurnAddCard', `if cardId == nil or cardId == "" or amount == nil or amount <= 0 then return end if self.NextTurnAddCards == nil then self.NextTurnAddCards = {} end for i = 1, #self.NextTurnAddCards do local entry = self.NextTurnAddCards[i] if entry ~= nil and entry.cardId == cardId then entry.amount = (entry.amount or 0) + amount return end end table.insert(self.NextTurnAddCards, { cardId = cardId, amount = amount })`, [ { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'cardId' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' }, ]), method('QueueNextTurnEffects', `if c == nil then return end if c.nextTurnBlock ~= nil then self.NextTurnBlock = (self.NextTurnBlock or 0) + c.nextTurnBlock end if c.nextTurnDraw ~= nil then self.NextTurnDraw = (self.NextTurnDraw or 0) + c.nextTurnDraw end if c.nextTurnKeepBlock == true then self.NextTurnKeepBlock = true end if c.nextTurnAttackMultiplier ~= nil and c.nextTurnAttackMultiplier > 0 then local cur = self.NextTurnAttackMultiplier or 1 self.NextTurnAttackMultiplier = cur * c.nextTurnAttackMultiplier end`, [{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'c' }]), method('ResolveCardEffects', `if c == nil then return end if c.blockGainMultiplier ~= nil and c.blockGainMultiplier > 0 then self.BlockGainMultiplier = (self.BlockGainMultiplier or 1) * c.blockGainMultiplier end if c.nextSkillCostZero == true then self.NextSkillCostZero = true end if c.nextSkillRepeatCount ~= nil and c.nextSkillRepeatCount > 0 then self.NextSkillRepeatCount = (self.NextSkillRepeatCount or 0) + c.nextSkillRepeatCount end if c.skillCostReductionThisTurn ~= nil and c.skillCostReductionThisTurn > 0 then self.SkillCostReductionThisTurn = (self.SkillCostReductionThisTurn or 0) + c.skillCostReductionThisTurn end if c.handCostZeroThisTurn == true then self.HandCostZeroThisTurn = true end if c.drawDisabledThisTurn == true then self.DrawDisabledThisTurn = true end if c.drawDamage ~= nil and c.drawDamage > 0 and c.kind ~= "Power" then self.DrawDamageThisTurn = (self.DrawDamageThisTurn or 0) + c.drawDamage end if c.drawPoison ~= nil and c.drawPoison > 0 and c.kind ~= "Power" then self.DrawPoisonThisTurn = (self.DrawPoisonThisTurn or 0) + c.drawPoison end if c.shivAoe == true and c.kind ~= "Power" then self.ShivAoeThisCombat = true end if c.skillSlyOnPlay == true and c.kind == "Skill" then if self.SkillSlyOnPlayCards == nil then self.SkillSlyOnPlayCards = {} end self.SkillSlyOnPlayCards[cardId] = true end if c.turnHandSlyCount ~= nil and c.turnHandSlyCount > 0 then if self.TurnSkillSlyCards == nil then self.TurnSkillSlyCards = {} end local picked = 0 if self.Hand ~= nil then for i = 1, #self.Hand do local hid = self.Hand[i] if hid ~= nil and hid ~= cardId then local hc = self.Cards[hid] if hc ~= nil and hc.kind == "Skill" and self.TurnSkillSlyCards[hid] ~= true and self.SkillSlyOnPlayCards[hid] ~= true and hc.sly ~= true then self.TurnSkillSlyCards[hid] = true picked = picked + 1 if picked >= c.turnHandSlyCount then break end end end end end end local xEnergy = energySpent or 0 local weakAmount = c.weak or 0 local vulnAmount = c.vuln or 0 local poisonAmount = c.poison or 0 if c.xWeakPerEnergy ~= nil and c.xWeakPerEnergy > 0 then weakAmount = weakAmount + xEnergy * c.xWeakPerEnergy end if c.kind == "Attack" then if c.damage ~= nil or c.xDamagePerEnergy ~= nil then self:PlayerAttackMotion() local baseDmg = self:AttackBaseForCard(slot, c) self.ActiveAttackDamageVsWeakMultiplier = c.attackDamageVsWeakMultiplier or 1 if c.xDamagePerEnergy ~= nil and c.xDamagePerEnergy > 0 then baseDmg = xEnergy * c.xDamagePerEnergy end local total = 0 local hitN = c.hits or 1 if c.otherHandAtLeast ~= nil and c.bonusHitsWhenOtherHandAtLeast ~= nil then local otherHand = 0 if self.Hand ~= nil then otherHand = #self.Hand - 1 if otherHand < 0 then otherHand = 0 end end if otherHand >= c.otherHandAtLeast then hitN = hitN + c.bonusHitsWhenOtherHandAtLeast end end for h = 1, hitN do total = total + self:CalcPlayerAttack(baseDmg) end local useAoe = c.aoe == true if c.class == "shiv" and (self.ShivAoeThisCombat == true or self:HasPowerField("shivAoe") == true) then useAoe = true end 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) 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(total / hitN, c.pierce == true) self.TargetIndex = prev if killed == true then roundKilled = true end 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 end if c.block ~= nil then self:AddCardBlock(c.block) end if free ~= true then self:ApplyRelics("cardPlayed") end elseif c.kind == "Skill" then if c.block ~= nil then self:AddCardBlock(c.block) end elseif c.kind == "Power" then if free ~= true then table.insert(self.PlayerPowers, cardId) end end if c.strength ~= nil then self.PlayerStr = self.PlayerStr + c.strength end if c.dex ~= nil then self.PlayerDex = self.PlayerDex + c.dex end if c.thorns ~= nil then self.PlayerThorns = self.PlayerThorns + c.thorns end if c.selfVuln ~= nil then self.PlayerVuln = self.PlayerVuln + c.selfVuln end if c.heal ~= nil then self.PlayerHp = math.min(self.PlayerHp + c.heal, self.PlayerMaxHp) end if c.gainEnergy ~= nil and c.gainEnergy ~= 0 then self.Energy = self.Energy + c.gainEnergy end if c.intangible ~= nil and c.intangible > 0 then self.PlayerIntangible = (self.PlayerIntangible or 0) + c.intangible end self.TurnCardsPlayedThisTurn = (self.TurnCardsPlayedThisTurn or 0) + 1 if c.blockPerDamageDealtThisTurn ~= nil and c.blockPerDamageDealtThisTurn > 0 then self:AddCardBlock((self.DamageDealtThisTurn or 0) * c.blockPerDamageDealtThisTurn) end self:QueueNextTurnEffects(c) 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 if c.weak ~= nil or c.vuln ~= nil or c.poison ~= nil or c.xWeakPerEnergy ~= nil or c.affectsAllEnemies == true or c.removeEnemyBlock == true or c.removeEnemyArtifact == true or (c.enemyStrengthLossThisTurn ~= nil and c.enemyStrengthLossThisTurn > 0) then local tm = self.Monsters[self.TargetIndex] if tm == nil or tm.alive ~= true then for i = 1, #self.Monsters do if self.Monsters[i].alive == true then tm = self.Monsters[i]; self.TargetIndex = i; break end end end local targets = {} if c.affectsAllEnemies == true and 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(targets, om) end end elseif tm ~= nil and tm.alive == true then table.insert(targets, tm) end if c.enemyStrengthLossThisTurn ~= nil and c.enemyStrengthLossThisTurn > 0 then self.EnemyStrengthLossThisTurn = (self.EnemyStrengthLossThisTurn or 0) + c.enemyStrengthLossThisTurn end for ti = 1, #targets do local target = targets[ti] if target ~= nil and target.alive == true then if c.removeEnemyBlock == true then target.block = 0 end if c.removeEnemyArtifact == true then target.artifact = 0 end if weakAmount ~= nil and weakAmount > 0 then if target.artifact ~= nil and target.artifact > 0 then target.artifact = target.artifact - 1 else target.weak = target.weak + weakAmount end end if poisonAmount ~= nil and poisonAmount > 0 then if c.poisonIfTargetPoisoned ~= true or (target.poison ~= nil and target.poison > 0) then local poisonHits = c.poisonHits or 1 for pi = 1, poisonHits do local target2 = target if c.poisonRandomTargets == true and self.Monsters ~= nil then local alive = {} for mi = 1, #self.Monsters do local om = self.Monsters[mi] if om ~= nil and om.alive == true then table.insert(alive, om) end end if #alive > 0 then target2 = alive[math.random(#alive)] end end if target2 ~= nil and target2.alive == true then self:ApplyPoisonToMonster(target2, poisonAmount) end end end end if vulnAmount ~= nil and vulnAmount > 0 then if target.artifact ~= nil and target.artifact > 0 then target.artifact = target.artifact - 1 else target.vuln = target.vuln + vulnAmount if self:HasRelic("championBelt") then target.weak = target.weak + 1 end end end end end end local drawnCards = {} if c.draw ~= nil then drawnCards = self:DrawCards(c.draw, true) or {} end if c.drawUntilHandSize ~= nil and c.drawUntilHandSize > 0 then local currentHand = 0 if self.Hand ~= nil then currentHand = #self.Hand if slot ~= nil and slot > 0 and self.Hand[slot] == cardId then currentHand = currentHand - 1 end end local need = c.drawUntilHandSize - currentHand if need > 0 then local moreDrawnCards = self:DrawCards(need, true) or {} for i = 1, #moreDrawnCards do table.insert(drawnCards, moreDrawnCards[i]) end end end if c.drawSkillBlock ~= nil and c.drawSkillBlock > 0 then for i = 1, #drawnCards do local drawnCard = self.Cards[drawnCards[i]] if drawnCard ~= nil and drawnCard.kind == "Skill" then self:AddCardBlock(c.drawSkillBlock) end end end if c.addShiv ~= nil and c.discard == nil and c.discardAll ~= true then self:AddCardsToHand("Shiv", c.addShiv) end`, [ { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'cardId' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }, { Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'c' }, { Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'free' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'energySpent' }, ]), method('TriggerSly', `local c = self.Cards[cardId] if c == nil then return end if c.sly ~= true then local onPlay = self.SkillSlyOnPlayCards ~= nil and self.SkillSlyOnPlayCards[cardId] == true local tempSly = self.TurnSkillSlyCards ~= nil and self.TurnSkillSlyCards[cardId] == true if onPlay ~= true and tempSly ~= true then return end end self:Toast("교활 발동: " .. c.name) self:ResolveCardEffects(cardId, 0, c, true, 0)`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'cardId' }]), method('DiscardHandCard', `if self.Hand == nil then return end local cardId = self.Hand[slot] if cardId == nil then return end local startX = self:GetHandSlotX(slot) table.remove(self.Hand, slot) table.insert(self.DiscardPile, cardId) self.TurnDiscardedCards = (self.TurnDiscardedCards or 0) + 1 if triggerSly == true then self:TriggerSly(cardId) end if animate == true then self:AnimateDiscardCards({ cardId }, { startX }, { slot }) end`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }, { Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'triggerSly' }, { Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'animate' }, ]), method('IsDiscardSelecting', `return self.DiscardSelectRemaining ~= nil and self.DiscardSelectRemaining > 0`, [], 0, 'boolean'), method('IsRetainSelecting', `return self.RetainSelectActive == true`, [], 0, 'boolean'), method('IsReserveSelecting', `return self.ReserveSelectActive == true`, [], 0, 'boolean'), method('UpdateDiscardPrompt', `local e = _EntityService:GetEntityByPath("/ui/RunUIGroup/CombatHud/DiscardPrompt") if e == nil then return end if self:IsDiscardSelecting() == true then local picked = self.DiscardSelectTotal - self.DiscardSelectRemaining self:SetText("/ui/RunUIGroup/CombatHud/DiscardPrompt", "버릴 카드 선택 " .. self:FormatNumber(picked + 1) .. "/" .. self:FormatNumber(self.DiscardSelectTotal)) e.Enable = true elseif self:IsRetainSelecting() == true then self:SetText("/ui/RunUIGroup/CombatHud/DiscardPrompt", "보존할 카드 선택 (턴 종료: 건너뛰기)") e.Enable = true elseif self:IsReserveSelecting() == true then local msg = self.NextTurnSelectPrompt or "" if msg == "" then msg = "다음 턴에 예약할 카드를 선택하세요" end self:SetText("/ui/RunUIGroup/CombatHud/DiscardPrompt", msg) e.Enable = true else e.Enable = false end`), method('BeginDiscardSelection', `if c == nil or self.Hand == nil then return false end if c.discardAll == true then return self:AutoDiscardHand(c) end local n = 0 if c.discard ~= nil then n = math.min(c.discard, #self.Hand) end if n <= 0 then return false end self.DiscardSelectRemaining = n self.DiscardSelectTotal = n self.DiscardPostShiv = 0 self.DiscardShivPerPick = 0 self.DiscardPostDraw = 0 self.DiscardDrawPerPick = 0 if c.addShiv ~= nil then self.DiscardPostShiv = c.addShiv end if c.addShivPerDiscard == true then self.DiscardShivPerPick = 1 end if c.drawPerDiscarded ~= nil and c.drawPerDiscarded > 0 then self.DiscardDrawPerPick = c.drawPerDiscarded end self:UpdateDiscardPrompt() self:Toast("버릴 카드를 선택하세요") return true`, [{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'c' }], 0, 'boolean'), method('BeginReserveSelection', `if c == nil or c.nextTurnSelectHandCard ~= true or c.nextTurnCopies == nil or c.nextTurnCopies <= 0 then return false end if self.Hand == nil or #self.Hand <= 0 then return false end self.ReserveSelectActive = true self.NextTurnSelectCopies = c.nextTurnCopies self.NextTurnSelectPrompt = c.nextTurnSelectPrompt or "" self:UpdateDiscardPrompt() self:Toast("예약할 카드를 선택하세요") self:RenderHand(false) return true`, [{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'c' }], 0, 'boolean'), method('SelectReserveSlot', `if self:IsReserveSelecting() ~= true then return false end if self.Hand == nil or self.Hand[slot] == nil then return true end local cardId = self.Hand[slot] local amount = self.NextTurnSelectCopies or 0 self.ReserveSelectActive = false self.NextTurnSelectCopies = 0 self.NextTurnSelectPrompt = "" self:UpdateDiscardPrompt() if amount > 0 and cardId ~= nil then self:QueueNextTurnAddCard(cardId, amount) local label = cardId if self.Cards[cardId] ~= nil and self.Cards[cardId].name ~= nil then label = self.Cards[cardId].name end self:Toast("다음 턴 예약: " .. label .. " " .. self:FormatNumber(amount) .. "장") end self:RenderPiles() self:RenderCombat() return true`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }], 0, 'boolean'), method('SelectRetainSlot', `if self:IsRetainSelecting() ~= true then return false end if self.Hand == nil or self.Hand[slot] == nil then return true end self:FinishPlayerTurn(slot) return true`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }], 0, 'boolean'), method('AutoDiscardHand', `if c == nil or self.Hand == nil or #self.Hand <= 0 then return false end local cardIds = {} local startXs = {} local slots = {} local n = #self.Hand for i = 1, n do local cardId = self.Hand[i] table.insert(cardIds, cardId) table.insert(startXs, self:GetHandSlotX(i)) table.insert(slots, i) table.insert(self.DiscardPile, cardId) self.TurnDiscardedCards = (self.TurnDiscardedCards or 0) + 1 end self.Hand = {} local shivCount = 0 if c.addShiv ~= nil then shivCount = shivCount + c.addShiv end if c.addShivPerDiscard == true then shivCount = shivCount + n end self.DiscardSelectRemaining = 0 self.DiscardSelectTotal = 0 self.DiscardPostShiv = 0 self.DiscardShivPerPick = 0 self.DiscardPostDraw = 0 self.DiscardDrawPerPick = 0 self:UpdateDiscardPrompt() self:AnimateDiscardCards(cardIds, startXs, slots) for i = 1, #cardIds do self:TriggerSly(cardIds[i]) end self:RenderPiles() self:RenderCombat() _TimerService:SetTimerOnce(function() if shivCount > 0 then self:AddCardsToHand("Shiv", shivCount) else self:RenderHand(false) self:RenderPiles() end if c.drawPerDiscarded ~= nil and c.drawPerDiscarded > 0 then self:DrawCards(n * c.drawPerDiscarded, true) end self:RenderCombat() self:CheckCombatEnd() end, 0.22) return true`, [{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'c' }], 0, 'boolean'), method('FinishDiscardSelection', `self.DiscardSelectRemaining = 0 self.DiscardSelectTotal = 0 local shivCount = self.DiscardPostShiv or 0 local drawCount = self.DiscardPostDraw or 0 self.DiscardPostShiv = 0 self.DiscardPostDraw = 0 self.DiscardShivPerPick = 0 self.DiscardDrawPerPick = 0 self:UpdateDiscardPrompt() local finish = function() if shivCount > 0 then self:AddCardsToHand("Shiv", shivCount) else self:RenderHand(false) self:RenderPiles() end if drawCount > 0 then self:DrawCards(drawCount, true) end self:RenderCombat() self:CheckCombatEnd() end if delayRender == true then _TimerService:SetTimerOnce(finish, 0.22) else finish() end`, [{ Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'delayRender' }]), method('SelectDiscardSlot', `if self:IsDiscardSelecting() ~= true then return false end if self.Hand == nil or self.Hand[slot] == nil then return true end local discarded = self.Hand[slot] self:DiscardHandCard(slot, true, true) if discarded ~= nil and self.DiscardShivPerPick ~= nil and self.DiscardShivPerPick > 0 then self.DiscardPostShiv = (self.DiscardPostShiv or 0) + self.DiscardShivPerPick end if discarded ~= nil and self.DiscardDrawPerPick ~= nil and self.DiscardDrawPerPick > 0 then self.DiscardPostDraw = (self.DiscardPostDraw or 0) + self.DiscardDrawPerPick end self.DiscardSelectRemaining = self.DiscardSelectRemaining - 1 if self.DiscardSelectRemaining <= 0 or #self.Hand <= 0 then self:FinishDiscardSelection(true) else self:UpdateDiscardPrompt() self:RenderPiles() self:RenderCombat() _TimerService:SetTimerOnce(function() self:RenderHand(false) end, 0.22) end return true`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }], 0, 'boolean'), ];