feat(card-frames): 생성기 — 프레임 렌더링·cardFaceLayout 통합 (5개 카드 사이트·단색판 제거)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -36,6 +36,29 @@ if (!ENEMIES.enemies[ENEMIES.activeEnemy]) {
|
||||
throw new Error(`[gen-slaydeck] activeEnemy가 enemies에 없음: ${ENEMIES.activeEnemy}`);
|
||||
}
|
||||
|
||||
// 카드 프레임 (사용자 제작 이미지 — 로컬 임포트 .sprite RUID, 직업 3종 × 등급 3종)
|
||||
const CARDFRAMES = JSON.parse(readFileSync('data/cardframes.json', 'utf8'));
|
||||
const RARITIES = ['normal', 'unique', 'legend'];
|
||||
for (const [fid, fr] of Object.entries(CARDFRAMES.frames)) {
|
||||
for (const r of RARITIES) {
|
||||
if (!fr[r]) throw new Error(`[gen-slaydeck] cardframes.frames.${fid}.${r} RUID 없음`);
|
||||
}
|
||||
}
|
||||
for (const [id, c] of Object.entries(CARDS.cards)) {
|
||||
if (!RARITIES.includes(c.rarity)) throw new Error(`[gen-slaydeck] 카드 ${id} rarity 누락/오류: ${c.rarity}`);
|
||||
const fc = CARDFRAMES.classToFrame[c.class];
|
||||
if (!fc || !CARDFRAMES.frames[fc]) throw new Error(`[gen-slaydeck] 카드 ${id} class ${c.class} → 프레임 매핑 없음`);
|
||||
}
|
||||
function frameRuid(card) {
|
||||
return CARDFRAMES.frames[CARDFRAMES.classToFrame[card.class]][card.rarity];
|
||||
}
|
||||
function luaFramesTable() {
|
||||
const frames = Object.entries(CARDFRAMES.frames).map(([fid, fr]) =>
|
||||
`\t${fid} = { normal = ${luaStr(fr.normal)}, unique = ${luaStr(fr.unique)}, legend = ${luaStr(fr.legend)} },`).join('\n');
|
||||
const cls = Object.entries(CARDFRAMES.classToFrame).map(([c, f]) => `\t${c} = ${luaStr(f)},`).join('\n');
|
||||
return `self.CardFrames = {\n${frames}\n}\nself.ClassToFrame = {\n${cls}\n}`;
|
||||
}
|
||||
|
||||
// 맵은 런타임 절차 생성(GenerateMap Lua ↔ tools/map/rogue-map.mjs 미러). 정적 data/map.json 제거됨.
|
||||
const MAP_ROWS = 7; // 걷는 행 1..7, 보스 row 8
|
||||
const MAP_COLS = 4;
|
||||
@@ -100,6 +123,7 @@ function luaCardsTable(cards) {
|
||||
if (c.value != null) fields.push(`value = ${c.value}`);
|
||||
if (!c.class) throw new Error(`[gen-slaydeck] 카드 ${id}에 class 누락`);
|
||||
fields.push(`class = ${luaStr(c.class)}`);
|
||||
fields.push(`rarity = ${luaStr(c.rarity)}`);
|
||||
if (c.hits != null) fields.push(`hits = ${c.hits}`);
|
||||
if (c.pierce === true) fields.push('pierce = true');
|
||||
if (c.selfVuln != null) fields.push(`selfVuln = ${c.selfVuln}`);
|
||||
@@ -162,6 +186,21 @@ const MAX_MONSTERS = 4;
|
||||
const HEAD_OFFSET_Y = 1.4; // 몬스터 월드 원점 위로 띄울 높이(머리 위) — world→screen 변환 전 가산
|
||||
|
||||
const HP_BAR_W = 140;
|
||||
const WHITE = { r: 1, g: 1, b: 1, a: 1 };
|
||||
const INK = { r: 0.13, g: 0.11, b: 0.09, a: 1 }; // 밝은 배너·설명 박스 위 먹색 글자
|
||||
// 카드 프레임(263×366 원본, 0.72 비율) 슬롯 레이아웃 — 180×250 기준값을 폭 비례 스케일
|
||||
function cardFaceLayout(W) {
|
||||
const s = W / 180;
|
||||
const r = (v) => Math.round(v * s);
|
||||
return {
|
||||
texts: [
|
||||
['Cost', { size: { x: r(44), y: r(44) }, pos: { x: r(-68), y: r(103) }, fontSize: r(26), bold: true, color: WHITE }],
|
||||
['Name', { size: { x: r(150), y: r(26) }, pos: { x: r(4), y: r(97) }, fontSize: r(18), bold: true, color: INK }],
|
||||
['Desc', { size: { x: r(152), y: r(64) }, pos: { x: 0, y: r(-85) }, fontSize: r(16), bold: false, color: INK }],
|
||||
],
|
||||
art: { size: { x: r(110), y: r(110) }, pos: { x: 0, y: r(16) } },
|
||||
};
|
||||
}
|
||||
const CARD_W = 180;
|
||||
const CARD_H = 250;
|
||||
const CARD_SPACING = 200;
|
||||
@@ -417,7 +456,9 @@ function appendUiSection(ui, section, entities) {
|
||||
function upsertUi() {
|
||||
const ui = JSON.parse(readFileSync(UI_FILE, 'utf8'));
|
||||
const E = ui.ContentProto.Entities;
|
||||
ui.ContentProto.Entities = E.filter((e) => !isGeneratedUiEntity(e));
|
||||
// CardHand는 스톡 섹션이라 과거 생성된 단색판(NamePlate/CostPlate)이 잔존 → 프레임 이미지 도입으로 제거
|
||||
const obsoletePlate = /^\/ui\/DefaultGroup\/CardHand\/Card\d+\/(NamePlate|CostPlate)$/;
|
||||
ui.ContentProto.Entities = E.filter((e) => !isGeneratedUiEntity(e) && !obsoletePlate.test(e.path));
|
||||
|
||||
const byPath = new Map(ui.ContentProto.Entities.map((e) => [e.path, e]));
|
||||
const uiSections = new Map();
|
||||
@@ -444,7 +485,7 @@ function upsertUi() {
|
||||
const previewIds = Object.keys(CARDS.cards);
|
||||
const cards = Array.from({ length: 5 }, (_, i) => {
|
||||
const c = CARDS.cards[previewIds[i % previewIds.length]];
|
||||
return { name: c.name, cost: String(c.cost), desc: c.desc, tint: c.kind === 'Attack' ? ATTACK : DEFEND };
|
||||
return { name: c.name, cost: String(c.cost), desc: c.desc, frame: frameRuid(c) };
|
||||
});
|
||||
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
@@ -456,9 +497,9 @@ function upsertUi() {
|
||||
tr.anchoredPosition = { x: CARD_XS[i - 1], y: 0 };
|
||||
tr.OffsetMin = { x: CARD_XS[i - 1] - CARD_W / 2, y: -CARD_H / 2 };
|
||||
tr.OffsetMax = { x: CARD_XS[i - 1] + CARD_W / 2, y: CARD_H / 2 };
|
||||
sp.ImageRUID = { DataId: '' };
|
||||
sp.Type = 1;
|
||||
sp.Color = cards[i - 1].tint;
|
||||
sp.ImageRUID = { DataId: cards[i - 1].frame };
|
||||
sp.Type = 0;
|
||||
sp.Color = WHITE;
|
||||
sp.RaycastTarget = true;
|
||||
const comps = card.jsonString['@components'];
|
||||
if (!comps.some((c) => c['@type'] === 'MOD.Core.ButtonComponent')) {
|
||||
@@ -476,11 +517,9 @@ function upsertUi() {
|
||||
card.jsonString.enable = true;
|
||||
card.jsonString.visible = true;
|
||||
|
||||
const children = [
|
||||
['Cost', { size: { x: 44, y: 44 }, pos: { x: -68, y: 103 }, value: cards[i - 1].cost, fontSize: 26, bold: true }],
|
||||
['Name', { size: { x: 168, y: 30 }, pos: { x: 0, y: -8 }, value: cards[i - 1].name, fontSize: 20, bold: true }],
|
||||
['Desc', { size: { x: 164, y: 70 }, pos: { x: 0, y: -62 }, value: cards[i - 1].desc, fontSize: 18, bold: false }],
|
||||
];
|
||||
const handLayout = cardFaceLayout(CARD_W);
|
||||
const previewValues = { Cost: cards[i - 1].cost, Name: cards[i - 1].name, Desc: cards[i - 1].desc };
|
||||
const children = handLayout.texts.map(([suffix, cfg]) => [suffix, { ...cfg, value: previewValues[suffix] }]);
|
||||
for (const [suffix, cfg] of children) {
|
||||
const path = `/ui/DefaultGroup/CardHand/Card${i}/${suffix}`;
|
||||
const dOrder = suffix === 'Cost' ? 7 : suffix === 'Name' ? 6 : 8;
|
||||
@@ -496,7 +535,7 @@ function upsertUi() {
|
||||
components: [
|
||||
transform({ parentW: CARD_W, parentH: CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: cfg.size, pos: cfg.pos }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: cfg.value, fontSize: cfg.fontSize, bold: cfg.bold }),
|
||||
text({ value: cfg.value, fontSize: cfg.fontSize, bold: cfg.bold, color: cfg.color }),
|
||||
],
|
||||
});
|
||||
ui.ContentProto.Entities.push(child);
|
||||
@@ -517,18 +556,19 @@ function upsertUi() {
|
||||
child.jsonString['@components'][2].Text = cfg.value;
|
||||
child.jsonString['@components'][2].FontSize = cfg.fontSize;
|
||||
child.jsonString['@components'][2].MaxSize = cfg.fontSize;
|
||||
child.jsonString['@components'][2].FontColor = cfg.color;
|
||||
}
|
||||
}
|
||||
|
||||
// 프레임 이미지가 이름판·코스트판을 내장하므로 Art만 유지 (잔존 NamePlate/CostPlate는 upsertUi 초입에서 제거)
|
||||
const frameKids = [
|
||||
['NamePlate', 3, { size: { x: 168, y: 34 }, pos: { x: 0, y: -8 } }, { r: 0.07, g: 0.08, b: 0.1, a: 0.92 }, 1],
|
||||
['CostPlate', 4, { size: { x: 44, y: 44 }, pos: { x: -68, y: 103 } }, { r: 0.07, g: 0.08, b: 0.1, a: 0.95 }, 1],
|
||||
['Art', 5, { size: { x: 96, y: 96 }, pos: { x: 0, y: 52 } }, { r: 1, g: 1, b: 1, a: 1 }, 0],
|
||||
['Art', 5, handLayout.art, WHITE, 0],
|
||||
];
|
||||
for (const [suffix, dOrder, cfg, color, spriteType] of frameKids) {
|
||||
const fPath = `/ui/DefaultGroup/CardHand/Card${i}/${suffix}`;
|
||||
if (!byPath.get(fPath)) {
|
||||
const fe = entity({
|
||||
let fe = byPath.get(fPath);
|
||||
if (!fe) {
|
||||
fe = entity({
|
||||
id: guid('dck', 200 + i * 10 + dOrder),
|
||||
path: fPath,
|
||||
modelId: 'uisprite',
|
||||
@@ -542,6 +582,14 @@ function upsertUi() {
|
||||
});
|
||||
ui.ContentProto.Entities.push(fe);
|
||||
byPath.set(fPath, fe);
|
||||
} else {
|
||||
const ftr = fe.jsonString['@components'].find((c) => c['@type'] === 'MOD.Core.UITransformComponent');
|
||||
if (ftr) {
|
||||
ftr.RectSize = cfg.size;
|
||||
ftr.anchoredPosition = cfg.pos;
|
||||
ftr.OffsetMin = { x: cfg.pos.x - cfg.size.x / 2, y: cfg.pos.y - cfg.size.y / 2 };
|
||||
ftr.OffsetMax = { x: cfg.pos.x + cfg.size.x / 2, y: cfg.pos.y + cfg.size.y / 2 };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -758,16 +806,13 @@ function upsertUi() {
|
||||
displayOrder: i,
|
||||
components: [
|
||||
transform({ parentW: 950, parentH: 610, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: INSPECT_CARD_W, y: INSPECT_CARD_H }, pos: { x: 0, y: 0 } }),
|
||||
sprite({ color: ATTACK, type: 1 }),
|
||||
sprite({ dataId: CARDFRAMES.frames.warrior.normal, color: WHITE, type: 0 }),
|
||||
],
|
||||
});
|
||||
card.jsonString.enable = false;
|
||||
inspect.push(card);
|
||||
for (const [suffix, cfg] of [
|
||||
['Cost', { size: { x: 38, y: 38 }, pos: { x: -58, y: 86 }, value: '1', fontSize: 22, bold: true }],
|
||||
['Name', { size: { x: 148, y: 26 }, pos: { x: 0, y: -8 }, value: '', fontSize: 17, bold: true }],
|
||||
['Desc', { size: { x: 144, y: 60 }, pos: { x: 0, y: -54 }, value: '', fontSize: 15, bold: false }],
|
||||
]) {
|
||||
const inspectLayout = cardFaceLayout(INSPECT_CARD_W);
|
||||
for (const [suffix, cfg] of inspectLayout.texts.map(([sfx, c]) => [sfx, { ...c, value: sfx === 'Cost' ? '1' : '' }])) {
|
||||
const dOrder = suffix === 'Cost' ? 7 : suffix === 'Name' ? 6 : 8;
|
||||
inspect.push(entity({
|
||||
id: guid('ins', insN++),
|
||||
@@ -779,28 +824,22 @@ function upsertUi() {
|
||||
components: [
|
||||
transform({ parentW: INSPECT_CARD_W, parentH: INSPECT_CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: cfg.size, pos: cfg.pos }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: cfg.value, fontSize: cfg.fontSize, bold: cfg.bold }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
for (const [suffix, dOrder, cfg, color, spriteType] of [
|
||||
['NamePlate', 3, { size: { x: 148, y: 30 }, pos: { x: 0, y: -8 } }, { r: 0.07, g: 0.08, b: 0.1, a: 0.92 }, 1],
|
||||
['CostPlate', 4, { size: { x: 38, y: 38 }, pos: { x: -58, y: 86 } }, { r: 0.07, g: 0.08, b: 0.1, a: 0.95 }, 1],
|
||||
['Art', 5, { size: { x: 84, y: 84 }, pos: { x: 0, y: 44 } }, { r: 1, g: 1, b: 1, a: 1 }, 0],
|
||||
]) {
|
||||
inspect.push(entity({
|
||||
id: guid('ins', insN++),
|
||||
path: `${cardPath}/${suffix}`,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: dOrder,
|
||||
components: [
|
||||
transform({ parentW: INSPECT_CARD_W, parentH: INSPECT_CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: cfg.size, pos: cfg.pos }),
|
||||
sprite({ color, type: spriteType, raycast: false }),
|
||||
text({ value: cfg.value, fontSize: cfg.fontSize, bold: cfg.bold, color: cfg.color }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
inspect.push(entity({
|
||||
id: guid('ins', insN++),
|
||||
path: `${cardPath}/Art`,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: 5,
|
||||
components: [
|
||||
transform({ parentW: INSPECT_CARD_W, parentH: INSPECT_CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: inspectLayout.art.size, pos: inspectLayout.art.pos }),
|
||||
sprite({ color: WHITE, type: 0, raycast: false }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
emit('DeckInspectHud', inspect);
|
||||
|
||||
@@ -899,16 +938,13 @@ function upsertUi() {
|
||||
displayOrder: i,
|
||||
components: [
|
||||
transform({ parentW: 980, parentH: 620, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: ALL_DECK_CARD_W, y: ALL_DECK_CARD_H }, pos: { x: 0, y: 0 } }),
|
||||
sprite({ color: ATTACK, type: 1 }),
|
||||
sprite({ dataId: CARDFRAMES.frames.warrior.normal, color: WHITE, type: 0 }),
|
||||
],
|
||||
});
|
||||
card.jsonString.enable = false;
|
||||
allDeck.push(card);
|
||||
for (const [suffix, cfg] of [
|
||||
['Cost', { size: { x: 38, y: 38 }, pos: { x: -58, y: 86 }, value: '1', fontSize: 22, bold: true }],
|
||||
['Name', { size: { x: 148, y: 26 }, pos: { x: 0, y: -8 }, value: '', fontSize: 17, bold: true }],
|
||||
['Desc', { size: { x: 144, y: 60 }, pos: { x: 0, y: -54 }, value: '', fontSize: 15, bold: false }],
|
||||
]) {
|
||||
const allDeckLayout = cardFaceLayout(ALL_DECK_CARD_W);
|
||||
for (const [suffix, cfg] of allDeckLayout.texts.map(([sfx, c]) => [sfx, { ...c, value: sfx === 'Cost' ? '1' : '' }])) {
|
||||
const dOrder = suffix === 'Cost' ? 7 : suffix === 'Name' ? 6 : 8;
|
||||
allDeck.push(entity({
|
||||
id: guid('all', allN++),
|
||||
@@ -920,28 +956,22 @@ function upsertUi() {
|
||||
components: [
|
||||
transform({ parentW: ALL_DECK_CARD_W, parentH: ALL_DECK_CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: cfg.size, pos: cfg.pos }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: cfg.value, fontSize: cfg.fontSize, bold: cfg.bold }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
for (const [suffix, dOrder, cfg, color, spriteType] of [
|
||||
['NamePlate', 3, { size: { x: 148, y: 30 }, pos: { x: 0, y: -8 } }, { r: 0.07, g: 0.08, b: 0.1, a: 0.92 }, 1],
|
||||
['CostPlate', 4, { size: { x: 38, y: 38 }, pos: { x: -58, y: 86 } }, { r: 0.07, g: 0.08, b: 0.1, a: 0.95 }, 1],
|
||||
['Art', 5, { size: { x: 84, y: 84 }, pos: { x: 0, y: 44 } }, { r: 1, g: 1, b: 1, a: 1 }, 0],
|
||||
]) {
|
||||
allDeck.push(entity({
|
||||
id: guid('all', allN++),
|
||||
path: `${cardPath}/${suffix}`,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: dOrder,
|
||||
components: [
|
||||
transform({ parentW: ALL_DECK_CARD_W, parentH: ALL_DECK_CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: cfg.size, pos: cfg.pos }),
|
||||
sprite({ color, type: spriteType, raycast: false }),
|
||||
text({ value: cfg.value, fontSize: cfg.fontSize, bold: cfg.bold, color: cfg.color }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
allDeck.push(entity({
|
||||
id: guid('all', allN++),
|
||||
path: `${cardPath}/Art`,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: 5,
|
||||
components: [
|
||||
transform({ parentW: ALL_DECK_CARD_W, parentH: ALL_DECK_CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: allDeckLayout.art.size, pos: allDeckLayout.art.pos }),
|
||||
sprite({ color: WHITE, type: 0, raycast: false }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
emit('DeckAllHud', allDeck);
|
||||
|
||||
@@ -1415,15 +1445,12 @@ function upsertUi() {
|
||||
displayOrder: i,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: CARD_W, y: CARD_H }, pos: { x: rewardXs[i - 1], y: 0 } }),
|
||||
sprite({ color: ATTACK, type: 1, raycast: true }),
|
||||
sprite({ dataId: CARDFRAMES.frames.warrior.normal, color: WHITE, type: 0, raycast: true }),
|
||||
button(),
|
||||
],
|
||||
}));
|
||||
for (const [suffix, cfg] of [
|
||||
['Cost', { size: { x: 44, y: 44 }, pos: { x: -68, y: 103 }, value: '1', fontSize: 26, bold: true }],
|
||||
['Name', { size: { x: 168, y: 30 }, pos: { x: 0, y: -8 }, value: '카드', fontSize: 20, bold: true }],
|
||||
['Desc', { size: { x: 164, y: 70 }, pos: { x: 0, y: -62 }, value: '', fontSize: 18, bold: false }],
|
||||
]) {
|
||||
const rewardLayout = cardFaceLayout(CARD_W);
|
||||
for (const [suffix, cfg] of rewardLayout.texts.map(([sfx, c]) => [sfx, { ...c, value: sfx === 'Cost' ? '1' : sfx === 'Name' ? '카드' : '' }])) {
|
||||
const dOrder = suffix === 'Cost' ? 7 : suffix === 'Name' ? 6 : 8;
|
||||
reward.push(entity({
|
||||
id: guid('rwd', rwdN++),
|
||||
@@ -1435,28 +1462,22 @@ function upsertUi() {
|
||||
components: [
|
||||
transform({ parentW: CARD_W, parentH: CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: cfg.size, pos: cfg.pos }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: cfg.value, fontSize: cfg.fontSize, bold: cfg.bold }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
for (const [suffix, dOrder, cfg, color, spriteType] of [
|
||||
['NamePlate', 3, { size: { x: 168, y: 34 }, pos: { x: 0, y: -8 } }, { r: 0.07, g: 0.08, b: 0.1, a: 0.92 }, 1],
|
||||
['CostPlate', 4, { size: { x: 44, y: 44 }, pos: { x: -68, y: 103 } }, { r: 0.07, g: 0.08, b: 0.1, a: 0.95 }, 1],
|
||||
['Art', 5, { size: { x: 96, y: 96 }, pos: { x: 0, y: 52 } }, { r: 1, g: 1, b: 1, a: 1 }, 0],
|
||||
]) {
|
||||
reward.push(entity({
|
||||
id: guid('rwd', rwdN++),
|
||||
path: `${cardPath}/${suffix}`,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: dOrder,
|
||||
components: [
|
||||
transform({ parentW: CARD_W, parentH: CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: cfg.size, pos: cfg.pos }),
|
||||
sprite({ color, type: spriteType, raycast: false }),
|
||||
text({ value: cfg.value, fontSize: cfg.fontSize, bold: cfg.bold, color: cfg.color }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
reward.push(entity({
|
||||
id: guid('rwd', rwdN++),
|
||||
path: `${cardPath}/Art`,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: 5,
|
||||
components: [
|
||||
transform({ parentW: CARD_W, parentH: CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: rewardLayout.art.size, pos: rewardLayout.art.pos }),
|
||||
sprite({ color: WHITE, type: 0, raycast: false }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
reward.push(entity({
|
||||
id: guid('rwd', rwdN++),
|
||||
@@ -1631,16 +1652,14 @@ function upsertUi() {
|
||||
displayOrder: i,
|
||||
components: [
|
||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: CARD_W, y: CARD_H }, pos: { x: shopXs[i - 1], y: 20 } }),
|
||||
sprite({ color: ATTACK, type: 1, raycast: true }),
|
||||
sprite({ dataId: CARDFRAMES.frames.warrior.normal, color: WHITE, type: 0, raycast: true }),
|
||||
button(),
|
||||
],
|
||||
}));
|
||||
for (const [suffix, cfg] of [
|
||||
['Cost', { size: { x: 44, y: 44 }, pos: { x: -68, y: 103 }, value: '1', fontSize: 26, bold: true, color: { r: 1, g: 1, b: 1, a: 1 } }],
|
||||
['Name', { size: { x: 168, y: 30 }, pos: { x: 0, y: -8 }, value: '카드', fontSize: 20, bold: true, color: { r: 1, g: 1, b: 1, a: 1 } }],
|
||||
['Desc', { size: { x: 164, y: 56 }, pos: { x: 0, y: -58 }, value: '', fontSize: 18, bold: false, color: { r: 1, g: 1, b: 1, a: 1 } }],
|
||||
['Price', { size: { x: 160, y: 40 }, pos: { x: 0, y: -105 }, value: '30 메소', fontSize: 22, bold: true, color: { r: 0.98, g: 0.85, b: 0.4, a: 1 } }],
|
||||
]) {
|
||||
const shopLayout = cardFaceLayout(CARD_W);
|
||||
const shopTexts = shopLayout.texts.map(([sfx, c]) => [sfx, { ...c, value: sfx === 'Cost' ? '1' : sfx === 'Name' ? '카드' : '' }]);
|
||||
shopTexts.push(['Price', { size: { x: 160, y: 40 }, pos: { x: 0, y: -135 }, value: '30 메소', fontSize: 22, bold: true, color: { r: 0.98, g: 0.85, b: 0.4, a: 1 } }]);
|
||||
for (const [suffix, cfg] of shopTexts) {
|
||||
const dOrder = suffix === 'Cost' ? 7 : suffix === 'Name' ? 6 : suffix === 'Desc' ? 8 : 9;
|
||||
shop.push(entity({
|
||||
id: guid('shp', shpN++),
|
||||
@@ -1656,24 +1675,18 @@ function upsertUi() {
|
||||
],
|
||||
}));
|
||||
}
|
||||
for (const [suffix, dOrder, cfg, color, spriteType] of [
|
||||
['NamePlate', 3, { size: { x: 168, y: 34 }, pos: { x: 0, y: -8 } }, { r: 0.07, g: 0.08, b: 0.1, a: 0.92 }, 1],
|
||||
['CostPlate', 4, { size: { x: 44, y: 44 }, pos: { x: -68, y: 103 } }, { r: 0.07, g: 0.08, b: 0.1, a: 0.95 }, 1],
|
||||
['Art', 5, { size: { x: 96, y: 96 }, pos: { x: 0, y: 52 } }, { r: 1, g: 1, b: 1, a: 1 }, 0],
|
||||
]) {
|
||||
shop.push(entity({
|
||||
id: guid('shp', shpN++),
|
||||
path: `${cardPath}/${suffix}`,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: dOrder,
|
||||
components: [
|
||||
transform({ parentW: CARD_W, parentH: CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: cfg.size, pos: cfg.pos }),
|
||||
sprite({ color, type: spriteType, raycast: false }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
shop.push(entity({
|
||||
id: guid('shp', shpN++),
|
||||
path: `${cardPath}/Art`,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: 5,
|
||||
components: [
|
||||
transform({ parentW: CARD_W, parentH: CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: shopLayout.art.size, pos: shopLayout.art.pos }),
|
||||
sprite({ color: WHITE, type: 0, raycast: false }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
shop.push(entity({
|
||||
id: guid('shp', shpN++),
|
||||
@@ -2418,6 +2431,8 @@ function writeCodeblocks() {
|
||||
prop('string', 'DeckInspectKind', '""'),
|
||||
prop('boolean', 'DeckAllOpen', 'false'),
|
||||
prop('any', 'Cards'),
|
||||
prop('any', 'CardFrames'),
|
||||
prop('any', 'ClassToFrame'),
|
||||
prop('number', 'PlayerHp', '0'),
|
||||
prop('number', 'PlayerMaxHp', '80'),
|
||||
prop('number', 'PlayerBlock', '0'),
|
||||
@@ -2667,6 +2682,7 @@ self.CurrentNodeId = ""
|
||||
self.CurrentEnemyId = ""
|
||||
self.PlayerJob = ""
|
||||
${luaJobsTable(JOBS)}
|
||||
${luaFramesTable()}
|
||||
self:GenerateMap()
|
||||
self:BindButtons()
|
||||
self:AddRelic("${RELICS.startingRelic}")
|
||||
@@ -3148,16 +3164,18 @@ end
|
||||
self:RenderPiles()`, [{ Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'animate' }]),
|
||||
method('ApplyCardFace', `local c = self.Cards[cardId]
|
||||
if c == nil then
|
||||
c = { name = cardId, cost = 0, desc = "", kind = "Skill" }
|
||||
c = { name = cardId, cost = 0, desc = "", kind = "Skill", class = "warrior", rarity = "normal" }
|
||||
end
|
||||
local e = _EntityService:GetEntityByPath(base)
|
||||
if e ~= nil and e.SpriteGUIRendererComponent ~= nil then
|
||||
if c.kind == "Attack" then
|
||||
e.SpriteGUIRendererComponent.Color = Color(0.86, 0.42, 0.38, 1)
|
||||
elseif c.kind == "Power" then
|
||||
e.SpriteGUIRendererComponent.Color = Color(0.46, 0.68, 0.52, 1)
|
||||
else
|
||||
e.SpriteGUIRendererComponent.Color = Color(0.42, 0.55, 0.85, 1)
|
||||
local frames = self.CardFrames[self.ClassToFrame[c.class] or "warrior"]
|
||||
local ruid = nil
|
||||
if frames ~= nil then
|
||||
ruid = frames[c.rarity or "normal"]
|
||||
end
|
||||
if ruid ~= nil then
|
||||
e.SpriteGUIRendererComponent.ImageRUID = ruid
|
||||
e.SpriteGUIRendererComponent.Color = Color(1, 1, 1, 1)
|
||||
end
|
||||
end
|
||||
self:SetText(base .. "/Cost", string.format("%d", c.cost))
|
||||
|
||||
Reference in New Issue
Block a user