Одержимость питомцев, стервятник-разведчик, метки на карте, череп, глава письма

- Одержимость питомца: ПКМ с книгой - вселиться, E - отозвать (и изнутри питомца),
  пояс скрыт, надпись о враге у тела.
- Одержимый стервятник: полёт, рывок на ЛКМ с 10 м с прямым уроном, звуком и
  кровотечением, обычный прицел краснеет на цели; посадка в позу 0 и взлёт на пробел;
  ПКМ - клич-приманка (паникующая курица, откат 0.5 с); F - питаться трупами и
  блоками-падалью, доеденное исчезает.
- Дух змеи атакует только нежить; фиолетовая метка на карте у змеи и одержимого.
- Череп из красного трупа (этап 1 цинерария); глава книги «Письмо архимага» с видео.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-10-06 22:09:28 +03:00
co-authored by Claude Opus 5.5
parent a79bb085ab
commit beab1640b0
16 changed files with 1287 additions and 57 deletions
+102 -18
View File
@@ -316,14 +316,9 @@ namespace NecromancerTome
if (_actionData.indexInEntityOfAction == RecallActionIndex)
{
if (owned.Count > 0)
{
RecallPet(holdingEntity, owned[0].Id, tooltips.RecalledKey, tooltips.ConsumesBook ? tooltips.SummonItemName : null);
}
else if (holdingEntity.world != null)
{
GameManager.ShowTooltip(GameManager.Instance.World.GetPrimaryPlayer(), tooltips.NothingToRecallKey);
}
// С 06.10 ПКМ - одержимость питомца (Patch_..._ExecuteAction_InstantRecall делает её на
// нажатии, сюда доходит только если та пропустила); отзыв - на E (PetRecallKey).
TryPossessPet(holdingEntity, owned, tooltips);
return false;
}
@@ -436,8 +431,66 @@ namespace NecromancerTome
/// <summary>summonItemName is null when this species doesn't consume its book on summon
/// (see PetInfo.ConsumesBook) - nothing was taken, so nothing is given back.</summary>
/// <summary>ПКМ с книгой призыва (указание 06.10): вселиться в призванного питомца этой книги.
/// Нет питомца - надпись necroPetNothingToPossess.</summary>
public static void TryPossessPet(EntityAlive holdingEntity, List<OwnedEntityData> owned, PetInfo tooltips)
{
EntityPlayerLocal local = holdingEntity as EntityPlayerLocal;
if (local == null || tooltips.RecalledKey == null)
{
return;
}
EntityAlive pet = owned.Count > 0 && holdingEntity.world != null ? holdingEntity.world.GetEntity(owned[0].Id) as EntityAlive : null;
if (pet == null || pet.IsDead())
{
GameManager.ShowTooltip(local, "necroPetNothingToPossess");
return;
}
if (!Possession.EnterPet(local, pet))
{
Debug.Log("[NecromancerTome] SummonPatch: вселиться в питомца " + pet.entityId + " нельзя (змея, чертоги, транспорт или уже одержимость)");
}
}
/// <summary>Отозвать питомца книги, которую держит игрок (E, PetRecallKey).</summary>
public static void RecallHeldBookPet(EntityAlive holdingEntity, string entityToSpawn, PetInfo tooltips)
{
int petClassId = EntityClass.GetId(entityToSpawn);
if (petClassId == -1)
{
return;
}
List<OwnedEntityData> owned = holdingEntity.GetOwnedEntities(petClassId);
if (owned.Count > 0)
{
RecallPet(holdingEntity, owned[0].Id, tooltips.RecalledKey, tooltips.ConsumesBook ? tooltips.SummonItemName : null);
}
else if (holdingEntity.world != null)
{
GameManager.ShowTooltip(GameManager.Instance.World.GetPrimaryPlayer(), tooltips.NothingToRecallKey);
}
}
/// <summary>E в одержимом питомце (тест 06.10: «E не сработал, срабатывает R») - отзыв его самого:
/// игрок возвращается в тело, питомец уходит в книгу. HandleInteraction во время одержимости не
/// идёт (PlayerMoveController.Update выключен), поэтому зовёт Possession. True - отозван.</summary>
public static bool RecallPossessedPet(EntityAlive owner, EntityAlive pet)
{
if (owner == null || pet == null || !EntityClass.list.TryGetValue(pet.entityClass, out EntityClass ec) ||
!LimitedPets.TryGetValue(ec.entityClassName, out PetInfo info) || info.RecalledKey == null)
{
return false;
}
RecallPet(owner, pet.entityId, info.RecalledKey, info.ConsumesBook ? info.SummonItemName : null);
return true;
}
public static void RecallPet(EntityAlive owner, int petEntityId, string recalledTooltipKey, string summonItemName)
{
if (Possession.Active && Possession.Zombie != null && Possession.Zombie.entityId == petEntityId)
{
Possession.Exit("питомец отозван");
}
World world = owner.world;
if (world != null)
{
@@ -551,17 +604,9 @@ namespace NecromancerTome
}
lastRecallTime = Time.time;
// С 06.10 ПКМ - одержимость питомца, отзыв переехал на E (PetRecallKey).
List<OwnedEntityData> owned = holdingEntity.GetOwnedEntities(petClassId);
if (owned.Count > 0)
{
Patch_ItemActionSpawnEntity_Spawn_PetLimit.RecallPet(holdingEntity, owned[0].Id,
petInfo.RecalledKey, petInfo.ConsumesBook ? petInfo.SummonItemName : null);
holdingEntity.PlayOneShot(__instance.soundWarn);
}
else
{
GameManager.ShowTooltip(GameManager.Instance.World.GetPrimaryPlayer(), petInfo.NothingToRecallKey);
}
Patch_ItemActionSpawnEntity_Spawn_PetLimit.TryPossessPet(holdingEntity, owned, petInfo);
return false;
}
}
@@ -619,6 +664,40 @@ namespace NecromancerTome
}
}
/// <summary>
/// ОТЗЫВ НА E - указание 06.10: «Отозвать питомца снимаем с ПКМ и ставим на E» (ПКМ отдана
/// одержимости питомца). E у игрока - «взаимодействие»: PlayerMoveController.HandleInteraction
/// получает _activate и открывает то, что под прицелом (блок, предмет, сущность). Отзываем только
/// когда под прицелом взаимодействовать не с чем - иначе E с книгой в руке перестала бы открывать
/// двери и ящики. «Не с чем» = у игры нет подсказки «Нажмите E…» (strTextLabelPointingTo пуст: на
/// кадре нажатия она не обновляется и держит прошлый кадр - то, куда игрок смотрел).
/// </summary>
[HarmonyPatch(typeof(PlayerMoveController), nameof(PlayerMoveController.HandleInteraction))]
public static class Patch_PlayerMoveController_HandleInteraction_PetRecallKey
{
public static void Postfix(PlayerMoveController __instance, bool _activate)
{
if (!_activate || Possession.Active || !string.IsNullOrEmpty(__instance.strTextLabelPointingTo))
{
return;
}
EntityPlayerLocal player = __instance.entityPlayerLocal;
if (player == null || player.AttachedToEntity != null || player.IsDead() || player.inventory == null)
{
return;
}
ItemClass held = player.inventory.holdingItem;
if (held?.Actions == null || held.Actions.Length == 0 || !(held.Actions[0] is ItemActionSpawnEntity spawn) ||
!Patch_ItemActionSpawnEntity_Spawn_PetLimit.LimitedPets.TryGetValue(spawn.entityToSpawn, out Patch_ItemActionSpawnEntity_Spawn_PetLimit.PetInfo info) ||
info.RecalledKey == null)
{
return;
}
Patch_ItemActionSpawnEntity_Spawn_PetLimit.RecallHeldBookPet(player, spawn.entityToSpawn, info);
player.PlayOneShot(spawn.soundWarn);
}
}
[HarmonyPatch(typeof(EntityFactory), "CreateEntity", new System.Type[] { typeof(int), typeof(Vector3), typeof(Vector3) })]
public static class Patch_EntityFactory_CreateEntity_PetOwnership
{
@@ -683,6 +762,11 @@ namespace NecromancerTome
PetFollowPatch.Register(owner, __result, petInfo.UsesFollowTask, collisionIgnored, petInfo.LitEyes,
petInfo.FlyingHomeRadius, petInfo.Guards);
AttachFollowTask(owner, __result, petInfo);
// Указание 06.10: при призыве ненадолго - «Отозвать питомца - E». Рой не отзывается.
if (petInfo.RecalledKey != null && owner is EntityPlayerLocal ownerLocal)
{
GameManager.ShowTooltip(ownerLocal, "necroPetRecallHint");
}
// Полупрозрачность больше НЕ делается здесь. Бывший ApplyGhostlyTransparency ставил
// альфу в material.color и не работал ни разу: шейдеры моделей мобов альфу не
// поддерживают (на Рое это выяснилось ещё 28.08 - он только засорял лог). Призрачный