637 lines
21 KiB
JavaScript
637 lines
21 KiB
JavaScript
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
|
|
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.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)
|
|
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: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
|
|
if self.ActiveKillReward ~= nil and self.ActiveKillReward <= 0 then
|
|
self.ActiveKillReward = 0
|
|
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.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
|
|
m.poison = (m.poison or 0) + 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('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: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
|
|
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: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.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
|
|
m.poison = (m.poison or 0) + 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: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()
|
|
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
|
|
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 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.PlayerJob == "" 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`),
|
|
];
|