Убран мёртвый код проигрывания видео; заготовка своей модели ножа

PlayBlackPortalVideoLegacy и BlackPortalVideoPath ниоткуда не вызывались
с 2026-09-09, когда концовку заменили слайдами. Разборы Pause/PlayVideo и
синтаксиса @modfolder перенесены в NoteFlashbackPatch - единственное место,
где эти API ещё вызываются. Перекрёстные ссылки в FinalSlides поправлены,
устаревшее упоминание StayVideoPath/ReturnVideoPath убрано.

В items.xml у necroWpnBladeNecroKnife добавлена закомментированная строка
Meshfile под будущий бандл Resources/necroknife - включать вместе с самим
бандлом, не раньше. Там же снято старое сомнение про TintColor: ваниль сама
тинтит тот же boneShivPrefab, значит механизм на оружии работает.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y3pwyLpTrwz41qBjyjzNSA
This commit is contained in:
AlexCube
2026-09-10 02:07:31 +03:00
co-authored by Claude Opus 5
parent 6e1f42f053
commit 026e006c9c
6 changed files with 77 additions and 93 deletions
+29 -1
View File
@@ -893,9 +893,37 @@
whether melee weapons with ShowQuality="true" (inherited from the base knife) whether melee weapons with ShowQuality="true" (inherited from the base knife)
render TintColor the same way. If it's still not black after this deploy, that's render TintColor the same way. If it's still not black after this deploy, that's
the next thing to dig into (possibly needs ShowQuality or a cosmetic-slot the next thing to dig into (possibly needs ShowQuality or a cosmetic-slot
workaround), not a guess to make blind right now. --> workaround), not a guess to make blind right now.
СОМНЕНИЕ СНЯТО 2026-09-10 по самой ванили: meleeWpnBladeT0BoneKnife задаёт себе
TintColor 107, 107, 71 (Data/Config/items.xml:2419) на том же самом
boneShivPrefab, то есть слот тинта у этого меша рабочий и на оружии с
ShowQuality тоже применяется. Копать тут больше нечего; осталось только
посмотреть глазами, достаточно ли чёрный получается клинок. -->
<property name="TintColor" value="0, 0, 0"/> <property name="TintColor" value="0, 0, 0"/>
<!-- СВОЯ МОДЕЛЬ, ЗАГОТОВКА 2026-09-10 - ВКЛЮЧАТЬ ТОЛЬКО ВМЕСТЕ С БАНДЛОМ.
Строка ниже намеренно закомментирована: файла Resources/necroknife в моде ещё
нет, а Meshfile, указывающий в никуда, сломает нож прямо в живой папке мода -
игра читает именно её, и вечерний тест v1.0 идёт с этих же файлов.
Раскомментировать в тот момент, когда бандл лёг в Resources/, и не раньше.
Синтаксис: "#" - грузить из бандла, "@modfolder(NecromancerTome):" - путь от
корня этого мода, "?" отделяет путь к префабу ВНУТРИ бандла. Форма собрана из
двух подтверждённых кусков: ваниль пишет "#Entities/Trees?SnakeweedPrefab.prefab"
(Data/Config/blocks.xml), а поддержка "@modfolder:" лежит в Assembly-CSharp.
Живым примером форма не проверена - первый тестовый бандл с кубом её подтвердит
или опровергнет, до того как рисовать текстуру.
necroknife - имя AssetBundle, заданное префабу в Unity (инспектор, нижняя строка
AssetBundle); necroKnifePrefab.prefab - имя префаба ВНУТРИ бандла. Оба должны
совпасть с тем, что реально собралось, иначе предмет останется без модели молча.
Сборка - Mods/_NecromancerTome_private/tools/UnityEditorScripts/BuildNecroBundles.cs.
Геометрия остаётся ванильной, меняются материал и текстура, поэтому HoldType,
посадка в руке и анимации от базового ножа продолжают подходить без правок. -->
<!-- <property name="Meshfile" value="#@modfolder(NecromancerTome):Resources/necroknife?necroKnifePrefab.prefab"/> -->
<!-- MOD SLOTS ADDED 2026-09-07 (user request: "добавь в нож слоты для <!-- MOD SLOTS ADDED 2026-09-07 (user request: "добавь в нож слоты для
модификаций... модификации там будут особые, именно для ножа модификаций... модификации там будут особые, именно для ножа
некроманта, а не для обычного ножа"). Tags has to be restated in full here некроманта, а не для обычного ножа"). Tags has to be restated in full here
+6 -8
View File
@@ -44,7 +44,7 @@ namespace NecromancerTome
/// ПАУЗА. Begin ставит GameManager.Instance.Pause(true) один раз на всю сцену и больше её не /// ПАУЗА. Begin ставит GameManager.Instance.Pause(true) один раз на всю сцену и больше её не
/// трогает: снимать паузу незачем, потому что любой выход отсюда ведёт в главное меню, а /// трогает: снимать паузу незачем, потому что любой выход отсюда ведёт в главное меню, а
/// GameManager.Disconnect() зовёт Pause(false) внутри себя (см. комментарий в /// GameManager.Disconnect() зовёт Pause(false) внутри себя (см. комментарий в
/// PortalStonePatch.ActivateBlackPortal). Как и вся остальная UI-часть этого мода, сцена /// FinishEnding). Как и вся остальная UI-часть этого мода, сцена
/// рассчитана на локального игрока - Pause вообще работает только в одиночной игре, это /// рассчитана на локального игрока - Pause вообще работает только в одиночной игре, это
/// ограничение самой ванили, а не мода. /// ограничение самой ванили, а не мода.
/// </summary> /// </summary>
@@ -258,14 +258,12 @@ namespace NecromancerTome
/// "Конец". /// "Конец".
/// ///
/// ВИДЕО ОТСЮДА УБРАНО 2026-09-09 по прямому указанию ("временно, убираем вообще видосы /// ВИДЕО ОТСЮДА УБРАНО 2026-09-09 по прямому указанию ("временно, убираем вообще видосы
/// из финала"). Сам вызов XUiC_VideoPlayer.PlayVideo целиком сохранён в /// из финала"). Файлы Video/FinalStay.webm, FinalReturn.webm и BlackPortal.webm тогда же
/// PortalStonePatch.PlayBlackPortalVideoLegacy - вернуть видео можно, не восстанавливая /// удалены из мода перед публикацией: все три были побайтовой копией ванильного
/// код по кускам.
///
/// Файлы Video/FinalStay.webm, FinalReturn.webm и BlackPortal.webm УДАЛЕНЫ ИЗ МОДА
/// 2026-09-09 перед публикацией: все три были побайтовой копией ванильного
/// TFP_Intro.webm (заглушка для тестов), а раздавать чужой ассет игры в релизе нельзя. /// TFP_Intro.webm (заглушка для тестов), а раздавать чужой ассет игры в релизе нельзя.
/// Настоящее видео класть под тем же именем. /// Мёртвый код проигрывания видео (PortalStonePatch.PlayBlackPortalVideoLegacy и
/// константа с путём) убран 2026-09-10 - живой пример того же вызова, если видео
/// понадобится вернуть, остался в NoteFlashbackPatch.cs.
/// ///
/// Задержки и наезда здесь нет намеренно: смотреть на чёрный экран пять секунд незачем, /// Задержки и наезда здесь нет намеренно: смотреть на чёрный экран пять секунд незачем,
/// текст показывается сразу.</summary> /// текст показывается сразу.</summary>
+23 -6
View File
@@ -7,10 +7,21 @@ namespace NecromancerTome
/// <summary> /// <summary>
/// Duke's note ("Записка от Дюка", item noteDuke01) - user request 2026-08-30: "в момент /// Duke's note ("Записка от Дюка", item noteDuke01) - user request 2026-08-30: "в момент
/// открытия записки, ставить игру на паузу и проигрывать флэшбек" (at the moment the note is /// открытия записки, ставить игру на паузу и проигрывать флэшбек" (at the moment the note is
/// opened, pause the game and play a flashback). Reuses the exact pause+video pipeline /// opened, pause the game and play a flashback). Built on the pause+video pipeline first
/// already built and tested for the Black Portal Stone (see PortalStonePatch.cs's /// written for the Black Portal Stone; since the finale switched to text slides
/// ActivateBlackPortal - GameManager.Instance.Pause/XUiC_VideoPlayer.PlayVideo, both APIs /// (FinalSlides) and its dead video code was removed 2026-09-10, this patch is the only
/// decompiled there already, same reasoning applies unchanged here). /// place in the mod that still calls either API, so both write-ups live here now:
/// - GameManager.Instance.Pause(bool) - decompiled GameManager.updatePauseState: sets
/// Time.timeScale=0 for real, but ONLY takes effect in singleplayer (an SP-only check
/// baked into vanilla itself, not a limitation added by this mod) - a deliberate,
/// documented no-op in multiplayer rather than something silently broken.
/// - XUiC_VideoPlayer.PlayVideo(xui, VideoData, skippable, onFinished) - opens the same
/// fullscreen "VideoPlayer" window vanilla's own TFP intro/menu-background videos use.
/// Decompiled XUiV_Video confirms video playback isn't gated by Time.timeScale, so it
/// keeps playing correctly while paused. skippable=true (Cancel key) so a broken/
/// missing video file can't soft-lock the player - XUiV_Video.OnVideoErrorReceived
/// already auto-closes on a bad file on its own, this is just a second, player-facing
/// way out.
/// ///
/// FINDING THE RIGHT PATCH POINT: noteDuke01 has no custom C# class of its own - it's a /// FINDING THE RIGHT PATCH POINT: noteDuke01 has no custom C# class of its own - it's a
/// plain Class="Eat" item (items.xml) whose entire "reading" experience is a vanilla trick: /// plain Class="Eat" item (items.xml) whose entire "reading" experience is a vanilla trick:
@@ -42,8 +53,9 @@ namespace NecromancerTome
/// flashback -> read text -> confirm", rather than overlapping the video with the text box. /// flashback -> read text -> confirm", rather than overlapping the video with the text box.
/// ///
/// VIDEO FILE: Video/DukeNoteFlashback.mp4 - the user's real flashback clip (delivered /// VIDEO FILE: Video/DukeNoteFlashback.mp4 - the user's real flashback clip (delivered
/// 2026-08-30 as exch/flashbback.mp4), kept as .mp4 rather than renamed to .webm like the /// 2026-08-30 as exch/flashbback.mp4), and the only video the mod still ships. Kept as
/// Black Portal placeholder: Unity's VideoPlayer component (confirmed by decompiling /// .mp4 rather than renamed to .webm like the since-deleted Black Portal placeholder:
/// Unity's VideoPlayer component (confirmed by decompiling
/// XUiV_Video - it wraps a plain UnityEngine.Video.VideoPlayer) natively decodes MP4/H.264 on /// XUiV_Video - it wraps a plain UnityEngine.Video.VideoPlayer) natively decodes MP4/H.264 on
/// Windows via Media Foundation, and re-labeling an actual MP4 container as .webm would just /// Windows via Media Foundation, and re-labeling an actual MP4 container as .webm would just
/// make it fail to decode (VP8/VP9 container expected, not H.264) - not decompiled/proven /// make it fail to decode (VP8/VP9 container expected, not H.264) - not decompiled/proven
@@ -54,6 +66,11 @@ namespace NecromancerTome
[HarmonyPatch(typeof(XUiC_MessageBoxWindowGroup), "ShowOkCancel")] [HarmonyPatch(typeof(XUiC_MessageBoxWindowGroup), "ShowOkCancel")]
public static class Patch_XUiC_MessageBoxWindowGroup_ShowOkCancel_NoteFlashback public static class Patch_XUiC_MessageBoxWindowGroup_ShowOkCancel_NoteFlashback
{ {
/// <summary>"@modfolder(NecromancerTome):..." is the exact mod-relative path syntax
/// XUiV_Video.startVideo resolves via ModManager.TryPatchModPathString (decompiled to
/// confirm - looks for "@modfolder(&lt;mod name&gt;):" and substitutes the mod's real
/// install path; "NecromancerTome" here is this mod's own ModInfo.xml Name, not its
/// DisplayName).</summary>
public const string NoteFlashbackVideoPath = "@modfolder(NecromancerTome):Video/DukeNoteFlashback.mp4"; public const string NoteFlashbackVideoPath = "@modfolder(NecromancerTome):Video/DukeNoteFlashback.mp4";
/// <summary>Guards the re-entrant call this patch makes to the very method it patches /// <summary>Guards the re-entrant call this patch makes to the very method it patches
+19 -78
View File
@@ -159,45 +159,23 @@ namespace NecromancerTome
TeleportToBedroll(player); TeleportToBedroll(player);
} }
/// <summary>Black portal confirmation + fullscreen video, user request 2026-08-30 /// <summary>Black portal confirmation dialog, user request 2026-08-30
/// ("диалоговое окно... вы уверены... Если Да, то игра останавливается и проигрывается /// ("диалоговое окно... вы уверены"). XUiC_MessageBoxWindowGroup.ShowCustom(xui, title,
/// видео"). Real APIs, both decompiled directly: /// text, icon, setupCallback, ...) - the same generic Yes/No popup vanilla itself uses (its
/// - XUiC_MessageBoxWindowGroup.ShowCustom(xui, title, text, icon, setupCallback, ...) - /// own delete-item/disconnect confirmations, etc), decompiled directly. ShowOkCancel/
/// the same generic Yes/No popup vanilla itself uses (its own delete-item/disconnect /// ShowConfirmCancel exist too but hardcode their button caption keys ("xuiOk"/"xuiCancel"/
/// confirmations, etc). ShowOkCancel/ShowConfirmCancel exist too but hardcode their /// "btnConfirm") - ShowCustom's _setupCallback is the only variant that lets the two buttons
/// button caption keys ("xuiOk"/"xuiCancel"/"btnConfirm") - ShowCustom's /// be captioned "xuiYes"/"xuiNo" directly (both are real, already-localized vanilla keys,
/// _setupCallback is the only variant that lets the two buttons be captioned /// confirmed against Data/Config/Localization.csv), matching the user's literal "да/нет"
/// "xuiYes"/"xuiNo" directly (both are real, already-localized vanilla keys, confirmed /// wording. Buttons[0]/[2] (not [1]) is the same slot pairing ShowOkCancel/ShowConfirmCancel
/// against Data/Config/Localization.csv), matching the user's literal "да/нет" /// themselves use internally - Buttons[1] is left unused, same as vanilla's own 2-button
/// wording. Buttons[0]/[2] (not [1]) is the same slot pairing ShowOkCancel/ /// dialogs.
/// ShowConfirmCancel themselves use internally - Buttons[1] is left unused, same as
/// vanilla's own 2-button dialogs.
/// - GameManager.Instance.Pause(bool) - decompiled GameManager.updatePauseState: sets
/// Time.timeScale=0 for real, but ONLY takes effect in singleplayer (an SP-only check
/// baked into vanilla itself, not a limitation added by this mod) - a deliberate,
/// documented no-op in multiplayer rather than something silently broken.
/// - XUiC_VideoPlayer.PlayVideo(xui, VideoData, skippable, onFinished) - opens the same
/// fullscreen "VideoPlayer" window vanilla's own TFP intro/menu-background videos use.
/// Decompiled XUiV_Video confirms video playback isn't gated by Time.timeScale, so it
/// keeps playing correctly while paused. skippable=true (Cancel key) so a broken/
/// missing video file can't soft-lock the player - XUiV_Video.OnVideoErrorReceived
/// already auto-closes on a bad file on its own, this is just a second, player-facing
/// way out.
/// ///
/// VIDEO FILE: Video/BlackPortal.webm NO LONGER SHIPS WITH THE MOD. It used to be a /// Второй половины прежнего сценария - паузы и полноэкранного видео - здесь больше нет:
/// byte-for-byte copy of vanilla's own TFP_Intro.webm (from /// после "Да" управление уходит в FinalSlides (см. ActivateBlackPortal ниже), пауза живёт
/// 7DaysToDie_Data/StreamingAssets/Video/), placed there 2026-08-30 as a test stand-in /// там, а видео из финала убрано 2026-09-09. Разбор GameManager.Instance.Pause и
/// ("Пока файл видео замени заглушкой") - deleted 2026-09-09 before the public release, /// XUiC_VideoPlayer.PlayVideo переехал в NoteFlashbackPatch.cs - единственное место в моде,
/// since redistributing a game asset is not ours to do. This path is dead until a real /// где обе эти ванильные API ещё вызываются.</summary>
/// video is dropped in under the same name; the method below is legacy anyway (the
/// finale plays text epilogues now, see FinalSlides).
/// "@modfolder(NecromancerTome):..." is the exact mod-relative path syntax
/// XUiV_Video.startVideo resolves via ModManager.TryPatchModPathString (decompiled to
/// confirm - looks for "@modfolder(&lt;mod name&gt;):" and substitutes the mod's real
/// install path; "NecromancerTome" here is this mod's own ModInfo.xml Name, not its
/// DisplayName).</summary>
public const string BlackPortalVideoPath = "@modfolder(NecromancerTome):Video/BlackPortal.webm";
public static void ShowBlackPortalConfirmation(EntityPlayerLocal player) public static void ShowBlackPortalConfirmation(EntityPlayerLocal player)
{ {
LocalPlayerUI playerUI = LocalPlayerUI.GetUIForPlayer(player); LocalPlayerUI playerUI = LocalPlayerUI.GetUIForPlayer(player);
@@ -220,55 +198,18 @@ namespace NecromancerTome
} }
/// <summary>ЗАМЕНЕНО 2026-09-09: раньше отсюда сразу стартовало полноэкранное видео /// <summary>ЗАМЕНЕНО 2026-09-09: раньше отсюда сразу стартовало полноэкранное видео
/// (BlackPortalVideoPath), теперь запускается финальная сцена из шести слайдов с текстом /// (Video/BlackPortal.webm), теперь запускается финальная сцена из шести слайдов с текстом
/// - FinalSlides.Begin. Причина в BACKLOG.md ("концовка серией диалоговых окон вместо /// - FinalSlides.Begin. Причина в BACKLOG.md ("концовка серией диалоговых окон вместо
/// видео"): видео не локализуется, а текст слайдов идёт обычной строкой через /// видео"): видео не локализуется, а текст слайдов идёт обычной строкой через
/// Localization.csv. Пауза и выход в главное меню никуда не делись - и то и другое /// Localization.csv. Пауза и выход в главное меню никуда не делись - и то и другое
/// теперь живёт внутри FinalSlides, а видео осталось финальным аккордом ПОСЛЕ выбора /// теперь живёт внутри FinalSlides. Видео из концовки убрано целиком 2026-09-09 -
/// концовки на последнем слайде. /// ни здесь, ни в FinalSlides его больше нет.</summary>
///
/// Всё, что описано в комментарии к BlackPortalVideoPath выше, по-прежнему верно и
/// применяется - просто к двум новым файлам (FinalSlides.StayVideoPath /
/// ReturnVideoPath) вместо одного. Сама константа BlackPortalVideoPath больше не
/// используется и оставлена только как документация к разбору "@modfolder(...)" и
/// XUiC_VideoPlayer.PlayVideo, на который FinalSlides ссылается.</summary>
public static void ActivateBlackPortal(EntityPlayerLocal player) public static void ActivateBlackPortal(EntityPlayerLocal player)
{ {
Debug.Log("[NecromancerTome] PortalStonePatch: black portal confirmed by owner=" + player.entityId + ", handing over to FinalSlides"); Debug.Log("[NecromancerTome] PortalStonePatch: black portal confirmed by owner=" + player.entityId + ", handing over to FinalSlides");
FinalSlides.Begin(player); FinalSlides.Begin(player);
} }
/// <summary>Прежняя концовка "сразу видео, потом главное меню". Больше ниоткуда не
/// вызывается (см. ActivateBlackPortal выше) - оставлена целиком, потому что весь разбор
/// Pause/PlayVideo/Disconnect в её комментариях остаётся актуальным и на неё ссылается
/// FinalSlides. Удалять при следующей уборке, если так и не понадобится.</summary>
public static void PlayBlackPortalVideoLegacy(EntityPlayerLocal player)
{
Debug.Log("[NecromancerTome] PortalStonePatch: black portal confirmed by owner=" + player.entityId + ", pausing + playing video");
GameManager.Instance.Pause(true);
LocalPlayerUI playerUI = LocalPlayerUI.GetUIForPlayer(player);
VideoData videoData = new VideoData { url = BlackPortalVideoPath };
XUiC_VideoPlayer.PlayVideo(playerUI.xui, videoData, true, delegate(bool skipped)
{
// EXIT TO MAIN MENU after the video, user request 2026-08-30 ("После видео нужно
// выходить из игры в главное меню") - fires whether the video played to the end
// or was skipped (Cancel key / a bad file), same as any other "the video is over"
// outcome. GameManager.Instance.Disconnect() is not a guess - it's the EXACT same
// call the real in-game ESC menu's own "Exit to Main Menu" button uses
// (decompiled XUiC_InGameMenuWindow.exitGame/BtnExit_OnPressed to confirm: it's a
// thin wrapper straight to this method). Handles everything a clean exit needs by
// itself - closes modal windows, un-pauses (calls Pause(false) internally, so no
// separate unpause call needed here), saves/shuts down the local server, and
// returns to XUiC_MainMenu - not reinventing any of that by hand. Replaces the
// earlier "thrownStonePortalBlackNotBound" tooltip placeholder entirely: with a
// real exit-to-menu ending, staying in-game and showing a tooltip no longer makes
// sense (BACKLOG.md item 6's "destination not decided" placeholder is now this
// exit itself, not a tooltip).
Debug.Log("[NecromancerTome] PortalStonePatch: black portal video finished (skipped=" + skipped + "), exiting to main menu");
GameManager.Instance.Disconnect();
});
}
/// <summary>BedrollPos comes from EntityPlayer.PersistentPlayerData (decompiled - reads /// <summary>BedrollPos comes from EntityPlayer.PersistentPlayerData (decompiled - reads
/// GameManager.Instance.persistentPlayers.GetPlayerDataFromEntityID(entityId)), the same /// GameManager.Instance.persistentPlayers.GetPlayerDataFromEntityID(entityId)), the same
/// field the game's own respawn-at-bedroll flow reads (PersistentPlayerData.BedrollPos / /// field the game's own respawn-at-bedroll flow reads (PersistentPlayerData.BedrollPos /
Binary file not shown.
Binary file not shown.