Сумка одержимого зомби: 2 ячейки, Tab; при выходе - мешком, при гибели - мешком с меткой
ZombieBag: своя сумка на одно вселение у зомби с видом от первого лица (число ячеек по виду, пока 2), окно как у сумки волка; пишется в отдельный файл сейва, остаток после вылета выпадает у тела при следующем вселении. Мешок и метка - общие с сумкой волка (WolfBag.DropSack/AddWaypoint). Подсказка Tab - 13 языков. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c98e3f8b74
commit
eb6c5fb4f1
@@ -0,0 +1,160 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NecromancerTome
|
||||
{
|
||||
/// <summary>
|
||||
/// Сумка одержимого зомби (указание 13.10: «всем одержимым зомби с видом от первого лица сумку и возможность вызывать
|
||||
/// её по Tab»). Решения пользователя 13.10:
|
||||
/// - своя, одна на всех зомби, число ячеек - по виду зомби (SlotsFor), пока у всех 2;
|
||||
/// - при выходе из одержимости зомби сбрасывает рюкзаком всё, что было в сумке (и на поясе - когда в поясе
|
||||
/// появятся предметы);
|
||||
/// - гибель одержимого - мешок на месте и метка на карте, как у сумки волка.
|
||||
/// Только зомби из списка вида от первого лица (FpArms.Allows), не питомцы. Окно - то же, что у сумки волка
|
||||
/// (XUiC_BagStorageWindowGroup, без рюкзака персонажа), Tab в зомби открывает его вместо рюкзака.
|
||||
///
|
||||
/// ХРАНЕНИЕ. Сумка живёт одно вселение, но пишется в отдельный файл сейва при каждом изменении (как сумка волка,
|
||||
/// сейв игрока не трогается): вылет посреди вселения вещи не теряет - при следующем вселении то, что осталось в
|
||||
/// файле, выпадает мешком у тела.
|
||||
/// </summary>
|
||||
public static class ZombieBag
|
||||
{
|
||||
const string FileName = "NecromancerTome_zombiebag.bin";
|
||||
public const int DefaultSlots = 2;
|
||||
/// <summary>Метка выпавшей сумки - ключ локализации (снимается вместе с меткой сумки волка, когда мешок исчез).</summary>
|
||||
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;
|
||||
|
||||
/// <summary>У кого сумка: зомби с видом от первого лица, не питомцы.</summary>
|
||||
public static bool Has(EntityAlive _z, bool _pet) => !_pet && FpArms.Allows(_z);
|
||||
|
||||
/// <summary>Ячеек у этого вида зомби (указание: «каким-то добавим, каким-то наоборот») - пока у всех DefaultSlots.</summary>
|
||||
public static int SlotsFor(EntityAlive _z) => DefaultSlots;
|
||||
|
||||
/// <summary>Вселение началось: то, что осталось в файле после вылета, - мешком у тела; новая пустая сумка.</summary>
|
||||
public static void Begin(EntityPlayerLocal _body, EntityAlive _z)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(FilePath))
|
||||
{
|
||||
Bag old = SpatialVaultPersistence.ParsePayload(File.ReadAllBytes(FilePath));
|
||||
List<ItemStack> 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);
|
||||
}
|
||||
|
||||
/// <summary>Вселение кончилось: всё из сумки - мешком на месте зомби; погиб - ещё и метка на карте.</summary>
|
||||
public static void End(EntityAlive _z)
|
||||
{
|
||||
if (_bag == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
CloseWindow();
|
||||
List<ItemStack> 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<ItemStack> Items(Bag _b)
|
||||
{
|
||||
List<ItemStack> items = new List<ItemStack>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Tab в зомби - окно сумки, как «багажник» волка (WolfBag.OpenWindow).</summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Выход из мира - забыть (файл остаётся: при следующем вселении выпадет мешком у тела).</summary>
|
||||
public static void Forget()
|
||||
{
|
||||
_bag = null;
|
||||
WindowOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user