using HarmonyLib; using UnityEngine; namespace NecromancerTome { /// /// Одержимый медведь - танк (решено 09.10, BACKLOG «Медведь и лютоволк в одержимости»): /// - толстая шкура: пока в нём игрок, урон по нему вдвое меньше (вне одержимости медведь прежний); /// - рёв на ПКМ: зомби ближе RoarRange бросают свои цели и идут на медведя, спящие просыпаются - /// увести толпу от тела или базы. Та же провокация, что каждую секунду делает одержимость /// (Possession.Taunt), только шире, без «видит ли» и на 20 с; /// - удар лапой по блоку: ЛКМ по блоку под прицелом бьёт его на SmashDamage (дерево и двери за 2-3 /// удара, бетон дольше). /// 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"; /// Урон блоку за удар лапой. Для сравнения: удар ванильного зомби-медведя по блокам - десятки. public const int SmashDamage = 300; /// Досягаемость лапы от головы медведя, м. 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); } /// ЛКМ: блок под прицелом в досягаемости лапы (не существо) - урон SmashDamage. 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() != 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 + ")"); } } /// Толстая шкура одержимого медведя - вдвое меньше урона. [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)); } } } }