Add deck pile inspector UI
This commit is contained in:
@@ -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 : 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 : 0xfe;
|
||||
const v = (ns * 0x100000 + n) >>> 0;
|
||||
return `${v.toString(16).padStart(8, '0')}-0000-4000-8000-${v.toString(16).padStart(12, '0')}`;
|
||||
}
|
||||
@@ -221,6 +221,38 @@ function text({ value, fontSize, bold = false, color = { r: 1, g: 1, b: 1, a: 1
|
||||
};
|
||||
}
|
||||
|
||||
function scrollLayoutGroup({ cellSize, spacing, columns }) {
|
||||
return {
|
||||
'@type': 'MOD.Core.ScrollLayoutGroupComponent',
|
||||
CellSize: cellSize,
|
||||
ChildAlignment: 0,
|
||||
Constraint: 1,
|
||||
ConstraintCount: columns,
|
||||
GridChildAlignment: 0,
|
||||
GridSpacing: spacing,
|
||||
HorizontalScrollBarDirection: 0,
|
||||
IgnoreMapLayerCheck: false,
|
||||
OrderInLayer: 0,
|
||||
OverrideSorting: false,
|
||||
Padding: { left: 16, right: 16, top: 16, bottom: 16 },
|
||||
ReverseArrangement: false,
|
||||
ScrollBarBackgroundColor: { r: 1, g: 1, b: 1, a: 0.18 },
|
||||
ScrollBarBgImageRUID: { DataId: '' },
|
||||
ScrollBarHandleColor: { r: 0.94, g: 0.74, b: 0.26, a: 0.9 },
|
||||
ScrollBarHandleImageRUID: { DataId: '' },
|
||||
ScrollBarThickness: 12,
|
||||
ScrollBarVisible: 1,
|
||||
SortingLayer: 'UI',
|
||||
Spacing: 0,
|
||||
StartAxis: 0,
|
||||
StartCorner: 0,
|
||||
Type: 2,
|
||||
UseScroll: true,
|
||||
VerticalScrollBarDirection: 1,
|
||||
Enable: true,
|
||||
};
|
||||
}
|
||||
|
||||
function entity({ id, path, modelId, entryId, componentNames, components, displayOrder }) {
|
||||
const parts = path.split('/');
|
||||
const name = parts[parts.length - 1];
|
||||
@@ -255,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/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/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]));
|
||||
|
||||
@@ -349,11 +381,12 @@ function upsertUi() {
|
||||
path: `/ui/DefaultGroup/DeckHud/${pile.key}`,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent',
|
||||
displayOrder: pile.key === 'DrawPile' ? 0 : 1,
|
||||
components: [
|
||||
transform({ parentW: 1280, parentH: 330, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 132, y: 186 }, pos: { x: pile.x, y: 8 }, align: ALIGN_CENTER }),
|
||||
sprite({ color: pile.color, type: 1, raycast: true }),
|
||||
button(),
|
||||
],
|
||||
}));
|
||||
add(entity({
|
||||
@@ -415,6 +448,128 @@ function upsertUi() {
|
||||
|
||||
ui.ContentProto.Entities.push(...hud);
|
||||
|
||||
const inspect = [];
|
||||
const inspectHud = entity({
|
||||
id: guid('ins', 0),
|
||||
path: '/ui/DefaultGroup/DeckInspectHud',
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
displayOrder: 15,
|
||||
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 }),
|
||||
],
|
||||
});
|
||||
inspectHud.jsonString.enable = false;
|
||||
inspect.push(inspectHud);
|
||||
inspect.push(entity({
|
||||
id: guid('ins', 1),
|
||||
path: '/ui/DefaultGroup/DeckInspectHud/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: 1040, y: 760 }, pos: { x: 0, y: 10 }, align: ALIGN_CENTER }),
|
||||
sprite({ color: { r: 0.08, g: 0.09, b: 0.11, a: 0.96 }, type: 1 }),
|
||||
],
|
||||
}));
|
||||
inspect.push(entity({
|
||||
id: guid('ins', 2),
|
||||
path: '/ui/DefaultGroup/DeckInspectHud/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: 720, y: 54 }, pos: { x: 0, y: 350 } }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: '\uB371 \uBCF4\uAE30', fontSize: 34, bold: true, color: GOLD, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
inspect.push(entity({
|
||||
id: guid('ins', 3),
|
||||
path: '/ui/DefaultGroup/DeckInspectHud/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: 466, y: 350 } }),
|
||||
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 }),
|
||||
],
|
||||
}));
|
||||
inspect.push(entity({
|
||||
id: guid('ins', 4),
|
||||
path: '/ui/DefaultGroup/DeckInspectHud/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: 30 } }),
|
||||
sprite({ color: TRANSPARENT }),
|
||||
text({ value: '\uCE74\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4', fontSize: 28, bold: true, color: { r: 0.82, g: 0.86, b: 0.9, a: 1 }, alignment: 4 }),
|
||||
],
|
||||
}));
|
||||
inspect.push(entity({
|
||||
id: guid('ins', 5),
|
||||
path: '/ui/DefaultGroup/DeckInspectHud/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: 950, y: 610 }, 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 insN = 6;
|
||||
const INSPECT_CARD_COUNT = 60;
|
||||
const INSPECT_CARD_W = 142;
|
||||
const INSPECT_CARD_H = 198;
|
||||
for (let i = 1; i <= INSPECT_CARD_COUNT; i++) {
|
||||
const cardPath = `/ui/DefaultGroup/DeckInspectHud/Grid/Card${i}`;
|
||||
const card = entity({
|
||||
id: guid('ins', insN++),
|
||||
path: cardPath,
|
||||
modelId: 'uisprite',
|
||||
entryId: 'UISprite',
|
||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
||||
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 }),
|
||||
],
|
||||
});
|
||||
card.jsonString.enable = false;
|
||||
inspect.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 }],
|
||||
]) {
|
||||
inspect.push(entity({
|
||||
id: guid('ins', insN++),
|
||||
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: 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 }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
}
|
||||
ui.ContentProto.Entities.push(...inspect);
|
||||
|
||||
const PANEL_BG = { r: 0.08, g: 0.09, b: 0.11, a: 0.78 };
|
||||
const combat = [];
|
||||
combat.push(entity({
|
||||
@@ -1015,6 +1170,10 @@ function writeCodeblocks() {
|
||||
prop('number', 'TweenEventId', '0'),
|
||||
prop('any', 'EndTurnHandler'),
|
||||
prop('any', 'NewGameHandler'),
|
||||
prop('any', 'DrawPileHandler'),
|
||||
prop('any', 'DiscardPileHandler'),
|
||||
prop('any', 'DeckInspectCloseHandler'),
|
||||
prop('string', 'DeckInspectKind', '""'),
|
||||
prop('any', 'Cards'),
|
||||
prop('number', 'PlayerHp', '0'),
|
||||
prop('number', 'PlayerMaxHp', '80'),
|
||||
@@ -1126,6 +1285,30 @@ if endTurn ~= nil and endTurn.ButtonComponent ~= nil then
|
||||
end
|
||||
self.EndTurnHandler = endTurn:ConnectEvent(ButtonClickEvent, function() self:EndPlayerTurn() end)
|
||||
end
|
||||
local drawPile = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckHud/DrawPile")
|
||||
if drawPile ~= nil and drawPile.ButtonComponent ~= nil then
|
||||
if self.DrawPileHandler ~= nil then
|
||||
drawPile:DisconnectEvent(ButtonClickEvent, self.DrawPileHandler)
|
||||
self.DrawPileHandler = nil
|
||||
end
|
||||
self.DrawPileHandler = drawPile:ConnectEvent(ButtonClickEvent, function() self:OpenDeckInspect("draw") end)
|
||||
end
|
||||
local discardPile = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckHud/DiscardPile")
|
||||
if discardPile ~= nil and discardPile.ButtonComponent ~= nil then
|
||||
if self.DiscardPileHandler ~= nil then
|
||||
discardPile:DisconnectEvent(ButtonClickEvent, self.DiscardPileHandler)
|
||||
self.DiscardPileHandler = nil
|
||||
end
|
||||
self.DiscardPileHandler = discardPile:ConnectEvent(ButtonClickEvent, function() self:OpenDeckInspect("discard") end)
|
||||
end
|
||||
local inspectClose = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckInspectHud/Close")
|
||||
if inspectClose ~= nil and inspectClose.ButtonComponent ~= nil then
|
||||
if self.DeckInspectCloseHandler ~= nil then
|
||||
inspectClose:DisconnectEvent(ButtonClickEvent, self.DeckInspectCloseHandler)
|
||||
self.DeckInspectCloseHandler = nil
|
||||
end
|
||||
self.DeckInspectCloseHandler = inspectClose:ConnectEvent(ButtonClickEvent, function() self:CloseDeckInspect() 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
|
||||
@@ -1212,7 +1395,83 @@ self.DiscardPile = {}
|
||||
self:Shuffle(self.DrawPile)`),
|
||||
method('RenderPiles', `self:SetText("/ui/DefaultGroup/DeckHud/DrawPile/Count", tostring(#self.DrawPile))
|
||||
self:SetText("/ui/DefaultGroup/DeckHud/DiscardPile/Count", tostring(#self.DiscardPile))
|
||||
self:SetText("/ui/DefaultGroup/DeckHud/Energy", "에너지 " .. string.format("%d", self.Energy) .. "/" .. string.format("%d", self.MaxEnergy))`),
|
||||
self:SetText("/ui/DefaultGroup/DeckHud/Energy", "에너지 " .. string.format("%d", self.Energy) .. "/" .. string.format("%d", self.MaxEnergy))
|
||||
local inspect = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckInspectHud")
|
||||
if inspect ~= nil and inspect.Enable == true and self.DeckInspectKind ~= "" then
|
||||
self:OpenDeckInspect(self.DeckInspectKind)
|
||||
end`),
|
||||
method('OpenDeckInspect', `self.DeckInspectKind = kind
|
||||
local pile = {}
|
||||
local title = ""
|
||||
if kind == "discard" then
|
||||
pile = self.DiscardPile or {}
|
||||
title = "버린 덱"
|
||||
else
|
||||
pile = self.DrawPile or {}
|
||||
title = "뽑을 덱"
|
||||
end
|
||||
self:RenderDeckInspect(pile, title)
|
||||
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckInspectHud")
|
||||
if hud ~= nil then
|
||||
hud.Enable = true
|
||||
end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'kind' }]),
|
||||
method('CloseDeckInspect', `self.DeckInspectKind = ""
|
||||
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckInspectHud")
|
||||
if hud ~= nil then
|
||||
hud.Enable = false
|
||||
end`),
|
||||
method('RenderDeckInspect', `local count = 0
|
||||
if pile ~= nil then
|
||||
count = #pile
|
||||
end
|
||||
local suffix = " (" .. tostring(count) .. ")"
|
||||
if count > 60 then
|
||||
suffix = suffix .. " - 60장까지 표시"
|
||||
end
|
||||
self:SetText("/ui/DefaultGroup/DeckInspectHud/Title", title .. suffix)
|
||||
local empty = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckInspectHud/Empty")
|
||||
if empty ~= nil then
|
||||
empty.Enable = count <= 0
|
||||
end
|
||||
for i = 1, 60 do
|
||||
local e = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckInspectHud/Grid/Card" .. tostring(i))
|
||||
if e ~= nil then
|
||||
local cardId = nil
|
||||
if pile ~= nil then
|
||||
cardId = pile[i]
|
||||
end
|
||||
if cardId == nil then
|
||||
e.Enable = false
|
||||
else
|
||||
e.Enable = true
|
||||
self:ApplyInspectCardVisual(i, cardId)
|
||||
end
|
||||
end
|
||||
end`, [
|
||||
{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'pile' },
|
||||
{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'title' },
|
||||
]),
|
||||
method('ApplyInspectCardVisual', `local c = self.Cards[cardId]
|
||||
if c == nil then
|
||||
c = { name = cardId, cost = 0, desc = "", kind = "Skill" }
|
||||
end
|
||||
local base = "/ui/DefaultGroup/DeckInspectHud/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)
|
||||
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('RenderHand', `local drawStart = Vector2(-590, 8)
|
||||
for i = 1, 5 do
|
||||
\tlocal cardEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(i))
|
||||
|
||||
Reference in New Issue
Block a user