feat(bandit): 사일런트 도적 덱 추가
- 도적 시작 직업을 선택 화면에서 활성화하고 bandit 스타터 덱으로 런을 시작하도록 생성기를 연결 - Slay the Spire 사일런트 카드 75장을 bandit 카드 풀에 추가하고 카드명/설명을 한글화 - 현재 전투 엔진이 지원하는 피해, 방어도, 드로우, 독, 약화, 취약, 광역, 다단히트, 회복, 파워 효과로 카드 효과를 매핑 - 도적 스타터 덱을 타격 5장, 수비 5장, 무력화, 생존자로 구성 - bandit 및 도적 전직 계열(shiv, poisoner, trickster)을 카드 프레임 매핑에 연결 - ui/DefaultGroup.ui와 SlayDeckController.codeblock을 생성기로 재생성 검증: - node --check tools/deck/gen-slaydeck.mjs - node --test tools/balance/sim-balance.test.mjs tools/map/rogue-map.test.mjs - 도적 카드 75장 및 한글화 잔여 영어/깨짐 없음 확인
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
|
||||
const CARDS = JSON.parse(readFileSync('data/cards.json', 'utf8'));
|
||||
const ENEMIES = JSON.parse(readFileSync('data/enemies.json', 'utf8'));
|
||||
@@ -6,6 +6,7 @@ const ENEMIES = JSON.parse(readFileSync('data/enemies.json', 'utf8'));
|
||||
// 검증 (fail-fast): 잘못된 데이터면 생성 중단
|
||||
const CLASSES = {
|
||||
warrior: { label: '전사', maxHp: 80 },
|
||||
bandit: { label: '도적', maxHp: 70 },
|
||||
magician: { label: '마법사', maxHp: 70 },
|
||||
};
|
||||
for (const cls of Object.keys(CLASSES)) {
|
||||
@@ -26,6 +27,11 @@ const JOBS = {
|
||||
{ id: 'icelightning', name: '위자드(썬·콜)', desc: '광역·빙결 특화\n썬더 볼트(전체)\n콜드 빔 · 칠링 스텝', starter: 'ThunderBolt' },
|
||||
{ id: 'cleric', name: '클레릭', desc: '회복·축복 특화\n힐 · 블레스\n홀리 애로우', starter: 'Heal' },
|
||||
],
|
||||
bandit: [
|
||||
{ id: 'shiv', name: 'Shiv', desc: 'Many small attacks\nBlade Dance\nAccuracy · After Image', starter: 'BladeDance' },
|
||||
{ id: 'poisoner', name: 'Poison', desc: 'Poison scaling\nDeadly Poison\nCatalyst · Noxious Fumes', starter: 'DeadlyPoison' },
|
||||
{ id: 'trickster', name: 'Trickster', desc: 'Draw and tempo\nAcrobatics\nAdrenaline · Tools', starter: 'Acrobatics' },
|
||||
],
|
||||
};
|
||||
for (const [cls, jobs] of Object.entries(JOBS)) {
|
||||
for (const j of jobs) {
|
||||
@@ -2242,7 +2248,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: 'Thief', label: '\uB3C4\uC801', desc: 'Silent deck', x: 0, enabled: true, tint: { r: 0.26, g: 0.5, b: 0.34, 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++) {
|
||||
@@ -2432,6 +2438,7 @@ function writeCodeblocks() {
|
||||
prop('any', 'EndTurnHandler'),
|
||||
prop('any', 'NewGameHandler'),
|
||||
prop('any', 'WarriorSelectHandler'),
|
||||
prop('any', 'ThiefSelectHandler'),
|
||||
prop('any', 'MageSelectHandler'),
|
||||
prop('any', 'AscMinusHandler'),
|
||||
prop('any', 'AscPlusHandler'),
|
||||
@@ -2602,6 +2609,14 @@ if warrior ~= nil and warrior.ButtonComponent ~= nil then
|
||||
end
|
||||
self.WarriorSelectHandler = warrior:ConnectEvent(ButtonClickEvent, function() self:SelectClass("warrior") end)
|
||||
end
|
||||
local thief = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/ThiefButton")
|
||||
if thief ~= nil and thief.ButtonComponent ~= nil then
|
||||
if self.ThiefSelectHandler ~= nil then
|
||||
thief:DisconnectEvent(ButtonClickEvent, self.ThiefSelectHandler)
|
||||
self.ThiefSelectHandler = nil
|
||||
end
|
||||
self.ThiefSelectHandler = thief:ConnectEvent(ButtonClickEvent, function() self:SelectClass("bandit") end)
|
||||
end
|
||||
local mage = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/MageButton")
|
||||
if mage ~= nil and mage.ButtonComponent ~= nil then
|
||||
if self.MageSelectHandler ~= nil then
|
||||
@@ -2657,14 +2672,24 @@ if mage ~= nil and mage.SpriteGUIRendererComponent ~= nil then
|
||||
mage.SpriteGUIRendererComponent.Color = Color(0.16, 0.2, 0.26, 1)
|
||||
end
|
||||
end
|
||||
local thief = _EntityService:GetEntityByPath("/ui/DefaultGroup/CharacterSelectHud/ThiefButton")
|
||||
if thief ~= nil and thief.SpriteGUIRendererComponent ~= nil then
|
||||
if self.SelectedClass == "bandit" then
|
||||
thief.SpriteGUIRendererComponent.Color = Color(0.28, 0.36, 0.46, 1)
|
||||
else
|
||||
thief.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 == "bandit" 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", "직업을 선택하고 시작하세요")
|
||||
end`),
|
||||
method('StartNewGame', `if self.SelectedClass ~= "warrior" and self.SelectedClass ~= "magician" then
|
||||
method('StartNewGame', `if self.SelectedClass ~= "warrior" and self.SelectedClass ~= "bandit" and self.SelectedClass ~= "magician" then
|
||||
self:SetText("/ui/DefaultGroup/CharacterSelectHud/Status", "직업을 먼저 선택하세요")
|
||||
return
|
||||
end
|
||||
@@ -2679,6 +2704,9 @@ end`, [
|
||||
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(', ')} }
|
||||
@@ -3745,6 +3773,8 @@ self:SetEntityEnabled("/ui/DefaultGroup/JobSelectHud", true)`),
|
||||
end
|
||||
if self.SelectedClass == "warrior" then
|
||||
return "전사"
|
||||
elseif self.SelectedClass == "bandit" then
|
||||
return "도적"
|
||||
elseif self.SelectedClass == "magician" then
|
||||
return "마법사"
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user