using System.Collections.Generic; using System.IO; using UnityEngine; namespace NecromancerTome { /// /// Сумка одержимого зомби (указание 13.10: «всем одержимым зомби с видом от первого лица сумку и возможность вызывать /// её по Tab»). Решения пользователя 13.10: /// - своя, одна на всех зомби, число ячеек - по виду зомби (SlotsFor), пока у всех 2; /// - при выходе из одержимости зомби сбрасывает рюкзаком всё, что было в сумке (и на поясе - когда в поясе /// появятся предметы); /// - гибель одержимого - мешок на месте и метка на карте, как у сумки волка. /// Лут (указание 13.10: «раз у них E свободно, подключай лутание») - как у волка: E на ящик или мешок - обыск с /// переносом в сумку, E на предмет на земле - подобрать (WolfBag.Target - сумка зомби, пока она есть). /// Только зомби из списка вида от первого лица (FpArms.Allows), не питомцы. Окно - то же, что у сумки волка /// (XUiC_BagStorageWindowGroup, без рюкзака персонажа), Tab в зомби открывает его вместо рюкзака. /// /// ХРАНЕНИЕ. Сумка живёт одно вселение, но пишется в отдельный файл сейва при каждом изменении (как сумка волка, /// сейв игрока не трогается): вылет посреди вселения вещи не теряет - при следующем вселении то, что осталось в /// файле, выпадает мешком у тела. /// public static class ZombieBag { const string FileName = "NecromancerTome_zombiebag.bin"; public const int DefaultSlots = 2; /// Метка выпавшей сумки - ключ локализации (снимается вместе с меткой сумки волка, когда мешок исчез). public const string WaypointName = "necroZombieBagWaypoint"; public static bool WindowOpen; static Bag _bag; static string FilePath => Path.Combine(GameIO.GetSaveGameDir(), FileName); public static bool Active => _bag != null; public static Bag Current => _bag; /// Записать после лута (WolfBag.SaveTarget). public static void SaveNow() => Save(); /// У кого сумка: зомби с видом от первого лица, не питомцы. public static bool Has(EntityAlive _z, bool _pet) => !_pet && FpArms.Allows(_z); /// Ячеек у этого вида зомби (указание: «каким-то добавим, каким-то наоборот») - пока у всех DefaultSlots. public static int SlotsFor(EntityAlive _z) => DefaultSlots; /// Вселение началось: то, что осталось в файле после вылета, - мешком у тела; новая пустая сумка. public static void Begin(EntityPlayerLocal _body, EntityAlive _z) { try { if (File.Exists(FilePath)) { Bag old = SpatialVaultPersistence.ParsePayload(File.ReadAllBytes(FilePath)); List left = Items(old); if (left.Count > 0 && _body != null) { WolfBag.DropSack(_body.position + _body.GetForwardVector() * 0.8f, left); Debug.Log("[NecromancerTome] ZombieBag: в файле осталась сумка прошлого вселения - мешком у тела (" + left.Count + ")"); } File.Delete(FilePath); } } catch (System.Exception e) { Debug.LogWarning("[NecromancerTome] ZombieBag: файл сумки не прочитан (" + e.Message + ") - оставлен как есть"); } _bag = new Bag(new Vector2i(SlotsFor(_z), 1), XUiC_ItemStack.StackLocationTypes.LootContainer, null); Debug.Log("[NecromancerTome] ZombieBag: сумка " + _z.EntityName + " - ячеек " + _bag.SlotCount); } /// Вселение кончилось: всё из сумки - мешком на месте зомби; погиб - ещё и метка на карте. public static void End(EntityAlive _z) { if (_bag == null) { return; } CloseWindow(); List items = Items(_bag); _bag = null; Save(); if (items.Count == 0 || _z == null) { return; } bool died = _z.IsDead(); WolfBag.DropSack(_z.position, items); if (died) { WolfBag.AddWaypoint(_z.position, WaypointName); } Debug.Log("[NecromancerTome] ZombieBag: " + (died ? "одержимый погиб" : "выход из одержимости") + " - сумка мешком (" + items.Count + ")"); } static List Items(Bag _b) { List items = new List(); if (_b?.ItemGrid?.items == null) { return items; } foreach (ItemStack s in _b.ItemGrid.items) { if (s != null && !s.IsEmpty()) { items.Add(s.Clone()); } } return items; } static void Save() { try { if (_bag == null || _bag.IsEmpty()) { if (File.Exists(FilePath)) { File.Delete(FilePath); } return; } File.WriteAllBytes(FilePath, SpatialVaultPersistence.BuildPayload(_bag)); } catch (System.Exception e) { Debug.LogWarning("[NecromancerTome] ZombieBag: сумка не записана - " + e.Message); } } /// Tab в зомби - окно сумки, как «багажник» волка (WolfBag.OpenWindow). public static void OpenWindow(EntityPlayerLocal _player, EntityAlive _z) { LocalPlayerUI ui = LocalPlayerUI.GetUIForPlayer(_player); if (ui == null || _bag == null) { return; } WindowOpen = true; XUiC_BagStorageWindowGroup.Open(ui.xui, _z, _bag, LootContainer.GetLootContainer("roboticDrone"), Localization.Get("necroZombieBagWindowTitle"), Save, () => { WindowOpen = false; Save(); }); Debug.Log("[NecromancerTome] ZombieBag: окно сумки"); } public static void CloseWindow() { if (WindowOpen) { LocalPlayerUI.GetUIForPrimaryPlayer()?.windowManager.CloseAllOpenModalWindows(); WindowOpen = false; } } /// Выход из мира - забыть (файл остаётся: при следующем вселении выпадет мешком у тела). public static void Forget() { _bag = null; WindowOpen = false; } } }