feat(magician): 캐릭터 선택 오픈·전직 화면 동적화 (생성기)
- Mage 버튼 활성·SelectClass(magician)·2클래스 하이라이트/가드 - JobSelectHud Job_slot1..3 범용화 + ShowJobSelect(클래스별 JOBS 채움) - SetJob/JobLabel을 Jobs 테이블 기반으로 (luaJobsTable 주입, luaStr 개행 이스케이프) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user