diff --git a/tools/deck/gen-slaydeck.mjs b/tools/deck/gen-slaydeck.mjs index 0ff0f1b..4327522 100644 --- a/tools/deck/gen-slaydeck.mjs +++ b/tools/deck/gen-slaydeck.mjs @@ -17,14 +17,14 @@ for (const cls of Object.keys(CLASSES)) { // 전직 옵션 (클래스별 2차 — JobSelectHud 동적 구성·SetJob 대표 카드) const JOBS = { warrior: [ - { id: 'fighter', name: '파이터', desc: '공격 특화\\n콤보 어택 · 버서크\\n라이징 어택', starter: 'ComboAttack' }, - { id: 'page', name: '페이지', desc: '속성 차지 특화\\n썬더/블리자드 차지\\n파워 가드', starter: 'ThunderCharge' }, - { id: 'spearman', name: '스피어맨', desc: '방어·관통 특화\\n피어스 · 아이언 월\\n하이퍼 바디', starter: 'Pierce' }, + { id: 'fighter', name: '파이터', desc: '공격 특화\n콤보 어택 · 버서크\n라이징 어택', starter: 'ComboAttack' }, + { id: 'page', name: '페이지', desc: '속성 차지 특화\n썬더/블리자드 차지\n파워 가드', starter: 'ThunderCharge' }, + { id: 'spearman', name: '스피어맨', desc: '방어·관통 특화\n피어스 · 아이언 월\n하이퍼 바디', starter: 'Pierce' }, ], magician: [ - { id: 'firepoison', name: '위자드(불·독)', desc: '화염·독 특화\\n파이어 애로우\\n포이즌 브레스 · 앰플', starter: 'FireArrow' }, - { id: 'icelightning', name: '위자드(썬·콜)', desc: '광역·빙결 특화\\n썬더 볼트(전체)\\n콜드 빔 · 칠링 스텝', starter: 'ThunderBolt' }, - { id: 'cleric', name: '클레릭', desc: '회복·축복 특화\\n힐 · 블레스\\n홀리 애로우', starter: 'Heal' }, + { id: 'firepoison', name: '위자드(불·독)', desc: '화염·독 특화\n파이어 애로우\n포이즌 브레스 · 앰플', starter: 'FireArrow' }, + { id: 'icelightning', name: '위자드(썬·콜)', desc: '광역·빙결 특화\n썬더 볼트(전체)\n콜드 빔 · 칠링 스텝', starter: 'ThunderBolt' }, + { id: 'cleric', name: '클레릭', desc: '회복·축복 특화\n힐 · 블레스\n홀리 애로우', starter: 'Heal' }, ], }; for (const [cls, jobs] of Object.entries(JOBS)) { @@ -79,7 +79,14 @@ function luaEnemiesTable(enemies) { } // Lua 직렬화 헬퍼 function luaStr(s) { - return '"' + String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"'; + return '"' + String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"'; +} +function luaJobsTable(jobs) { + const cls = Object.entries(jobs).map(([clsId, list]) => { + const items = list.map((j) => `\t\t{ id = ${luaStr(j.id)}, name = ${luaStr(j.name)}, desc = ${luaStr(j.desc)}, starter = ${luaStr(j.starter)} },`).join('\n'); + return `\t${clsId} = {\n${items}\n\t},`; + }).join('\n'); + return `self.Jobs = {\n${cls}\n}`; } function luaCardsTable(cards) { const lines = Object.entries(cards).map(([id, c]) => { @@ -1979,10 +1986,11 @@ function upsertUi() { text({ value: '2차 전직 — 직업을 선택하세요', fontSize: 36, bold: true, color: GOLD, alignment: 4 }), ], })); + // 범용 슬롯 3개 — ShowJobSelect(Lua)가 클래스별 JOBS로 텍스트를 채움 (P10 동적화) const jobs = [ - ['fighter', '파이터', '공격 특화\n콤보 어택 · 버서크\n라이징 어택', '대표 카드: 콤보 어택', -440, { r: 0.82, g: 0.4, b: 0.34, a: 1 }], - ['page', '페이지', '속성 차지 특화\n썬더/블리자드 차지\n파워 가드', '대표 카드: 썬더 차지', 0, { r: 0.4, g: 0.55, b: 0.85, a: 1 }], - ['spearman', '스피어맨', '방어·관통 특화\n피어스 · 아이언 월\n하이퍼 바디', '대표 카드: 피어스', 440, { r: 0.42, g: 0.72, b: 0.46, a: 1 }], + ['slot1', '', '', '', -440, { r: 0.82, g: 0.4, b: 0.34, a: 1 }], + ['slot2', '', '', '', 0, { r: 0.4, g: 0.55, b: 0.85, a: 1 }], + ['slot3', '', '', '', 440, { r: 0.42, g: 0.72, b: 0.46, a: 1 }], ]; jobs.forEach(([jobId, name, desc, starter, x, color], ji) => { const base = `/ui/DefaultGroup/JobSelectHud/Job_${jobId}`; @@ -2170,7 +2178,7 @@ function upsertUi() { const classCards = [ { key: 'Warrior', label: '\uC804\uC0AC', desc: '\uAC15\uD55C \uACF5\uACA9\uACFC \uBC29\uC5B4', x: -360, enabled: true, tint: { r: 0.74, g: 0.32, b: 0.28, a: 1 } }, { key: 'Thief', label: '\uB3C4\uC801', desc: '\uCD94\uD6C4 \uC5F4\uB9BC', x: 0, enabled: false, tint: { r: 0.18, g: 0.19, b: 0.21, a: 1 } }, - { key: 'Mage', label: '\uB9C8\uBC95\uC0AC', desc: '\uCD94\uD6C4 \uC5F4\uB9BC', x: 360, enabled: false, tint: { r: 0.18, g: 0.19, b: 0.21, a: 1 } }, + { key: 'Mage', label: '\uB9C8\uBC95\uC0AC', desc: '\uB9C8\uBC95 \uC6D0\uAC70\uB9AC \uB51C\uB7EC', x: 360, enabled: true, tint: { r: 0.3, g: 0.4, b: 0.75, a: 1 } }, ]; for (let i = 0; i < classCards.length; i++) { const cls = classCards[i]; @@ -2351,6 +2359,8 @@ function writeCodeblocks() { prop('any', 'EndTurnHandler'), prop('any', 'NewGameHandler'), prop('any', 'WarriorSelectHandler'), + prop('any', 'MageSelectHandler'), + prop('any', 'JobOpts'), prop('any', 'StartGameHandler'), prop('string', 'SelectedClass', '""'), prop('any', 'DrawPileHandler'), @@ -2461,6 +2471,14 @@ if warrior ~= nil and warrior.ButtonComponent ~= nil then end self.WarriorSelectHandler = warrior:ConnectEvent(ButtonClickEvent, function() self:SelectClass("warrior") end) end +local mage = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/MageButton") +if mage ~= nil and mage.ButtonComponent ~= nil then + if self.MageSelectHandler ~= nil then + mage:DisconnectEvent(ButtonClickEvent, self.MageSelectHandler) + self.MageSelectHandler = nil + end + self.MageSelectHandler = mage:ConnectEvent(ButtonClickEvent, function() self:SelectClass("magician") end) +end local start = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/StartButton") if start ~= nil and start.ButtonComponent ~= nil then if self.StartGameHandler ~= nil then @@ -2484,13 +2502,23 @@ if warrior ~= nil and warrior.SpriteGUIRendererComponent ~= nil then warrior.SpriteGUIRendererComponent.Color = Color(0.16, 0.2, 0.26, 1) end end +local mage = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/MageButton") +if mage ~= nil and mage.SpriteGUIRendererComponent ~= nil then + if self.SelectedClass == "magician" then + mage.SpriteGUIRendererComponent.Color = Color(0.28, 0.36, 0.46, 1) + else + mage.SpriteGUIRendererComponent.Color = Color(0.16, 0.2, 0.26, 1) + end +end if self.SelectedClass == "warrior" then self:SetText("/ui/DefaultGroup/CharacterSelectHud/Status", "전사 선택됨") +elseif self.SelectedClass == "magician" then + self:SetText("/ui/DefaultGroup/CharacterSelectHud/Status", "마법사 선택됨") else - self:SetText("/ui/DefaultGroup/CharacterSelectHud/Status", "전사를 선택하고 시작하세요") + self:SetText("/ui/DefaultGroup/CharacterSelectHud/Status", "직업을 선택하고 시작하세요") end`), - method('StartNewGame', `if self.SelectedClass ~= "warrior" then - self:SetText("/ui/DefaultGroup/CharacterSelectHud/Status", "현재는 전사만 선택할 수 있습니다") + method('StartNewGame', `if self.SelectedClass ~= "warrior" and self.SelectedClass ~= "magician" then + self:SetText("/ui/DefaultGroup/CharacterSelectHud/Status", "직업을 먼저 선택하세요") return end self:StartRun()`), @@ -2523,6 +2551,7 @@ ${luaEnemiesTable(ENEMIES.enemies)} self.CurrentNodeId = "" self.CurrentEnemyId = "" self.PlayerJob = "" +${luaJobsTable(JOBS)} self:GenerateMap() self:BindButtons() self:AddRelic("${RELICS.startingRelic}") @@ -2795,12 +2824,15 @@ local jcJob = _EntityService:GetEntityByPath("/ui/DefaultGroup/JobChoiceHud/JobB if jcJob ~= nil and jcJob.ButtonComponent ~= nil then jcJob:ConnectEvent(ButtonClickEvent, function() self:PickJobReward("job") end) end -local jobIds = { "fighter", "page", "spearman" } -for i = 1, #jobIds do - local jid = jobIds[i] - local jb = _EntityService:GetEntityByPath("/ui/DefaultGroup/JobSelectHud/Job_" .. jid) +for i = 1, 3 do + local slotIdx = i + local jb = _EntityService:GetEntityByPath("/ui/DefaultGroup/JobSelectHud/Job_slot" .. tostring(i)) if jb ~= nil and jb.ButtonComponent ~= nil then - jb:ConnectEvent(ButtonClickEvent, function() self:SetJob(jid) end) + jb:ConnectEvent(ButtonClickEvent, function() + if self.JobOpts ~= nil and self.JobOpts[slotIdx] ~= nil then + self:SetJob(self.JobOpts[slotIdx].id) + end + end) end end`), method('StartPlayerTurn', `self.Turn = self.Turn + 1 @@ -3507,27 +3539,51 @@ if kind == "relic" then end self:ContinueAfterBoss() else - self:SetEntityEnabled("/ui/DefaultGroup/JobSelectHud", true) + self:ShowJobSelect() end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'kind' }]), - method('JobLabel', `if self.PlayerJob == "fighter" then - return "파이터" -elseif self.PlayerJob == "page" then - return "페이지" -elseif self.PlayerJob == "spearman" then - return "스피어맨" + method('ShowJobSelect', `local opts = self.Jobs[self.SelectedClass] +if opts == nil then + opts = self.Jobs["warrior"] +end +self.JobOpts = opts +for i = 1, 3 do + local base = "/ui/DefaultGroup/JobSelectHud/Job_slot" .. tostring(i) + local o = opts[i] + if o ~= nil then + self:SetEntityEnabled(base, true) + self:SetText(base .. "/Name", o.name) + self:SetText(base .. "/Desc", o.desc) + local sc = self.Cards[o.starter] + if sc ~= nil then + self:SetText(base .. "/Starter", "대표 카드: " .. sc.name) + end + else + self:SetEntityEnabled(base, false) + end +end +self:SetEntityEnabled("/ui/DefaultGroup/JobSelectHud", true)`), + method('JobLabel', `if self.PlayerJob ~= "" and self.Jobs ~= nil then + for cls, list in pairs(self.Jobs) do + for i = 1, #list do + if list[i].id == self.PlayerJob then + return list[i].name + end + end + end end if self.SelectedClass == "warrior" then return "전사" +elseif self.SelectedClass == "magician" then + return "마법사" end return "플레이어"`, [], 0, 'string'), method('SetJob', `self.PlayerJob = jobId local starter = "" -if jobId == "fighter" then - starter = "ComboAttack" -elseif jobId == "page" then - starter = "ThunderCharge" -elseif jobId == "spearman" then - starter = "Pierce" +local opts = self.Jobs[self.SelectedClass] or {} +for i = 1, #opts do + if opts[i].id == jobId then + starter = opts[i].starter + end end if starter ~= "" then table.insert(self.RunDeck, starter)