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, luaCharsTable, 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 runMethods = [ method('StartRun', `if self.SelectedClass == "magician" then self.PlayerMaxHp = ${CLASSES.magician.maxHp} self.RunDeck = { ${CARDS.starterDecks.magician.map(luaStr).join(', ')} } elseif self.SelectedClass == "bandit" then self.PlayerMaxHp = ${CLASSES.bandit.maxHp} self.RunDeck = { ${CARDS.starterDecks.bandit.map(luaStr).join(', ')} } else self.PlayerMaxHp = ${CLASSES.warrior.maxHp} self.RunDeck = { ${CARDS.starterDecks.warrior.map(luaStr).join(', ')} } end self.PlayerMaxHp = self.PlayerMaxHp - self:AscStartHpPenalty() self.PlayerHp = self.PlayerMaxHp self.Gold = 0 self.Floor = 1 self.RunLength = ${ACT_COUNT} self.RunActive = true self.RunRelics = {} self.RunPotions = {} self.PotionSlots = ${POTIONS.baseSlots} ${luaPotionsTable(POTIONS.potions)} ${luaRelicsTable(RELICS.relics)} self.RelicPool = { ${RELICS.relicPool.map(luaStr).join(', ')} } ${luaEnemiesTable(ENEMIES.enemies)} self.CurrentNodeId = "" self.CurrentEnemyId = "" self.PlayerJob = "" ${luaJobsTable(JOBS)} ${luaFramesTable()} ${luaNodeIconsTable()} ${luaCharsTable()} self:GenerateMap() self:BindButtons() self:AddRelic("${RELICS.startingRelic}") self:ApplySoulUnlocks() self:RenderPotions() self:TeleportToActMap() self:ShowMap()`), method('KickCombatCamera', `local cam = nil local lp = _UserService.LocalPlayer if lp ~= nil then cam = lp.CameraComponent end if cam == nil then cam = _CameraService:GetCurrentCameraComponent() end if cam ~= nil then cam.ConfineCameraArea = false end _TimerService:SetTimerOnce(function() local cc = nil local lp2 = _UserService.LocalPlayer if lp2 ~= nil then cc = lp2.CameraComponent end if cc == nil then cc = _CameraService:GetCurrentCameraComponent() end if cc ~= nil then cc.ZoomRatio = ${CAM.zoomRatio} cc.CameraOffset = Vector2(${CAM.cameraOffsetX}, ${CAM.cameraOffsetY}) cc.ScreenOffset = Vector2(${CAM.screenOffsetX}, ${CAM.screenOffsetY}) cc.ConfineCameraArea = true end end, 0.2)`), method('StartCombat', `self:ShowState("combat") self:KickCombatCamera() self:SetEntityEnabled("/ui/RunUIGroup/CombatHud/Result", false) self:SetEntityEnabled("/ui/RunUIGroup/CombatHud/PotionMenu", false) self:SetEntityEnabled("/ui/RunUIGroup/CombatHud/TooltipBox", false) self:SetEntityEnabled("/ui/RunUIGroup/CombatHud/DiscardPrompt", false) self:SetText("/ui/RunUIGroup/CombatHud/PlayerPanel/Name", self:JobLabel()) self.MaxEnergy = 3 self.Turn = 0 self.PlayerBlock = 0 self.BlockGainMultiplier = 1 self.CardsDrawnThisCombat = 0 self.HandCostZeroThisTurn = false self.DrawDisabledThisTurn = false self.NextSkillCostZero = false self.NextSkillRepeatCount = 0 self.SkillCostReductionThisTurn = 0 self.PlayerStr = 0 self.PlayerDex = 0 self.PlayerThorns = 0 self.PlayerWeak = 0 self.PlayerVuln = 0 self.PlayerIntangible = 0 self.BonusRewardScreens = 0 self.ActiveKillReward = 0 self.PlayerPowers = {} self.FightAttackCount = 0 self.TurnAttackCardsPlayed = 0 self.TurnDiscardedCards = 0 self.DmgPopSeq = 0 self.FirstHpLossDone = false self.ClayBlockNext = 0 self.DiscardSelectRemaining = 0 self.DiscardSelectTotal = 0 self.DiscardPostShiv = 0 self.DiscardShivPerPick = 0 self.RetainSelectActive = false 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.CombatOver = false self.DiscardPile = {} self.ExhaustPile = {} self.Hand = {} ${luaCardsTable(CARDS.cards)} self.DrawPile = {} for i = 1, #self.RunDeck do self.DrawPile[i] = self.RunDeck[i] end self:Shuffle(self.DrawPile) self:PrepareCombatDrawPile() self:BuildMonsters() self:RenderCombat() self:StartPlayerTurn() self:ApplyRelics("combatStart") self:RenderCombat() local slotTid = 0 slotTid = _TimerService:SetTimerRepeat(function() if self.CombatOver == true or self.Monsters == nil or #self.Monsters == 0 then _TimerService:ClearTimer(slotTid) return end for i = 1, #self.Monsters do if self.Monsters[i] ~= nil and self.Monsters[i].alive == true then self:PositionMonsterSlot(i) end end end, 0.15)`), method('RegisterMonster', `if self.Registered == nil then self.Registered = {} end local g = group if g == nil or g == "" then g = "combat" end local mp = mapName if mp == nil then mp = "" end table.insert(self.Registered, { entity = monster, enemyId = enemyId, group = g, map = mp })`, [ { Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'monster' }, { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'enemyId' }, { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'group' }, { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'mapName' }, ]), method('BuildMonsters', `self.Monsters = {} local g = "combat" local node = self.MapNodes[self.CurrentNodeId] if node ~= nil and node.type ~= nil then g = node.type end local pmap = "" local lp = _UserService.LocalPlayer if lp ~= nil and lp.CurrentMapName ~= nil then pmap = lp.CurrentMapName end local reg = self.Registered or {} for i = 1, #reg do if reg[i].entity ~= nil and isvalid(reg[i].entity) then reg[i].entity:SetVisible(false) end end local byGroup = {} for i = 1, #reg do local r = reg[i] if r.entity ~= nil and isvalid(r.entity) and (r.map == nil or r.map == "" or pmap == "" or r.map == pmap) then local gg = r.group if gg == nil or gg == "" then gg = "combat" end if byGroup[gg] == nil then byGroup[gg] = {} end local x = 0 if r.entity.TransformComponent ~= nil then x = r.entity.TransformComponent.WorldPosition.x end table.insert(byGroup[gg], { entity = r.entity, enemyId = r.enemyId, x = x }) end end -- 노드 타입별 랜덤 구성: 일반 1~3 / 엘리트 1+일반0~2 / 보스 1 local chosen = {} local function takeFrom(key, k) local src = byGroup[key] or {} local pool = {} for i = 1, #src do pool[i] = src[i] end self:Shuffle(pool) local taken = 0 for i = 1, #pool do if taken >= k then break end table.insert(chosen, pool[i]) taken = taken + 1 end end if g == "boss" then takeFrom("boss", 1) elseif g == "elite" then takeFrom("elite", 1) takeFrom("combat", math.random(0, 2)) else takeFrom("combat", math.random(1, 3)) end if #chosen == 0 then takeFrom(g, 1) end if #chosen == 0 then takeFrom("combat", 1) end table.sort(chosen, function(a, b) return a.x < b.x end) local mult = 1 + (self.Floor - 1) * 0.45 if g == "elite" or g == "boss" then mult = mult + self:AscEliteBonus() end local n = #chosen if n > ${MAX_MONSTERS} then n = ${MAX_MONSTERS} end for i = 1, n do local item = chosen[i] local e = self.Enemies[item.enemyId] if e == nil then e = { name = item.enemyId, maxHp = 10, intents = { { kind = "Attack", value = 5 } } } end local intents = {} for k = 1, #e.intents do local v = e.intents[k].value or 0 if e.intents[k].kind == "Attack" then v = math.floor(v * mult * self:AscAtkMult()) elseif e.intents[k].kind ~= "Debuff" then v = math.floor(v * mult) end intents[k] = { kind = e.intents[k].kind, value = v, effect = e.intents[k].effect, card = e.intents[k].card, count = e.intents[k].count } end local maxHp = math.floor(e.maxHp * mult * self:AscHpMult()) local hitClip = nil local standClip = nil if item.entity.StateAnimationComponent ~= nil then pcall(function() hitClip = item.entity.StateAnimationComponent.ActionSheet["hit"] standClip = item.entity.StateAnimationComponent.ActionSheet["stand"] end) end local startIdx = 1 if #intents > 0 then startIdx = math.random(1, #intents) end self.Monsters[i] = { entity = item.entity, enemyId = item.enemyId, name = e.name, hp = maxHp, maxHp = maxHp, block = 0, str = 0, weak = 0, vuln = 0, poison = 0, hitClip = hitClip, standClip = standClip, motionBusy = false, intents = intents, intentIdx = startIdx, alive = true, slot = i } self:ReviveMonsterEntity(item.entity) self:PositionMonsterSlot(i) end self.TargetIndex = 1`), method('ReviveMonsterEntity', `if monster == nil or not isvalid(monster) then return end monster:SetEnable(true) monster:SetVisible(true)`, [{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'monster' }]), ];