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 renderMethods = [ method('BuffsLabel', `local parts = {} if str ~= nil and str > 0 then table.insert(parts, "힘+" .. tostring(str)) end if weak ~= nil and weak > 0 then table.insert(parts, "약화" .. tostring(weak)) end if vuln ~= nil and vuln > 0 then table.insert(parts, "취약" .. tostring(vuln)) end if poison ~= nil and poison > 0 then table.insert(parts, "독" .. tostring(poison)) end return table.concat(parts, " ")`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'str' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'weak' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'vuln' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'poison' }, ], 0, 'string'), method('RenderCombat', `for i = 1, ${MAX_MONSTERS} do local base = "/ui/RunUIGroup/CombatHud/MonsterStatus" .. tostring(i) local m = self.Monsters[i] if m ~= nil and m.alive == true then self:SetEntityEnabled(base, true) self:SetText(base .. "/Name", m.name) self:SetText(base .. "/Hp", string.format("%d", m.hp) .. "/" .. string.format("%d", m.maxHp)) local intent = m.intents[m.intentIdx] local t = "" if intent ~= nil then if intent.kind == "Attack" then 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 t = "공격 " .. tostring(atk) elseif intent.kind == "Defend" then t = "방어 " .. tostring(intent.value) elseif intent.kind == "Debuff" then if intent.effect == "weak" then t = "약화 " .. tostring(intent.value) .. " 부여" else t = "취약 " .. tostring(intent.value) .. " 부여" end elseif intent.kind == "AddCard" then t = "저주 카드 추가" end end self:SetText(base .. "/Intent", t) local dragActive = self.DragTargetIndex ~= nil and self.DragTargetIndex > 0 local shownTarget = self.TargetIndex if dragActive == true then shownTarget = self.DragTargetIndex end self:SetEntityEnabled(base .. "/TargetMarker", i == shownTarget and dragActive) self:SetEntityEnabled(base .. "/TargetMarker/Label", i == shownTarget and dragActive) local intentEntity = _EntityService:GetEntityByPath(base .. "/Intent") if intentEntity ~= nil and intentEntity.TextComponent ~= nil and intent ~= nil then if intent.kind == "Attack" then intentEntity.TextComponent.FontColor = Color(1, 0.45, 0.35, 1) elseif intent.kind == "Debuff" then intentEntity.TextComponent.FontColor = Color(0.8, 0.5, 1, 1) elseif intent.kind == "AddCard" then intentEntity.TextComponent.FontColor = Color(0.6, 0.85, 0.4, 1) else intentEntity.TextComponent.FontColor = Color(0.5, 0.75, 1, 1) end end self:SetHpBar(base .. "/HpBarFill", m.hp, m.maxHp, ${HP_BAR_W}) self:SetEntityEnabled(base .. "/BlockBadge", m.block > 0) self:SetText(base .. "/BlockBadge/Value", string.format("%d", m.block)) self:SetText(base .. "/Buffs", self:BuffsLabel(m.str, m.weak, m.vuln, m.poison or 0)) else self:SetEntityEnabled(base, false) end end self:SetText("/ui/RunUIGroup/CombatHud/PlayerPanel/HpText", string.format("%d", self.PlayerHp) .. "/" .. string.format("%d", self.PlayerMaxHp)) self:SetHpBar("/ui/RunUIGroup/CombatHud/PlayerPanel/HpBarFill", self.PlayerHp, self.PlayerMaxHp, 220) self:SetEntityEnabled("/ui/RunUIGroup/CombatHud/PlayerPanel/BlockBadge", self.PlayerBlock > 0) self:SetText("/ui/RunUIGroup/CombatHud/PlayerPanel/BlockBadge/Value", string.format("%d", self.PlayerBlock)) local pb = self:BuffsLabel(self.PlayerStr, self.PlayerWeak, self.PlayerVuln, 0) if self.PlayerDex ~= nil and self.PlayerDex > 0 then if pb ~= "" then pb = pb .. " " end pb = pb .. "민첩+" .. tostring(self.PlayerDex) end if self.PlayerThorns ~= nil and self.PlayerThorns > 0 then if pb ~= "" then pb = pb .. " " end pb = pb .. "가시" .. tostring(self.PlayerThorns) end if self.PlayerPowers ~= nil and #self.PlayerPowers > 0 then local names = {} for i = 1, #self.PlayerPowers do local pc = self.Cards[self.PlayerPowers[i]] if pc ~= nil then table.insert(names, pc.name) end end if pb ~= "" then pb = pb .. " · " end pb = pb .. table.concat(names, " ") 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 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 self:SetEntityEnabled(base, false) end, 0.48)`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' }, ]), method('ShowPlayerDmgPop', `local base = "/ui/RunUIGroup/CombatHud/PlayerPanel/DmgPop" if amount > 0 then self:SetText(base, "-" .. string.format("%d", amount)) else self:SetText(base, "막음") end self:SetEntityEnabled(base, true) _TimerService:SetTimerOnce(function() self:SetEntityEnabled(base, false) end, 0.6)`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' }]), method('PlayerAttackMotion', `local lp = _UserService.LocalPlayer if lp == nil then return end if lp.StateComponent == nil then return end pcall(function() lp.StateComponent:ChangeState("ATTACK") end) _TimerService:SetTimerOnce(function() if lp ~= nil and isvalid(lp) and lp.StateComponent ~= nil then pcall(function() lp.StateComponent:ChangeState("IDLE") end) end end, 0.5)`), method('PlayerHitMotion', `local lp = _UserService.LocalPlayer if lp == nil then return end if lp.StateComponent ~= nil then pcall(function() lp.StateComponent:ChangeState("HIT") end) end local tr = lp.TransformComponent if tr == nil then return end local p = tr.Position tr.Position = Vector3(p.x - 0.15, p.y, p.z) _TimerService:SetTimerOnce(function() if lp ~= nil and isvalid(lp) and lp.TransformComponent ~= nil then lp.TransformComponent.Position = Vector3(p.x, p.y, p.z) end end, 0.15)`), method('MonsterLunge', `local m = self.Monsters[idx] if m == nil or m.alive ~= true or m.entity == nil or not isvalid(m.entity) then return end if m.motionBusy == true then return end m.motionBusy = true local e = m.entity local tr = e.TransformComponent if tr == nil then m.motionBusy = false return end local p = tr.Position tr.Position = Vector3(p.x - 0.35, p.y, p.z) _TimerService:SetTimerOnce(function() if isvalid(e) and e.TransformComponent ~= nil then e.TransformComponent.Position = Vector3(p.x, p.y, p.z) end m.motionBusy = false end, 0.18)`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'idx' }]), method('MonsterHitMotion', `local m = self.Monsters[slot] if m == nil or m.alive ~= true or m.entity == nil or not isvalid(m.entity) then return end local e = m.entity if m.hitClip ~= nil and e.SpriteRendererComponent ~= nil then e.SpriteRendererComponent.SpriteRUID = m.hitClip _TimerService:SetTimerOnce(function() if isvalid(e) and e.SpriteRendererComponent ~= nil and m.alive == true and m.standClip ~= nil then e.SpriteRendererComponent.SpriteRUID = m.standClip end end, 0.5) else if m.motionBusy == true then return end m.motionBusy = true local tr = e.TransformComponent if tr == nil then m.motionBusy = false return end local p = tr.Position local seq = { 0.12, -0.12, 0 } for i = 1, #seq do local dx = seq[i] _TimerService:SetTimerOnce(function() if isvalid(e) and e.TransformComponent ~= nil then e.TransformComponent.Position = Vector3(p.x + dx, p.y, p.z) end if i == #seq then m.motionBusy = false end end, 0.06 * i) end end`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), method('SetHpBar', `local e = _EntityService:GetEntityByPath(path) if e == nil or e.UITransformComponent == nil then return end local ratio = 0 if maxHp > 0 then ratio = hp / maxHp end if ratio < 0 then ratio = 0 end local w = width * ratio e.UITransformComponent.RectSize = Vector2(w, 14)`, [ { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'path' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'hp' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'maxHp' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'width' }, ]), method('PositionMonsterSlot', `local m = self.Monsters[slot] if m == nil or m.entity == nil or not isvalid(m.entity) then return end local tr = m.entity.TransformComponent if tr == nil then return end local wp = tr.WorldPosition local screen = _UILogic:WorldToScreenPosition(Vector2(wp.x, wp.y + ${HEAD_OFFSET_Y})) local uipos = _UILogic:ScreenToUIPosition(screen) local e = _EntityService:GetEntityByPath("/ui/RunUIGroup/CombatHud/MonsterStatus" .. tostring(slot)) if e ~= nil and e.UITransformComponent ~= nil then e.UITransformComponent.anchoredPosition = uipos end`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), method('SetTarget', `if self.Monsters[slot] ~= nil and self.Monsters[slot].alive == true then self.TargetIndex = slot self:RenderCombat() end`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), method('RenderRun', `local floorText = "막 " .. string.format("%d", self.Floor) .. "/" .. string.format("%d", self.RunLength) .. " · " .. string.format("%d", self.Depth) .. "층" if self.AscensionLevel > 0 then floorText = floorText .. " · 승천" .. string.format("%d", self.AscensionLevel) end self:SetText("/ui/RunUIGroup/CombatHud/TopBar/Floor", floorText) self:SetText("/ui/RunUIGroup/CombatHud/TopBar/Gold", "메소 " .. string.format("%d", self.Gold))`), ];