fix(ui): keep deck popups above monster hp

This commit is contained in:
2026-06-11 01:41:40 +09:00
parent 1a15225ff6
commit 066ad6ddca
4 changed files with 131788 additions and 131683 deletions

View File

@@ -79,6 +79,32 @@ function luaSlotGroup(arr) {
const UI_FILE = 'ui/DefaultGroup.ui';
const COMMON_FILE = 'Global/common.gamelogic';
const UI_ROOT = '/ui/DefaultGroup';
const GENERATED_UI_SECTIONS = [
'DeckHud',
'DeckInspectHud',
'DeckAllHud',
'CombatHud',
'RewardHud',
'MapHud',
'ShopHud',
'RestHud',
'MainMenu',
'CharacterSelectHud',
];
const UI_APPEND_ORDER = [
'DeckHud',
'CombatHud',
'RewardHud',
'MapHud',
'ShopHud',
'RestHud',
'DeckInspectHud',
'DeckAllHud',
'MainMenu',
'CharacterSelectHud',
];
const DISABLED_STOCK_CONTROLS = ['Button_Attack', 'Button_Jump', 'UIJoystick'];
const TRANSPARENT = { r: 0, g: 0, b: 0, a: 0 };
const DARK = { r: 0.08, g: 0.09, b: 0.11, a: 0.92 };
@@ -250,21 +276,28 @@ function scrollLayoutGroup({ cellSize, spacing, columns }) {
};
}
function displayOrderFor(path, displayOrder) {
if (path.startsWith('/ui/DefaultGroup/DeckAllHud')) return 2000 + displayOrder;
if (path.startsWith('/ui/DefaultGroup/DeckInspectHud')) return 1900 + displayOrder;
return displayOrder;
}
function sortingOrderFor(path) {
if (path.startsWith('/ui/DefaultGroup/DeckAllHud')) return 2000;
if (path.startsWith('/ui/DefaultGroup/DeckInspectHud')) return 1900;
function popupLayerFor(path) {
if (path.startsWith('/ui/DefaultGroup/DeckAllHud')) return { root: '/ui/DefaultGroup/DeckAllHud', base: 4000 };
if (path.startsWith('/ui/DefaultGroup/DeckInspectHud')) return { root: '/ui/DefaultGroup/DeckInspectHud', base: 3000 };
return null;
}
function uiOrderFor(path, displayOrder) {
const popup = popupLayerFor(path);
if (popup != null) {
const relative = path.slice(popup.root.length).split('/').filter(Boolean);
return popup.base + relative.length * 100 + displayOrder;
}
return displayOrder;
}
function displayOrderFor(path, displayOrder) {
return uiOrderFor(path, displayOrder);
}
function applySortingOverride(path, components, displayOrder) {
const baseOrder = sortingOrderFor(path);
if (baseOrder == null) return components;
if (popupLayerFor(path) == null) return components;
const order = uiOrderFor(path, displayOrder);
return components.map((component) => {
if (component['@type'] !== 'MOD.Core.SpriteGUIRendererComponent' && component['@type'] !== 'MOD.Core.TextComponent') {
return component;
@@ -273,7 +306,7 @@ function applySortingOverride(path, components, displayOrder) {
...component,
OverrideSorting: true,
SortingLayer: 'UI',
OrderInLayer: baseOrder + displayOrder,
OrderInLayer: order,
};
});
}
@@ -310,6 +343,31 @@ function entity({ id, path, modelId, entryId, componentNames, components, displa
};
}
function uiPath(...parts) {
return [UI_ROOT, ...parts].join('/');
}
function sectionRoot(section) {
return uiPath(section);
}
function isGeneratedUiEntity(e) {
return GENERATED_UI_SECTIONS.some((section) => e.path.startsWith(sectionRoot(section)));
}
function appendUiSection(ui, section, entities) {
if (!GENERATED_UI_SECTIONS.includes(section)) {
throw new Error(`[gen-slaydeck] unknown generated UI section: ${section}`);
}
const root = sectionRoot(section);
for (const e of entities) {
if (!e.path.startsWith(root)) {
throw new Error(`[gen-slaydeck] ${section} section emitted unexpected path: ${e.path}`);
}
}
ui.ContentProto.Entities.push(...entities);
}
function upsertUi() {
for (const g of ['combat', 'elite', 'boss']) {
if (!Array.isArray(SLOTS[g]) || SLOTS[g].length < MAX_MONSTERS) {
@@ -318,10 +376,18 @@ 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/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') && !e.path.startsWith('/ui/DefaultGroup/CharacterSelectHud'));
ui.ContentProto.Entities = E.filter((e) => !isGeneratedUiEntity(e));
const byPath = new Map(ui.ContentProto.Entities.map((e) => [e.path, e]));
for (const path of ['/ui/DefaultGroup/Button_Attack', '/ui/DefaultGroup/Button_Jump', '/ui/DefaultGroup/UIJoystick']) {
const uiSections = new Map();
const emit = (section, entities) => {
if (uiSections.has(section)) {
throw new Error(`[gen-slaydeck] duplicate generated UI section: ${section}`);
}
uiSections.set(section, entities);
};
for (const path of DISABLED_STOCK_CONTROLS.map((name) => uiPath(name))) {
const e = byPath.get(path);
if (e != null) {
e.jsonString.enable = false;
@@ -503,7 +569,7 @@ function upsertUi() {
],
}));
ui.ContentProto.Entities.push(...hud);
emit('DeckHud', hud);
const inspect = [];
const inspectHud = entity({
@@ -625,7 +691,7 @@ function upsertUi() {
}));
}
}
ui.ContentProto.Entities.push(...inspect);
emit('DeckInspectHud', inspect);
const allDeck = [];
const allHud = entity({
@@ -747,7 +813,7 @@ function upsertUi() {
}));
}
}
ui.ContentProto.Entities.push(...allDeck);
emit('DeckAllHud', allDeck);
const PANEL_BG = { r: 0.08, g: 0.09, b: 0.11, a: 0.78 };
const combat = [];
@@ -907,7 +973,7 @@ function upsertUi() {
});
result.jsonString.enable = false;
combat.push(result);
ui.ContentProto.Entities.push(...combat);
emit('CombatHud', combat);
const reward = [];
const rewardHud = entity({
@@ -988,7 +1054,7 @@ function upsertUi() {
text({ value: '건너뛰기', fontSize: 26, bold: true, color: GOLD, alignment: 4 }),
],
}));
ui.ContentProto.Entities.push(...reward);
emit('RewardHud', reward);
const TYPE_KO = { combat: '전투', elite: '엘리트', boss: '보스', shop: '상점', rest: '휴식' };
const map = [];
@@ -1050,7 +1116,7 @@ function upsertUi() {
],
}));
}
ui.ContentProto.Entities.push(...map);
emit('MapHud', map);
const shop = [];
const shopHud = entity({
@@ -1184,7 +1250,7 @@ function upsertUi() {
text({ value: '나가기', fontSize: 26, bold: true, color: GOLD, alignment: 4 }),
],
}));
ui.ContentProto.Entities.push(...shop);
emit('ShopHud', shop);
const rest = [];
const restHud = entity({
@@ -1241,7 +1307,7 @@ function upsertUi() {
text({ value: '나가기', fontSize: 26, bold: true, color: GOLD, alignment: 4 }),
],
}));
ui.ContentProto.Entities.push(...rest);
emit('RestHud', rest);
const menu = [];
menu.push(entity({
@@ -1474,7 +1540,16 @@ function upsertUi() {
],
}));
select[0].jsonString.enable = false;
ui.ContentProto.Entities.push(...menu, ...select);
emit('MainMenu', menu);
emit('CharacterSelectHud', select);
for (const section of UI_APPEND_ORDER) {
const entities = uiSections.get(section);
if (entities == null) {
throw new Error(`[gen-slaydeck] missing generated UI section: ${section}`);
}
appendUiSection(ui, section, entities);
}
JSON.parse(JSON.stringify(ui));
writeFileSync(UI_FILE, JSON.stringify(ui, null, 2), 'utf8');
@@ -1920,12 +1995,6 @@ if self.DeckAllOpen == true then
allHud.Enable = false
end
end
for i = 1, 4 do
\tlocal monsterSlot = _EntityService:GetEntityByPath("/ui/DefaultGroup/CombatHud/MonsterSlot" .. tostring(i))
\tif monsterSlot ~= nil then
\t\tmonsterSlot.Enable = false
\tend
end
local pile = {}
local title = ""
if kind == "discard" then
@@ -1944,12 +2013,6 @@ end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], N
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckInspectHud")
if hud ~= nil then
hud.Enable = false
end
for i = 1, 4 do
\tlocal monsterSlot = _EntityService:GetEntityByPath("/ui/DefaultGroup/CombatHud/MonsterSlot" .. tostring(i))
\tif monsterSlot ~= nil then
\t\tmonsterSlot.Enable = true
\tend
end`),
method('RenderDeckInspect', `local count = 0
if pile ~= nil then
@@ -2009,12 +2072,6 @@ if inspectHud ~= nil then
end
self.DeckInspectKind = ""
self.DeckAllOpen = true
for i = 1, 4 do
\tlocal monsterSlot = _EntityService:GetEntityByPath("/ui/DefaultGroup/CombatHud/MonsterSlot" .. tostring(i))
\tif monsterSlot ~= nil then
\t\tmonsterSlot.Enable = false
\tend
end
self:RenderAllDeck()
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckAllHud")
if hud ~= nil then
@@ -2024,12 +2081,6 @@ end`),
local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckAllHud")
if hud ~= nil then
hud.Enable = false
end
for i = 1, 4 do
\tlocal monsterSlot = _EntityService:GetEntityByPath("/ui/DefaultGroup/CombatHud/MonsterSlot" .. tostring(i))
\tif monsterSlot ~= nil then
\t\tmonsterSlot.Enable = true
\tend
end`),
method('RenderAllDeck', `local pile = self.RunDeck or {}
local count = #pile