Files
necromants-tome-7d2d-3-2/HarmonySrc/BearControl.cs
T
AlexCubeandClaude Opus 5.5 31aa99860c Одержимость питомцев: роли медведя, волка, собаки и крысы; ноша стервятника; череп при обыске
- Могильная крыса: своя модель без волчьей шерсти; стоит на земле (центр капсулы);
  E - осмотр контейнеров без права взять (окно игры, рюкзак скрыт, отказ «инвентарь
  заполнен», контейнер не разрушается); ПКМ - проход сквозь постройки, полупрозрачная.
- Стервятник: курица в когтях - слепок с обвисшими крыльями и шеей, без хитбокса.
- Зомбособака: нюх - все существа рядом на компасе и карте.
- Медведь: толстая шкура (вдвое меньше урона), рёв-провокация, удар лапой по блокам.
- Лютоволк: бросок на цель с опрокидыванием, след крови раненых, сумка на 3 ячейки
  (лут, выгрузка у тела, выпадает мешком при гибели и отзыве; отдельный файл сейва).
- Череп (останки): 2% при обычном обыске красного трупа вместо браслета.
- Прицел книги не рисуется в одержимости; подсказки вселения на 13 языках.
- Замер EntityLeakProbe: удалённые, но живые в памяти сущности - в лог.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:58:40 +03:00

114 lines
4.8 KiB
C#

using HarmonyLib;
using UnityEngine;
namespace NecromancerTome
{
/// <summary>
/// Одержимый медведь - танк (решено 09.10, BACKLOG «Медведь и лютоволк в одержимости»):
/// - толстая шкура: пока в нём игрок, урон по нему вдвое меньше (вне одержимости медведь прежний);
/// - рёв на ПКМ: зомби ближе RoarRange бросают свои цели и идут на медведя, спящие просыпаются -
/// увести толпу от тела или базы. Та же провокация, что каждую секунду делает одержимость
/// (Possession.Taunt), только шире, без «видит ли» и на 20 с;
/// - удар лапой по блоку: ЛКМ по блоку под прицелом бьёт его на SmashDamage (дерево и двери за 2-3
/// удара, бетон дольше).
/// </summary>
public static class BearControl
{
public const string BearClass = "necroZombieBear";
public const float RoarRange = 15f;
public const float RoarCooldown = 8f;
public const int RoarTicks = 20 * 20;
public const string RoarSound = "bearalert";
/// <summary>Урон блоку за удар лапой. Для сравнения: удар ванильного зомби-медведя по блокам - десятки.</summary>
public const int SmashDamage = 300;
/// <summary>Досягаемость лапы от головы медведя, м.</summary>
public const float SmashReach = 2.6f;
public const float SkinFactor = 0.5f;
static float _nextRoar;
public static bool Is(EntityAlive _e)
{
return _e != null && EntityClass.list.TryGetValue(_e.entityClass, out EntityClass cls) && cls != null &&
cls.entityClassName == BearClass;
}
public static void Roar(World _world, EntityAlive _bear)
{
if (Time.time < _nextRoar)
{
return;
}
_nextRoar = Time.time + RoarCooldown;
_bear.PlayOneShot(RoarSound);
_bear.emodel?.avatarController?.StartAnimationAttack();
int n = 0;
for (int i = 0; i < _world.EntityAlives.Count; i++)
{
EntityAlive e = _world.EntityAlives[i];
if (e == null || e == _bear || e.IsDead() || !(e is EntityZombie) || e.aiManager == null ||
PetFollowPatch.FindOwnerOfPet(e.entityId) != null || (e.position - _bear.position).sqrMagnitude > RoarRange * RoarRange)
{
continue;
}
if (e.IsSleeping)
{
e.ConditionalTriggerSleeperWakeUp();
}
Possession.AllowZombieTarget(e, _bear);
e.SetAttackTarget(_bear, RoarTicks);
n++;
}
Debug.Log("[NecromancerTome] BearControl: рёв - на медведя идут " + n);
}
/// <summary>ЛКМ: блок под прицелом в досягаемости лапы (не существо) - урон SmashDamage.</summary>
public static void Smash(World _world, EntityPlayerLocal _player, EntityAlive _bear)
{
Camera cam = _player.playerCamera;
if (cam == null)
{
return;
}
Vector3 head = _bear.getHeadPosition();
Vector3 from = cam.transform.position + Origin.position;
Ray ray = new Ray(from, cam.transform.forward);
// С существами: если первым на луче стоит существо - это удар по нему, а не по блоку.
if (!Voxel.Raycast(_world, ray, Vector3.Distance(from, head) + SmashReach + 1f, -555528221, 69, 0f))
{
return;
}
WorldRayHitInfo hit = Voxel.voxelRayHitInfo;
if (hit.transform != null && GameUtils.GetHitRootTransform(hit.tag, hit.transform)?.GetComponent<Entity>() != null)
{
return;
}
if ((hit.hit.pos - head).sqrMagnitude > SmashReach * SmashReach)
{
return;
}
Vector3i pos = hit.hit.blockPos;
BlockValue bv = _world.GetBlock(pos);
if (bv.isair || bv.Block == null)
{
return;
}
bv.Block.DamageBlock(_world, new BlockValueRef(pos), bv, SmashDamage, _bear.entityId);
Debug.Log("[NecromancerTome] BearControl: лапой по " + bv.Block.GetBlockName() + " " + pos + " - " + SmashDamage +
" (прочность " + (bv.Block.MaxDamage - bv.damage) + " из " + bv.Block.MaxDamage + ")");
}
}
/// <summary>Толстая шкура одержимого медведя - вдвое меньше урона.</summary>
[HarmonyPatch(typeof(EntityAlive), nameof(EntityAlive.DamageEntity))]
public static class Patch_EntityAlive_DamageEntity_BearSkin
{
public static void Prefix(EntityAlive __instance, ref int _strength)
{
if (_strength > 1 && Possession.IsPossessed(__instance) && BearControl.Is(__instance))
{
_strength = Mathf.Max(1, Mathf.RoundToInt(_strength * BearControl.SkinFactor));
}
}
}
}