Add all deck popup

This commit is contained in:
2026-06-10 00:38:34 +09:00
parent de6e12c765
commit 1583f7ec26
3 changed files with 86235 additions and 3 deletions

View File

@@ -103,7 +103,7 @@ const ALIGN_BOTTOM_CENTER = 6;
function guid(prefix, n) {
// 유효한 8-4-4-4-12 hex GUID 생성. prefix는 충돌 방지용 네임스페이스 바이트로 매핑.
const ns = prefix === 'hud' ? 0xd0 : prefix === 'dck' ? 0xca : prefix === 'cmb' ? 0xcb : prefix === 'rwd' ? 0xcc : prefix === 'map' ? 0xcd : prefix === 'shp' ? 0xce : prefix === 'rst' ? 0xcf : prefix === 'menu' ? 0xe0 : prefix === 'ins' ? 0xe1 : 0xfe;
const ns = prefix === 'hud' ? 0xd0 : prefix === 'dck' ? 0xca : prefix === 'cmb' ? 0xcb : prefix === 'rwd' ? 0xcc : prefix === 'map' ? 0xcd : prefix === 'shp' ? 0xce : prefix === 'rst' ? 0xcf : prefix === 'menu' ? 0xe0 : prefix === 'ins' ? 0xe1 : prefix === 'all' ? 0xe2 : 0xfe;
const v = (ns * 0x100000 + n) >>> 0;
return `${v.toString(16).padStart(8, '0')}-0000-4000-8000-${v.toString(16).padStart(12, '0')}`;
}
@@ -287,7 +287,7 @@ function entity({ id, path, modelId, entryId, componentNames, components, displa
function upsertUi() {
const ui = JSON.parse(readFileSync(UI_FILE, 'utf8'));
const E = ui.ContentProto.Entities;
ui.ContentProto.Entities = E.filter((e) => !e.path.startsWith('/ui/DefaultGroup/DeckHud') && !e.path.startsWith('/ui/DefaultGroup/DeckInspectHud') && !e.path.startsWith('/ui/DefaultGroup/CombatHud') && !e.path.startsWith('/ui/DefaultGroup/RewardHud') && !e.path.startsWith('/ui/DefaultGroup/MapHud') && !e.path.startsWith('/ui/DefaultGroup/ShopHud') && !e.path.startsWith('/ui/DefaultGroup/RestHud') && !e.path.startsWith('/ui/DefaultGroup/MainMenu'));
ui.ContentProto.Entities = E.filter((e) => !e.path.startsWith('/ui/DefaultGroup/DeckHud') && !e.path.startsWith('/ui/DefaultGroup/DeckInspectHud') && !e.path.startsWith('/ui/DefaultGroup/DeckAllHud') && !e.path.startsWith('/ui/DefaultGroup/CombatHud') && !e.path.startsWith('/ui/DefaultGroup/RewardHud') && !e.path.startsWith('/ui/DefaultGroup/MapHud') && !e.path.startsWith('/ui/DefaultGroup/ShopHud') && !e.path.startsWith('/ui/DefaultGroup/RestHud') && !e.path.startsWith('/ui/DefaultGroup/MainMenu'));
const byPath = new Map(ui.ContentProto.Entities.map((e) => [e.path, e]));
@@ -446,6 +446,21 @@ function upsertUi() {
],
}));
add(entity({
id: guid('hud', hud.length),
path: '/ui/DefaultGroup/DeckHud/AllDeckButton',
modelId: 'uibutton',
entryId: 'UIButton',
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent',
displayOrder: 4,
components: [
transform({ parentW: 1280, parentH: 330, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 188, y: 58 }, pos: { x: 470, y: 135 }, align: ALIGN_CENTER }),
sprite({ color: DARK, type: 1, raycast: true }),
button(),
text({ value: '모든덱보기', fontSize: 23, bold: true, color: GOLD, alignment: 0 }),
],
}));
ui.ContentProto.Entities.push(...hud);
const inspect = [];
@@ -570,6 +585,128 @@ function upsertUi() {
}
ui.ContentProto.Entities.push(...inspect);
const allDeck = [];
const allHud = entity({
id: guid('all', 0),
path: '/ui/DefaultGroup/DeckAllHud',
modelId: 'uisprite',
entryId: 'UISprite',
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
displayOrder: 16,
components: [
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }),
sprite({ color: { r: 0.04, g: 0.05, b: 0.07, a: 0.78 }, type: 1, raycast: true }),
],
});
allHud.jsonString.enable = false;
allDeck.push(allHud);
allDeck.push(entity({
id: guid('all', 1),
path: '/ui/DefaultGroup/DeckAllHud/Panel',
modelId: 'uisprite',
entryId: 'UISprite',
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
displayOrder: 0,
components: [
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1080, y: 800 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }),
sprite({ color: { r: 0.08, g: 0.09, b: 0.11, a: 0.96 }, type: 1 }),
],
}));
allDeck.push(entity({
id: guid('all', 2),
path: '/ui/DefaultGroup/DeckAllHud/Title',
modelId: 'uitext',
entryId: 'UIText',
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
displayOrder: 1,
components: [
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 760, y: 54 }, pos: { x: 0, y: 380 } }),
sprite({ color: TRANSPARENT }),
text({ value: '모든 덱', fontSize: 34, bold: true, color: GOLD, alignment: 4 }),
],
}));
allDeck.push(entity({
id: guid('all', 3),
path: '/ui/DefaultGroup/DeckAllHud/Close',
modelId: 'uibutton',
entryId: 'UIButton',
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent',
displayOrder: 2,
components: [
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 78, y: 52 }, pos: { x: 486, y: 380 } }),
sprite({ color: { r: 0.16, g: 0.18, b: 0.22, a: 1 }, type: 1, raycast: true }),
button(),
text({ value: 'X', fontSize: 26, bold: true, color: GOLD, alignment: 4 }),
],
}));
allDeck.push(entity({
id: guid('all', 4),
path: '/ui/DefaultGroup/DeckAllHud/Empty',
modelId: 'uitext',
entryId: 'UIText',
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
displayOrder: 3,
components: [
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 600, y: 50 }, pos: { x: 0, y: 40 } }),
sprite({ color: TRANSPARENT }),
text({ value: '덱이 없습니다', fontSize: 28, bold: true, color: { r: 0.82, g: 0.86, b: 0.9, a: 1 }, alignment: 4 }),
],
}));
allDeck.push(entity({
id: guid('all', 5),
path: '/ui/DefaultGroup/DeckAllHud/Grid',
modelId: 'uiempty',
entryId: 'UIEmpty',
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ScrollLayoutGroupComponent',
displayOrder: 4,
components: [
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 980, y: 620 }, pos: { x: 0, y: 0 } }),
sprite({ color: TRANSPARENT, type: 1, raycast: true }),
scrollLayoutGroup({ cellSize: { x: 158, y: 214 }, spacing: { x: 22, y: 22 }, columns: 5 }),
],
}));
let allN = 6;
const ALL_DECK_CARD_COUNT = 120;
const ALL_DECK_CARD_W = 142;
const ALL_DECK_CARD_H = 198;
for (let i = 1; i <= ALL_DECK_CARD_COUNT; i++) {
const cardPath = `/ui/DefaultGroup/DeckAllHud/Grid/Card${i}`;
const card = entity({
id: guid('all', allN++),
path: cardPath,
modelId: 'uisprite',
entryId: 'UISprite',
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
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 }),
],
});
card.jsonString.enable = false;
allDeck.push(card);
for (const [suffix, cfg] of [
['Cost', { size: { x: 42, y: 38 }, pos: { x: -46, y: 73 }, value: '1', fontSize: 25, bold: true }],
['Name', { size: { x: 126, y: 42 }, pos: { x: 0, y: 34 }, value: '', fontSize: 21, bold: true }],
['Desc', { size: { x: 126, y: 70 }, pos: { x: 0, y: -58 }, value: '', fontSize: 16, bold: false }],
]) {
allDeck.push(entity({
id: guid('all', allN++),
path: `${cardPath}/${suffix}`,
modelId: 'uitext',
entryId: 'UIText',
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
displayOrder: suffix === 'Cost' ? 0 : suffix === 'Name' ? 1 : 2,
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 }),
],
}));
}
}
ui.ContentProto.Entities.push(...allDeck);
const PANEL_BG = { r: 0.08, g: 0.09, b: 0.11, a: 0.78 };
const combat = [];
combat.push(entity({
@@ -1173,7 +1310,10 @@ function writeCodeblocks() {
prop('any', 'DrawPileHandler'),
prop('any', 'DiscardPileHandler'),
prop('any', 'DeckInspectCloseHandler'),
prop('any', 'AllDeckHandler'),
prop('any', 'AllDeckCloseHandler'),
prop('string', 'DeckInspectKind', '""'),
prop('boolean', 'DeckAllOpen', 'false'),
prop('any', 'Cards'),
prop('number', 'PlayerHp', '0'),
prop('number', 'PlayerMaxHp', '80'),
@@ -1309,6 +1449,22 @@ if inspectClose ~= nil and inspectClose.ButtonComponent ~= nil then
end
self.DeckInspectCloseHandler = inspectClose:ConnectEvent(ButtonClickEvent, function() self:CloseDeckInspect() end)
end
local allDeckButton = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckHud/AllDeckButton")
if allDeckButton ~= nil and allDeckButton.ButtonComponent ~= nil then
if self.AllDeckHandler ~= nil then
allDeckButton:DisconnectEvent(ButtonClickEvent, self.AllDeckHandler)
self.AllDeckHandler = nil
end
self.AllDeckHandler = allDeckButton:ConnectEvent(ButtonClickEvent, function() self:OpenAllDeck() end)
end
local allDeckClose = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckAllHud/Close")
if allDeckClose ~= nil and allDeckClose.ButtonComponent ~= nil then
if self.AllDeckCloseHandler ~= nil then
allDeckClose:DisconnectEvent(ButtonClickEvent, self.AllDeckCloseHandler)
self.AllDeckCloseHandler = nil
end
self.AllDeckCloseHandler = allDeckClose:ConnectEvent(ButtonClickEvent, function() self:CloseAllDeck() end)
end
for i = 1, 5 do
local cardEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(i))
if cardEntity ~= nil and cardEntity.ButtonComponent ~= nil then
@@ -1460,6 +1616,63 @@ self:SetText(base .. "/Cost", tostring(c.cost))
self:SetText(base .. "/Name", c.name)
self:SetText(base .. "/Desc", c.desc)
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 == "Skill" then
e.SpriteGUIRendererComponent.Color = Color(0.42, 0.55, 0.85, 1)
else
e.SpriteGUIRendererComponent.Color = Color(0.46, 0.68, 0.52, 1)
end
end`, [
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' },
{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'cardId' },
]),
method('OpenAllDeck', `local inspectHud = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckInspectHud")
if inspectHud ~= nil then
inspectHud.Enable = false
end
self.DeckInspectKind = ""
self.DeckAllOpen = true
self:RenderAllDeck()
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckAllHud")
if hud ~= nil then
hud.Enable = true
end`),
method('CloseAllDeck', `self.DeckAllOpen = false
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckAllHud")
if hud ~= nil then
hud.Enable = false
end`),
method('RenderAllDeck', `local pile = self.RunDeck or {}
local count = #pile
self:SetText("/ui/DefaultGroup/DeckAllHud/Title", "모든 덱 (" .. tostring(count) .. ")")
local empty = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckAllHud/Empty")
if empty ~= nil then
empty.Enable = count <= 0
end
for i = 1, 120 do
local e = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckAllHud/Grid/Card" .. tostring(i))
if e ~= nil then
local cardId = pile[i]
if cardId == nil then
e.Enable = false
else
e.Enable = true
self:ApplyAllDeckCardVisual(i, cardId)
end
end
end`),
method('ApplyAllDeckCardVisual', `local cards = self.Cards or {}
local c = cards[cardId]
if c == nil then
c = { name = cardId, cost = 0, desc = "", kind = "Skill" }
end
local base = "/ui/DefaultGroup/DeckAllHud/Grid/Card" .. tostring(slot)
self:SetText(base .. "/Cost", tostring(c.cost))
self:SetText(base .. "/Name", c.name)
self:SetText(base .. "/Desc", c.desc)
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)