Чертоги разума, хваты пустой руки, одержимость, книга с оглавлением
- Чертоги разума: дух в префабе chertogi под землёй, книга памяти, нож и плакат; кровать префаба - мебель necroPalaceBed (иначе становилась точкой возрождения); охрана выбрасывает и игроков (их нет в EntityAlives). - Хваты пустой руки по R: «отрицание» и «пиф-паф» - поза рук костями поверх аниматора, остальные кости заморожены, ЛКМ - заклинание, прицел краснеет на зомби. - «Отрицание»: зомби в ужасе бежит дальше 40 м и теряет игрока как цель. - «Пиф-паф»: одержимость зомби-гуманоидом - бег, прыжок, удар, дальняя атака, подрыв, крик крикуньи; тело цепенеет, зомби танкуют одержимого; область вокруг тела сжата; полоска здоровья; спавн и прорисовка у одержимого. - Книга с оглавлением (окно necroTome), глава «Некроэнергия». - Прорисовка дверей у змеи и одержимого (CheckLODs от камеры, а не от тела). - Почтовый ящик консоли и команды necrobones/necropose/necroshot для разработки. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c828202c66
commit
66a0dcf403
+106
-9
@@ -876,6 +876,11 @@ namespace NecromancerTome
|
||||
{
|
||||
public static bool Prefix(EntityPlayerLocal __instance)
|
||||
{
|
||||
if (MindControl.Controlling)
|
||||
{
|
||||
MindControl.PlaceCamera(__instance.cameraTransform);
|
||||
return false;
|
||||
}
|
||||
if (!SnakeControl.Controlling)
|
||||
{
|
||||
return true;
|
||||
@@ -923,6 +928,14 @@ namespace NecromancerTome
|
||||
{
|
||||
SnakeControl.PlaceCamera(__instance.transform);
|
||||
}
|
||||
else if (Possession.Active)
|
||||
{
|
||||
Possession.PlaceCamera(__instance.transform);
|
||||
}
|
||||
else if (MindControl.Controlling)
|
||||
{
|
||||
MindControl.PlaceCamera(__instance.transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1111,13 +1124,10 @@ namespace NecromancerTome
|
||||
public static void Prefix(out Vector3 __state)
|
||||
{
|
||||
__state = Vector3.zero;
|
||||
if (!SnakeControl.Controlling)
|
||||
{
|
||||
return;
|
||||
}
|
||||
World world = GameManager.Instance.World;
|
||||
// И у одержимого зомби (03.10: «если одержимый отошёл подальше, новые не спавнятся возле него»).
|
||||
EntityAlive snake = SnakeControl.Controlling ? SnakeMode.Snake(world) : Possession.Active ? Possession.Zombie : null;
|
||||
EntityPlayerLocal player = world?.GetPrimaryPlayer();
|
||||
EntityAlive snake = SnakeMode.Snake(world);
|
||||
if (player == null || snake == null)
|
||||
{
|
||||
return;
|
||||
@@ -1150,12 +1160,18 @@ namespace NecromancerTome
|
||||
{
|
||||
public static bool Prefix(EntityAlive __instance, ref int ___ticksNoPlayerAdjacent)
|
||||
{
|
||||
if (!SnakeControl.Controlling)
|
||||
EntityAlive snake = SnakeControl.Controlling ? SnakeMode.Snake(__instance.world as World) : Possession.Active ? Possession.Zombie : null;
|
||||
if (snake == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
EntityAlive snake = SnakeMode.Snake(__instance.world as World);
|
||||
if (snake == null || snake == __instance || (__instance.position - snake.position).sqrMagnitude > 6400f)
|
||||
// Одержимый сам не исчезает, как бы далеко от тела ни ушёл.
|
||||
if (snake == __instance)
|
||||
{
|
||||
___ticksNoPlayerAdjacent = 0;
|
||||
return !Possession.IsPossessed(__instance);
|
||||
}
|
||||
if ((__instance.position - snake.position).sqrMagnitude > 6400f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -1172,7 +1188,7 @@ namespace NecromancerTome
|
||||
{
|
||||
public static bool Prefix(GUIWindow _w)
|
||||
{
|
||||
return !(SnakeControl.Controlling && _w != null && _w.Id == SnakeControl.ToolbeltWindow);
|
||||
return !((SnakeControl.Controlling || MindControl.Controlling) && _w != null && _w.Id == SnakeControl.ToolbeltWindow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1182,8 +1198,61 @@ namespace NecromancerTome
|
||||
/// змеи. На время вызова переносим позицию игрока к змее.</summary>
|
||||
public static class SnakeAudioSwap
|
||||
{
|
||||
/// <summary>Вложенность подмены: CheckLODs зовётся и изнутри WorldEnvironment.Update, где позиция уже
|
||||
/// подменена. Подменяет и возвращает только внешний вызов, вложенные ничего не трогают.</summary>
|
||||
static int _swapped, _swapFrame;
|
||||
|
||||
public static Vector3 Prefix()
|
||||
{
|
||||
if (_swapped > 0 && _swapFrame != Time.frameCount)
|
||||
{
|
||||
_swapped = 0; // подмена не вернулась (исключение посреди вызова) - не держим её дольше кадра
|
||||
}
|
||||
if (_swapped > 0)
|
||||
{
|
||||
_swapped++;
|
||||
return Vector3.zero;
|
||||
}
|
||||
Vector3 body = PrefixOuter();
|
||||
if (body != Vector3.zero)
|
||||
{
|
||||
_swapped = 1;
|
||||
_swapFrame = Time.frameCount;
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
static Vector3 PrefixOuter()
|
||||
{
|
||||
if (MindControl.Controlling)
|
||||
{
|
||||
// Дух чертогов: звук, мир и компас - от него (MindControl.cs).
|
||||
EntityPlayerLocal p = GameManager.Instance?.World?.GetPrimaryPlayer();
|
||||
if (p == null)
|
||||
{
|
||||
return Vector3.zero;
|
||||
}
|
||||
Vector3 bodyPos = p.position;
|
||||
p.position = MindControl.FeetPos;
|
||||
return bodyPos;
|
||||
}
|
||||
if (Possession.Active && Possession.Zombie != null)
|
||||
{
|
||||
// Одержимость (тест 03.10: «без звуков») - звуки мерятся от одержимого зомби, а не от тела.
|
||||
EntityPlayerLocal pp = GameManager.Instance?.World?.GetPrimaryPlayer();
|
||||
if (pp == null)
|
||||
{
|
||||
return Vector3.zero;
|
||||
}
|
||||
Vector3 bodyAt = pp.position;
|
||||
pp.position = Possession.Zombie.position;
|
||||
// Область тела сжата (Possession.BodyViewDim) - этим системам радиус полный, они рисуют у зомби.
|
||||
if (Possession.FullViewDim > 0 && pp.ChunkObserver != null)
|
||||
{
|
||||
pp.ChunkObserver.viewDim = Possession.FullViewDim;
|
||||
}
|
||||
return bodyAt;
|
||||
}
|
||||
if (!SnakeControl.Controlling)
|
||||
{
|
||||
return Vector3.zero;
|
||||
@@ -1204,16 +1273,44 @@ namespace NecromancerTome
|
||||
{
|
||||
if (_body == Vector3.zero)
|
||||
{
|
||||
if (_swapped > 1)
|
||||
{
|
||||
_swapped--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
_swapped = 0;
|
||||
EntityPlayerLocal player = GameManager.Instance?.World?.GetPrimaryPlayer();
|
||||
if (player != null)
|
||||
{
|
||||
player.position = _body;
|
||||
if (Possession.Active && Possession.FullViewDim > 0 && player.ChunkObserver != null)
|
||||
{
|
||||
player.ChunkObserver.viewDim = Possession.BodyViewDim;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Полная модель блоков (двери, люки, мебель) ближе 41 м, дальше - упрощённая замена:
|
||||
/// ChunkGameObject.CheckLODs меряет расстояние до ИГРОКА - тела. Его зовёт не только WorldEnvironment.Update
|
||||
/// (там позиция уже подменена), но и сам чанк при появлении - тест 03.10: «змея и одержимый видят вместо
|
||||
/// дверей деревянные блоки». Подменяем позицию и здесь: змея, одержимый, дух чертогов.</summary>
|
||||
[HarmonyPatch(typeof(ChunkGameObject), nameof(ChunkGameObject.CheckLODs))]
|
||||
public static class Patch_ChunkGameObject_CheckLODs_Snake
|
||||
{
|
||||
public static void Prefix(out Vector3 __state) { __state = SnakeAudioSwap.Prefix(); }
|
||||
public static void Postfix(Vector3 __state) { SnakeAudioSwap.Postfix(__state); }
|
||||
}
|
||||
|
||||
/// <summary>Детализация зданий (POI LOD) - вокруг одержимого, с полным радиусом (03.10, сжатая область тела).</summary>
|
||||
[HarmonyPatch(typeof(PrefabLODManager), nameof(PrefabLODManager.FrameUpdate))]
|
||||
public static class Patch_PrefabLODManager_FrameUpdate_Possession
|
||||
{
|
||||
public static void Prefix(out Vector3 __state) { __state = Possession.Active ? SnakeAudioSwap.Prefix() : Vector3.zero; }
|
||||
public static void Postfix(Vector3 __state) { SnakeAudioSwap.Postfix(__state); }
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(Audio.Manager), nameof(Audio.Manager.Play), new[] { typeof(Vector3), typeof(string), typeof(int), typeof(bool), typeof(float) })]
|
||||
public static class Patch_AudioManager_PlayAt_Snake
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user