Сумка одержимого зомби: 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
@@ -317,6 +317,10 @@ namespace NecromancerTome
|
||||
_loggedCamera = false;
|
||||
FirstPerson = false;
|
||||
BeltSlot = 0;
|
||||
if (ZombieBag.Has(_zombie, _pet))
|
||||
{
|
||||
ZombieBag.Begin(_player0, _zombie); // сумка зомби на это вселение (13.10)
|
||||
}
|
||||
// Пояс одержимому питомцу ни к чему (указание 06.10); открыться заново не даёт
|
||||
// Patch_GUIWindowManager_openInternal_SnakeToolbelt, после выхода игра откроет его сама.
|
||||
if (_pet)
|
||||
@@ -372,6 +376,7 @@ namespace NecromancerTome
|
||||
ShowPlayerBelt(true);
|
||||
RatLoot.CloseIfViewing();
|
||||
WolfBag.CloseWindow();
|
||||
ZombieBag.End(Zombie); // сумка - мешком на месте зомби (13.10), погиб - с меткой
|
||||
RatPhase.Stop(GameManager.Instance?.World, Zombie, "выход из одержимости");
|
||||
DogSense.Clear();
|
||||
WolfControl.Clear();
|
||||
@@ -520,6 +525,7 @@ namespace NecromancerTome
|
||||
DogSense.Clear();
|
||||
WolfControl.Clear();
|
||||
WolfBag.Forget();
|
||||
ZombieBag.Forget();
|
||||
_zoomCam = null;
|
||||
_zoomBaseFov = -1f;
|
||||
_zoom = 0f;
|
||||
@@ -798,6 +804,17 @@ namespace NecromancerTome
|
||||
// ПКМ (держать) - сквозь постройки, без полёта (BACKLOG, «Могильная крыса» п.4).
|
||||
RatPhase.Tick(world, z, rangedHeld);
|
||||
}
|
||||
if (ZombieBag.Active)
|
||||
{
|
||||
// Tab в зомби - окно его сумки, а не рюкзак персонажа (указание 13.10), как у волка.
|
||||
GUIWindowManager zwm = LocalPlayerUI.GetUIForPlayer(_player)?.windowManager;
|
||||
if (zwm != null && zwm.IsWindowOpen("windowpaging") && !ZombieBag.WindowOpen && !RatLoot.Viewing)
|
||||
{
|
||||
zwm.CloseAllOpenModalWindows();
|
||||
zwm.Close("windowpaging");
|
||||
ZombieBag.OpenWindow(_player, z);
|
||||
}
|
||||
}
|
||||
if (IsPet && WolfControl.Is(z))
|
||||
{
|
||||
// Tab в волке - окно его сумки, а не рюкзак персонажа (тест 10.10: «в волке Tab - вижу сумку игрока»).
|
||||
@@ -1565,6 +1582,10 @@ namespace NecromancerTome
|
||||
{
|
||||
text += ", " + Localization.Get("necroPossessHintView");
|
||||
}
|
||||
if (ZombieBag.Has(_z, IsPet))
|
||||
{
|
||||
text += ", " + Localization.Get("necroPossessHintZombieBag");
|
||||
}
|
||||
if (Flying)
|
||||
{
|
||||
text += ", " + Localization.Get("necroPossessHintFly");
|
||||
|
||||
@@ -497,7 +497,7 @@ namespace NecromancerTome
|
||||
/// <summary>Метка на карте и компасе - как игра ставит метку транспорта (XUiC_MapArea.RefreshVehiclePositionWaypoint),
|
||||
/// но обычная, не «последнее место сущности»: номер мешка после перезахода другой. Метка - в списке меток игрока,
|
||||
/// сохраняется игрой вместе с ним; удалить можно и вручную.</summary>
|
||||
static void AddWaypoint(Vector3 _at)
|
||||
public static void AddWaypoint(Vector3 _at, string _name = WaypointName)
|
||||
{
|
||||
EntityPlayerLocal p = GameManager.Instance?.World?.GetPrimaryPlayer();
|
||||
if (p?.Waypoints == null)
|
||||
@@ -508,7 +508,7 @@ namespace NecromancerTome
|
||||
w.pos = World.worldToBlockPos(_at);
|
||||
w.icon = WaypointIcon;
|
||||
w.ownerId = Platform.PlatformManager.InternalLocalUserIdentifier;
|
||||
w.name.Update(WaypointName, Platform.PlatformManager.MultiPlatform.User.PlatformUserId);
|
||||
w.name.Update(_name, Platform.PlatformManager.MultiPlatform.User.PlatformUserId);
|
||||
w.bUsingLocalizationId = true;
|
||||
p.Waypoints.Collection.Add(w);
|
||||
w.navObject = NavObjectManager.Instance.RegisterNavObject("waypoint", w.pos.ToVector3(), w.icon);
|
||||
@@ -530,7 +530,7 @@ namespace NecromancerTome
|
||||
for (int i = list.Count - 1; i >= 0; i--)
|
||||
{
|
||||
Waypoint w = list[i];
|
||||
if (w.name.Text == WaypointName && (w.pos.ToVector3() - _at).sqrMagnitude < 3f * 3f)
|
||||
if ((w.name.Text == WaypointName || w.name.Text == ZombieBag.WaypointName) && (w.pos.ToVector3() - _at).sqrMagnitude < 3f * 3f)
|
||||
{
|
||||
p.Waypoints.Collection.Remove(w);
|
||||
if (w.navObject != null)
|
||||
@@ -542,6 +542,9 @@ namespace NecromancerTome
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Мешок с предметами на земле (и для сумки одержимого зомби, ZombieBag).</summary>
|
||||
public static void DropSack(Vector3 _at, List<ItemStack> _items) => Drop(_at, _items);
|
||||
|
||||
static void Drop(Vector3 _at, List<ItemStack> _items)
|
||||
{
|
||||
GameManager.Instance.DropContentInLootContainerServer(-1, "DroppedLootContainer", _at + Vector3.up * 0.5f, _items.ToArray());
|
||||
@@ -574,7 +577,7 @@ namespace NecromancerTome
|
||||
{
|
||||
public static void Postfix(XUiC_BagStorageWindowGroup __instance)
|
||||
{
|
||||
bool wolf = WolfBag.WindowOpen && Possession.Active;
|
||||
bool wolf = (WolfBag.WindowOpen || ZombieBag.WindowOpen) && Possession.Active;
|
||||
if (wolf)
|
||||
{
|
||||
__instance.xui.playerUI.windowManager.Close("backpack");
|
||||
|
||||
@@ -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