diff --git a/HarmonySrc/BloodTide.cs b/HarmonySrc/BloodTide.cs index ecc23a3..3a03ec4 100644 --- a/HarmonySrc/BloodTide.cs +++ b/HarmonySrc/BloodTide.cs @@ -60,6 +60,7 @@ namespace NecromancerTome _origHoldType = -1; Heart(false); Patch_XUiC_HUDStatBar_Update_BloodTide.Forget(); + Patch_Stat_Tick_BloodTide.Next = null; // не держать стат (а через него игрока) после выхода из мира return; } bool tide = SnakeMode.IsBloodTide(); @@ -293,7 +294,11 @@ namespace NecromancerTome { return; } - if (!_bars.TryGetValue(__instance, out var e)) + if (_bars.TryGetValue(__instance, out var e) && e.bar.UiTransform == null) + { + _bars.Remove(__instance); // окно пересобрано - старая полоса удалена + } + if (!_bars.TryGetValue(__instance, out e)) { if (!BloodTide.Armed || !(__instance.GetChildById("BarContent")?.ViewComponent is XUiV_Sprite s)) { diff --git a/HarmonySrc/NecroTome.cs b/HarmonySrc/NecroTome.cs index 7d50a40..64732f3 100644 --- a/HarmonySrc/NecroTome.cs +++ b/HarmonySrc/NecroTome.cs @@ -281,6 +281,24 @@ namespace NecromancerTome /// Показать картинку или видео справа (или убрать обе). Видео задаётся только здесь, /// а не в вёрстке: XUiV_Video сам запускает ролик при открытии окна, и в оглавлении он играл /// бы невидимым. Пустой uri - XUiV_Video.startVideo сам освобождает свою текстуру. + /// Путь загруженной картинки (null - не загружена). Свой, а не _img.Path - см. ниже. + string _imgPath; + + void ReleaseImage() + { + if (_img == null || _imgPath == null) + { + return; + } + Texture t = _img.Texture; + _img.Path = null; // TextureUris = пусто, Texture = null - без выгрузки (autounload выключен) + if (t != null) + { + UnityEngine.Object.Destroy(t); + } + _imgPath = null; + } + void SetMedia(string _image, string _video, bool _top = false) { bool img = !string.IsNullOrEmpty(_image), vid = !img && !string.IsNullOrEmpty(_video); @@ -289,9 +307,18 @@ namespace NecromancerTome // Без чтения _img.Path: у пустой картинки (в вёрстке texture не задан) его геттер берёт // textureUris[0] из пустого массива - IndexOutOfRange (тест 09.10). Сеттер сам сравнивает // с текущим путём и не перезагружает тот же файл. - if (img) + // Утечка (проверка 10.10): картинка из файла мода - новая текстура (XUiV_Texture.FetchWwwTexture, клон), + // и при смене пути старая сама не удаляется (autounload в вёрстке не включён - в этом режиме игра на + // загрузке стирает путь новой картинки). Каждая смена главы оставляла ~2 МБ. Удаляем сами: при смене + // картинки, у главы без картинки и при закрытии окна (SetMedia(null, null)). + if (_imgPath != _image) + { + ReleaseImage(); + } + if (img && _imgPath == null) { _img.Path = _image; + _imgPath = _image; } _img.IsVisible = img; } diff --git a/HarmonySrc/PyramidWardPatch.cs b/HarmonySrc/PyramidWardPatch.cs index 4e8f2f4..a8b7c80 100644 --- a/HarmonySrc/PyramidWardPatch.cs +++ b/HarmonySrc/PyramidWardPatch.cs @@ -319,6 +319,7 @@ namespace NecromancerTome // светящееся фиолетовое кольцо по земле - LineRenderer, точки опущены на рельеф лучом. // Дочерний объект модели блока - поэтому сдвиги Origin и выгрузка чанка его не теряют. GameObject ring; + static Material _ringMat; public void SpawnZoneRing() { @@ -332,8 +333,13 @@ namespace NecromancerTome const int n = 96; lr.positionCount = n; lr.widthMultiplier = 0.14f; - var mat = new Material(Shader.Find("Sprites/Default")); - lr.material = mat; + // Один материал на все кольца (проверка утечек 10.10): материал - ассет и с объектом кольца не удаляется, + // а кольцо пересоздаётся при каждой перезагрузке чанка (модель блока - новая). Цвет - вершинный (startColor). + if (_ringMat == null) + { + _ringMat = new Material(Shader.Find("Sprites/Default")) { name = "NecroWardRing" }; + } + lr.sharedMaterial = _ringMat; Color col = new Color(0.75f, 0.25f, 1f, 0.85f); lr.startColor = col; lr.endColor = col; lr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; lr.receiveShadows = false; diff --git a/HarmonySrc/SpatialVaultPatch.cs b/HarmonySrc/SpatialVaultPatch.cs index 20fd7bf..b3de406 100644 --- a/HarmonySrc/SpatialVaultPatch.cs +++ b/HarmonySrc/SpatialVaultPatch.cs @@ -68,8 +68,10 @@ namespace NecromancerTome public const float ShoveDistance = 6f; /// Session cache only - the durable copy lives on disk, see - /// SpatialVaultPersistence.cs. Cleared on WorldShuttingDown so a different save loaded - /// afterwards cannot inherit this world's vault through a recycled entityId. + /// SpatialVaultPersistence.cs. NOT cleared on WorldShuttingDown any more (that event fires before + /// the final player save and wiped vaults - see SpatialVaultPersistence.cs); a different save cannot + /// inherit this world's vault because ToPlayer overwrites or removes the entry on load. Leftover entries + /// are at most one per world loaded in a session - not a growing leak (checked 10.10). public static readonly Dictionary PlayerVaults = new Dictionary(); public static bool Prefix(ItemActionData _actionData, bool _bReleased) diff --git a/NecromancerHarmony.dll b/NecromancerHarmony.dll index 9538040..40b13d0 100644 Binary files a/NecromancerHarmony.dll and b/NecromancerHarmony.dll differ diff --git a/NecromancerHarmony.pdb b/NecromancerHarmony.pdb index be5e3a2..e81133e 100644 Binary files a/NecromancerHarmony.pdb and b/NecromancerHarmony.pdb differ