Files
Alex CubeandClaude Opus 5 e8f064f5ec Книга некроманта 1.0 — первая публичная версия
Мод для 7 Days to Die 3.2: навык «Некромантия», растущий от счётчика убитых
зомби, тёмное оружие с шестью собственными модами, призывная нежить, пирамида
духов и сюжетный финал через Чёрный портал. Локализация на 13 языках.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaNro5hAGTzcQ7rJNN2tCX
2026-09-09 21:13:03 +03:00

120 lines
12 KiB
XML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<windows>
<!-- Black portal video (BACKLOG.md, "чёрный портал" dialog/video feature, 2026-08-30 follow-up
bugfix). Root cause of "пауза встала, видео не вижу": XUiC_VideoPlayer.PlayVideo opens a
window/group named "videoPlayer" - real and fully functional (it's exactly what plays the
TFP studio intro and the main-menu background loop), but that window is declared ONLY in
Data/Config/XUi_Menu/windows.xml (the MAIN MENU's own UI layout) - it does not exist at
all in Data/Config/XUi_InGame/windows.xml (confirmed by grepping both files directly), so
GUIWindowManager.GetWindow/Open logged "Window \"videoPlayer\" unknown!" the moment the
in-game code tried to open it. This appends the EXACT SAME window block vanilla's own menu
already uses (copied verbatim from XUi_Menu/windows.xml, not reinvented) into the in-game
UI instead, so the exact same controller="VideoPlayer" (XUiC_VideoPlayer, already compiled
into the base game, no Harmony needed) and the <video> element (a generic engine-level UI
primitive, not menu-specific) can be found by the in-game windowManager too. Needs a
matching window_group entry - see Config/XUi_InGame/xui.xml.
THIS ONE WINDOW IS SHARED - both the Black Portal video AND Duke's note flashback
(NoteFlashbackPatch.cs) call the same XUiC_VideoPlayer.PlayVideo, which always opens this
same "videoPlayer" window/group by id - one fix here covers both.
"видео полупрозрачное" fix (user report 2026-08-30, after first successful in-game test):
added globalopacitymod="0" to videoBackground/videoTexture/skipPrompt below. Root cause,
decompiled, not guessed: every XUiV_ImageBased-derived view (sprite AND video both are -
XUiV_Sprite/XUiV_TextureBased both extend it) has a globalOpacityModifier defaulting to 1,
meaning by default its final alpha gets multiplied by xui.BackgroundGlobalOpacity (unless
foregroundlayer="true", which uses a separate ForegroundGlobalOpacity instead) - this is a
REAL, already-live game option, decompiled straight from EnumGamePrefs.Options:
OptionsBackgroundGlobalOpacity defaults to 0.95, not 1.0, and is user-adjustable (further
down is common - players lower it to see more of the game world through menu backgrounds).
This window's copy (like vanilla's own menu copy) never set globalopacitymod, so both the
"opaque" black background AND the video picture itself were always getting dimmed by
whatever fraction of full opacity the user's own Background Opacity option is set to -
in the menu this is invisible (nothing behind the menu to bleed through), but in-game the
actual 3D world sits right behind this window and shows through the gap. The vanilla menu
copy has this exact same latent issue, just never exposed because it's never used with a
3D scene behind it - not something to also go "fix" in Data/Config, just worth knowing this
mod's copy needed the explicit opt-out that context actually requires. -->
<append xpath="/windows">
<window name="videoPlayer" controller="VideoPlayer" pos="0,0" depth="900" cursor_area="true">
<sprite name="videoBackground" depth="0" sprite="menu_empty" type="sliced" color="[black]" anchor_left="#cam,0,-10" anchor_right="#cam,1,10" anchor_bottom="#cam,0,-10" anchor_top="#cam,1,10" style="press" gamepad_selectable="false" globalopacitymod="0" />
<video name="videoTexture" depth="1" anchor_left="#cam,0,0" anchor_right="#cam,1,0" anchor_bottom="#cam,0,0" anchor_top="#cam,1,0" globalopacitymod="0"/>
<sprite name="skipPrompt" depth="5" sprite="ui_game_panel_header" color="[black]" anchor_left="#cam,1,-200" anchor_right="#cam,1,0" anchor_bottom="#cam,0,-0" anchor_top="#cam,0,60" globalopacitymod="0">
<label name="lblSkip" depth="6" text_key="Skip" justify="left" pivot="left" font_size="32" width="180" height="100" pos="20,-30" parse_actions="true" actions_default_format="( ### )"/>
</sprite>
</window>
</append>
<!-- ФИНАЛЬНЫЕ СЛАЙДЫ ЧЁРНОГО ПОРТАЛА, 2026-09-09 (BACKLOG.md "концовка серией диалоговых окон
вместо видео"). Шесть полноэкранных картинок, по одному окну на слайд; текст и кнопки
Назад/Дальше/выбор рисует поверх них ванильный messageBox - см. HarmonySrc/FinalSlides.cs,
там же вся логика листания.
ПОЧЕМУ ШЕСТЬ ОТДЕЛЬНЫХ ОКОН, А НЕ ОДНО С ПЕРЕКЛЮЧЕНИЕМ СПРАЙТА. Смена спрайта на лету
требует либо привязки ({binding}), либо своего XUiController - а в разведке (BACKLOG.md)
как раз осталось непроверенным, найдёт ли движок контроллер, объявленный в сборке мода.
Шесть окон с ЖЁСТКО прописанным sprite= обходят вопрос целиком: ни привязок, ни
контроллера, окно вообще без атрибута controller= (так объявлены и ванильные
HUDLeftStatBars/windowFuel, проверено в Data/Config/XUi_InGame/windows.xml). Листание -
это просто Close старого окна + Open нового из C#.
depth="900" - ниже messageBox (depth="1001" в шаблоне <messagebox>,
Data/Config/XUi_Common/templates.xml:143), так что текстовая коробка 1200x400 ложится
поверх картинки, а картинка видна вокруг неё. Открывать эти окна НЕ модально: в
GUIWindowManager.openInternal модальное открытие зовёт CloseAllOpenModalWindows(), то есть
модальный messageBox закрыл бы модальный слайд - а немодальный переживает это спокойно.
globalopacitymod="0" - та же правка, что у videoPlayer выше: без неё альфа умножается на
пользовательскую настройку Background Opacity (по умолчанию 0.95), и сквозь "финальную"
картинку просвечивал бы игровой мир.
ПРО ПРОПОРЦИИ: исходники 1536x1024 (3:2), а окно растягивается на весь экран (16:9). Чтобы
не плющить лица на крупных планах, картинки залиты чёрными полями по бокам до 1824x1024
(16:9) ЗАРАНЕЕ, при копировании в UIAtlases/NecroFinal - у XUiV_Sprite нет атрибута
сохранения пропорций (декомпилировано: он парсит только atlas/sprite/color/fill*/gradient*),
так что letterbox приходится запекать в сам PNG. Второй чёрный спрайт под картинкой -
страховка на неродных соотношениях сторон (ультраширокие мониторы).
Атлас NecroFinal - папка UIAtlases/NecroFinal, механизм тот же, что у ItemIconAtlas.
Шесть картинок 1824x1024 пакуются UIAtlasFromFolder.createUiAtlasFromTextures через
PackTextures(..., 2, 8192): суммарно ~11.2 Мпикс, влезает в один атлас 4096x4096. -->
<append xpath="/windows">
<window name="necroFinalSlide1" pos="0,0" depth="900">
<sprite name="slideLetterbox" depth="0" sprite="menu_empty" type="sliced" color="[black]" anchor_left="#cam,0,-10" anchor_right="#cam,1,10" anchor_bottom="#cam,0,-10" anchor_top="#cam,1,10" globalopacitymod="0"/>
<sprite name="slideArt" depth="1" atlas="NecroFinal" sprite="NecroFinal1" type="simple" anchor_left="#cam,0,0" anchor_right="#cam,1,0" anchor_bottom="#cam,0,0" anchor_top="#cam,1,0" globalopacitymod="0"/>
</window>
<window name="necroFinalSlide2" pos="0,0" depth="900">
<sprite name="slideLetterbox" depth="0" sprite="menu_empty" type="sliced" color="[black]" anchor_left="#cam,0,-10" anchor_right="#cam,1,10" anchor_bottom="#cam,0,-10" anchor_top="#cam,1,10" globalopacitymod="0"/>
<sprite name="slideArt" depth="1" atlas="NecroFinal" sprite="NecroFinal2" type="simple" anchor_left="#cam,0,0" anchor_right="#cam,1,0" anchor_bottom="#cam,0,0" anchor_top="#cam,1,0" globalopacitymod="0"/>
</window>
<window name="necroFinalSlide3" pos="0,0" depth="900">
<sprite name="slideLetterbox" depth="0" sprite="menu_empty" type="sliced" color="[black]" anchor_left="#cam,0,-10" anchor_right="#cam,1,10" anchor_bottom="#cam,0,-10" anchor_top="#cam,1,10" globalopacitymod="0"/>
<sprite name="slideArt" depth="1" atlas="NecroFinal" sprite="NecroFinal3" type="simple" anchor_left="#cam,0,0" anchor_right="#cam,1,0" anchor_bottom="#cam,0,0" anchor_top="#cam,1,0" globalopacitymod="0"/>
</window>
<window name="necroFinalSlide4" pos="0,0" depth="900">
<sprite name="slideLetterbox" depth="0" sprite="menu_empty" type="sliced" color="[black]" anchor_left="#cam,0,-10" anchor_right="#cam,1,10" anchor_bottom="#cam,0,-10" anchor_top="#cam,1,10" globalopacitymod="0"/>
<sprite name="slideArt" depth="1" atlas="NecroFinal" sprite="NecroFinal4" type="simple" anchor_left="#cam,0,0" anchor_right="#cam,1,0" anchor_bottom="#cam,0,0" anchor_top="#cam,1,0" globalopacitymod="0"/>
</window>
<window name="necroFinalSlide5" pos="0,0" depth="900">
<sprite name="slideLetterbox" depth="0" sprite="menu_empty" type="sliced" color="[black]" anchor_left="#cam,0,-10" anchor_right="#cam,1,10" anchor_bottom="#cam,0,-10" anchor_top="#cam,1,10" globalopacitymod="0"/>
<sprite name="slideArt" depth="1" atlas="NecroFinal" sprite="NecroFinal5" type="simple" anchor_left="#cam,0,0" anchor_right="#cam,1,0" anchor_bottom="#cam,0,0" anchor_top="#cam,1,0" globalopacitymod="0"/>
</window>
<window name="necroFinalSlide6" pos="0,0" depth="900">
<sprite name="slideLetterbox" depth="0" sprite="menu_empty" type="sliced" color="[black]" anchor_left="#cam,0,-10" anchor_right="#cam,1,10" anchor_bottom="#cam,0,-10" anchor_top="#cam,1,10" globalopacitymod="0"/>
<sprite name="slideArt" depth="1" atlas="NecroFinal" sprite="NecroFinal6" type="simple" anchor_left="#cam,0,0" anchor_right="#cam,1,0" anchor_bottom="#cam,0,0" anchor_top="#cam,1,0" globalopacitymod="0"/>
</window>
</append>
<!-- ЭПИЛОГ, 2026-09-09. После выбора на шестом слайде картинки заканчиваются и текст идёт по
чёрному - отсюда отдельное окно без спрайта из атласа, только чёрная заливка. Тот же
depth="900", что у слайдов, так что текстовая коробка (depth 1001) снова ложится сверху.
Заливка копирует приём videoPlayer'а выше: menu_empty, растянутый по #cam с запасом в
10px по каждой стороне, плюс globalopacitymod="0" - без него сквозь "чёрный экран"
просвечивал бы игровой мир на величину пользовательской настройки Background Opacity. -->
<append xpath="/windows">
<window name="necroFinalBlack" pos="0,0" depth="900">
<sprite name="blackout" depth="0" sprite="menu_empty" type="sliced" color="[black]" anchor_left="#cam,0,-10" anchor_right="#cam,1,10" anchor_bottom="#cam,0,-10" anchor_top="#cam,1,10" globalopacitymod="0"/>
</window>
</append>
</windows>