1.7.0: охрана питомцами, один питомец за раз, прицел книг, гео-оссуарий
Питомцы: - охрана хозяина: враг ближе 20 м (в режиме змеи - к телу) атакуется сам, поводок 40 м, охраняют все кроме крысы; приказ прицелом охрана не перебивает - IsFoe исключает существ мода (necro*): питомец выбирал целью себя - призыв нового спутника отзывает прежнего - прицел книг призыва - как у арбалета, краснеет на годной цели Гео-оссуарий (necroGeoOssuary): станция добычи на кровавой сфере - основа - коллектор росы; в 3 ячейки модов любой из 5 ресурсов (камень, уголь, селитра, железо, сланец), 1 сфера = стак 6000 за 5-8 ч - работает без открытого неба; верстак, Подмастерье (ур. 25) - своя модель (бандл necroossuary): кости, корни, сосуды, клыки, черепа с глазами-углями; шейдер и теги коллайдера берутся у ванильного коллектора Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b737dd43d0
commit
f2dfeed126
@@ -74,6 +74,17 @@ namespace NecromancerTome
|
||||
/// <summary>Радиус "дома" для летающих (см. PetInfo.FlyingHomeRadius). Ноль -
|
||||
/// питомец не летающий, дом ему не переставляем.</summary>
|
||||
public int FlyingHomeRadius;
|
||||
|
||||
/// <summary>Охраняет ли хозяина сам - копия PetInfo.Guards.</summary>
|
||||
public bool Guards;
|
||||
|
||||
/// <summary>На кого игрок указал прицелом (PetAttackCommand). Пока эта цель жива и питомец
|
||||
/// занят именно ею, охрана его не перенацеливает. -1 - приказа нет.</summary>
|
||||
public int OrderedTargetId = -1;
|
||||
|
||||
/// <summary>Питомец ушёл дальше GuardLeash и возвращается: пока он не подойдёт к хозяину
|
||||
/// на GuardRange, любые цели с него снимаются - иначе укус в спину разворачивал бы его.</summary>
|
||||
public bool Returning;
|
||||
}
|
||||
|
||||
public static readonly List<TrackedPet> TrackedPets = new List<TrackedPet>();
|
||||
@@ -106,7 +117,7 @@ namespace NecromancerTome
|
||||
|
||||
/// <summary>Called from SummonPatch.cs right after a pet is created and owned.</summary>
|
||||
public static void Register(EntityAlive owner, Entity pet, bool ownFollowTask, bool collisionIgnored,
|
||||
bool wantsLitEyes, int flyingHomeRadius)
|
||||
bool wantsLitEyes, int flyingHomeRadius, bool guards)
|
||||
{
|
||||
TrackedPets.Add(new TrackedPet
|
||||
{
|
||||
@@ -116,6 +127,7 @@ namespace NecromancerTome
|
||||
CollisionIgnored = collisionIgnored,
|
||||
WantsLitEyes = wantsLitEyes,
|
||||
FlyingHomeRadius = flyingHomeRadius,
|
||||
Guards = guards,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -190,6 +202,121 @@ namespace NecromancerTome
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>ОХРАНА ХОЗЯИНА - указание 30.09.2026. Образ: «ночь, мы в виде змеи ползаем на
|
||||
/// другом конце карты, а тело охраняет стервятник». До этого питомец дрался только по приказу
|
||||
/// (PetAttackCommand) - отзыв sanovskiy 23.09: «приказ нужен на каждую цель».
|
||||
///
|
||||
/// Решения пользователя по пунктам:
|
||||
/// - радиус охраны 20 м от хозяина (GuardRange); в режиме змеи хозяин - это лежащее тело,
|
||||
/// позиция игрока там и остаётся;
|
||||
/// - охраняют все, кроме крысы (PetInfo.Guards);
|
||||
/// - отходить можно до 40 м (GuardLeash), дальше - возвращается;
|
||||
/// - бить ближайшего к телу, если не было приказа атаковать конкретного.
|
||||
///
|
||||
/// Враг - тот же, что для тревоги у тела в режиме змеи (SnakeControl.IsFoe): живой EntityEnemy,
|
||||
/// не питомец и не девиант. СПЯЩИЕ НЕ В СЧЁТ: иначе питомец, проходящий сквозь стены, будил бы
|
||||
/// всё здание, пока игрок крадётся, - к спящему слово «приближается» не относится.
|
||||
///
|
||||
/// Уже начатую цель охрана не бросает, пока та в радиусе: выбор «ближайшего» заново каждую
|
||||
/// секунду заставил бы питомца метаться между двумя зомби и не добить ни одного.</summary>
|
||||
public const float GuardRange = 20f;
|
||||
public const float GuardLeash = 40f;
|
||||
|
||||
public static void NoteOrder(int petEntityId, int targetEntityId)
|
||||
{
|
||||
for (int i = 0; i < TrackedPets.Count; i++)
|
||||
{
|
||||
if (TrackedPets[i].PetEntityId == petEntityId)
|
||||
{
|
||||
TrackedPets[i].OrderedTargetId = targetEntityId;
|
||||
TrackedPets[i].Returning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static EntityAlive NearestFoe(World world, Vector3 center, float range)
|
||||
{
|
||||
EntityAlive nearest = null;
|
||||
float best = range * range;
|
||||
for (int i = 0; i < world.EntityAlives.Count; i++)
|
||||
{
|
||||
EntityAlive e = world.EntityAlives[i];
|
||||
if (!SnakeControl.IsFoe(e, null) || e.IsSleeping)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
float distSq = (e.position - center).sqrMagnitude;
|
||||
if (distSq < best)
|
||||
{
|
||||
best = distSq;
|
||||
nearest = e;
|
||||
}
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
|
||||
public static void Guard(TrackedPet tracked, EntityAlive pet, EntityAlive owner, World world)
|
||||
{
|
||||
if (owner.IsDead())
|
||||
{
|
||||
return;
|
||||
}
|
||||
float fromOwnerSq = (pet.position - owner.position).sqrMagnitude;
|
||||
EntityAlive target = pet.GetAttackTarget();
|
||||
|
||||
// Поводок 40 м - для всех спутников, и для приказа игрока тоже.
|
||||
if (tracked.Returning)
|
||||
{
|
||||
if (fromOwnerSq > GuardRange * GuardRange)
|
||||
{
|
||||
if (target != null)
|
||||
{
|
||||
pet.SetRevengeTarget(null);
|
||||
pet.SetAttackTarget(null, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
tracked.Returning = false;
|
||||
Debug.Log("[NecromancerTome] PetFollowPatch: pet " + pet.entityId + " вернулся к хозяину");
|
||||
}
|
||||
else if (fromOwnerSq > GuardLeash * GuardLeash)
|
||||
{
|
||||
tracked.Returning = true;
|
||||
tracked.OrderedTargetId = -1;
|
||||
pet.SetRevengeTarget(null);
|
||||
pet.SetAttackTarget(null, 0);
|
||||
Debug.Log("[NecromancerTome] PetFollowPatch: pet " + pet.entityId + " ушёл на " +
|
||||
Mathf.Sqrt(fromOwnerSq).ToCultureInvariantString("0") + " м от хозяина - цель снята, возвращается");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tracked.Guards)
|
||||
{
|
||||
return;
|
||||
}
|
||||
bool live = target != null && !target.IsDead();
|
||||
if (live && target.entityId == tracked.OrderedTargetId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
tracked.OrderedTargetId = -1;
|
||||
if (live && SnakeControl.IsFoe(target, null) &&
|
||||
(target.position - owner.position).sqrMagnitude <= GuardRange * GuardRange)
|
||||
{
|
||||
return;
|
||||
}
|
||||
EntityAlive foe = NearestFoe(world, owner.position, GuardRange);
|
||||
if (foe == null || foe == target)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pet.SetRevengeTarget(null);
|
||||
pet.SetAttackTarget(foe, PetAttackCommand.OrderTicks);
|
||||
Debug.Log("[NecromancerTome] PetFollowPatch: охрана - pet " + pet.entityId + " идёт на " + foe.entityId +
|
||||
" (" + foe.EntityClass.entityClassName + "), до хозяина " +
|
||||
(foe.position - owner.position).magnitude.ToCultureInvariantString("0.0") + " м");
|
||||
}
|
||||
|
||||
/// <summary>ПИТАНИЕ ПАДАЛЬЮ, 2026-09-18. Наблюдение пользователя: "Дух крысы после того как
|
||||
/// зомби погибает, начинает есть труп. Пусть тогда этот процесс восстанавливает ей здоровье".
|
||||
///
|
||||
@@ -392,6 +519,13 @@ namespace NecromancerTome
|
||||
tracked.CollisionIgnored = TryIgnoreCollisionWithOwner(owner, pet);
|
||||
}
|
||||
|
||||
// Охрана и поводок 40 м - до блока про «дом» летающих: тот в этом же тике должен
|
||||
// увидеть уже назначенную или снятую цель. Рой (не спутник) сюда не попадает.
|
||||
if (tracked.Guards || tracked.OwnFollowTask || tracked.FlyingHomeRadius > 0)
|
||||
{
|
||||
Guard(tracked, pet, owner, world);
|
||||
}
|
||||
|
||||
// СЛЕДОВАНИЕ ДЛЯ ЛЕТАЮЩИХ, 2026-09-18. Дом переставляется на хозяина каждую
|
||||
// секунду, а возвращается питомец САМ, своим полётом: у EntityVulture проверка
|
||||
// "не ушёл ли из дома" встроена и работает каждые 60 тиков. Ни телепорта, ни
|
||||
|
||||
Reference in New Issue
Block a user