Руки одержимого: урон по анимации удара рук, как у персонажа; в покое руки выше, на удар - ниже
- ЛКМ от первого лица - удар рук; через RaycastTime их состояния удара (как impactStart у персонажа) зомби бьёт сразу (ForceImpact в IsAttackImpact, задержка луча снята). Замер задержки урона убран. - Положение: в покое 0,-1.55,0.05 без сжатия; на удар - плавно к -1.7 со сжатием под FOV 45. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
885c344b3c
commit
2cae2377e2
+59
-38
@@ -55,9 +55,14 @@ namespace NecromancerTome
|
||||
/// рук по экранным осям: масштаб (k, k, 1) от камеры, k = tg(FOV камеры / 2) / tg(ArmsFov / 2) - каждый кадр
|
||||
/// (FOV меняется при приближении).</summary>
|
||||
public static float ArmsFov = 45f;
|
||||
/// <summary>Через сколько после сигнала удара кулак рук доходит до цели (подбор necroarms impact).</summary>
|
||||
public static float FpImpact = 0.15f;
|
||||
static float _punchAt, _attackAt, _hitDelay = 0.45f;
|
||||
/// <summary>Положение рук в покое (подобрано 13.10, без сжатия): «руки подними как были»; на удар - Offset со
|
||||
/// сжатием под ArmsFov, «анимацию удара занизь как сейчас». Переход - за BlendTime.</summary>
|
||||
public static Vector3 IdleOffset = new Vector3(0f, -1.55f, 0.05f);
|
||||
public const float BlendTime = 0.12f;
|
||||
/// <summary>Минимальный промежуток между ударами рук.</summary>
|
||||
public const float SwingTime = 0.6f;
|
||||
static bool _inAttack;
|
||||
static float _impactAt, _blend;
|
||||
/// <summary>Родитель: true - сама камера, false - её UFPSRoot (как у персонажа).</summary>
|
||||
public static bool ParentCamera = true;
|
||||
|
||||
@@ -274,32 +279,23 @@ namespace NecromancerTome
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Удар зомби начат: урон пройдёт, когда его замах дойдёт до точки удара (ItemActionMelee.OnHoldingUpdate,
|
||||
/// у разных зомби по-разному). Кулак рук запускаем с таким опозданием, чтобы пришёл в тот же миг (тест 13.10:
|
||||
/// «сначала анимация, потом пауза и удар»); _hitDelay - замер прошлых ударов этого зомби.</summary>
|
||||
public static void QueuePunch()
|
||||
/// <summary>Анимация удара рук вошла в состояние удара (AnimatorMeleeAttackState у наших рук): через RaycastTime -
|
||||
/// миг удара, как у персонажа (impactStart). Руки на это время уходят в нижнее положение (под прицел).</summary>
|
||||
public static void OnSwingEnter(float _raycastTime)
|
||||
{
|
||||
if (_rig == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_attackAt = Time.time;
|
||||
_punchAt = Time.time + Mathf.Max(0f, _hitDelay - FpImpact);
|
||||
_inAttack = true;
|
||||
_impactAt = Time.time + Mathf.Max(0.01f, _raycastTime);
|
||||
}
|
||||
|
||||
/// <summary>Урон зомби прошёл - замер задержки для следующих ударов.</summary>
|
||||
public static void OnSwingExit()
|
||||
{
|
||||
_inAttack = false;
|
||||
}
|
||||
|
||||
/// <summary>Урон зомби прошёл - вынужденный миг удара снят.</summary>
|
||||
public static void OnZombieHit()
|
||||
{
|
||||
if (_attackAt <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
float d = Time.time - _attackAt;
|
||||
_attackAt = 0f;
|
||||
if (d > 0.02f && d < 2f)
|
||||
{
|
||||
_hitDelay = d;
|
||||
}
|
||||
Possession.ForceImpact = false;
|
||||
}
|
||||
|
||||
/// <summary>Каждый кадр вида от первого лица (Possession.PlaceCamera): отложенный удар и сжатие под угол рук.</summary>
|
||||
@@ -309,18 +305,20 @@ namespace NecromancerTome
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_punchAt > 0f && Time.time >= _punchAt)
|
||||
if (_impactAt > 0f && Time.time >= _impactAt)
|
||||
{
|
||||
_punchAt = 0f;
|
||||
Punch();
|
||||
_impactAt = 0f;
|
||||
Possession.FpImpact();
|
||||
}
|
||||
float k = 1f;
|
||||
if (_cam != null && ArmsFov > 1f)
|
||||
{
|
||||
k = Mathf.Tan(_cam.fieldOfView * 0.5f * Mathf.Deg2Rad) / Mathf.Tan(ArmsFov * 0.5f * Mathf.Deg2Rad);
|
||||
}
|
||||
_rig.transform.localScale = new Vector3(k, k, 1f);
|
||||
_rig.transform.localPosition = new Vector3(Offset.x * k, Offset.y * k, Offset.z);
|
||||
_blend = Mathf.MoveTowards(_blend, _inAttack ? 1f : 0f, Time.deltaTime / BlendTime);
|
||||
float kk = Mathf.Lerp(1f, k, _blend);
|
||||
_rig.transform.localScale = new Vector3(kk, kk, 1f);
|
||||
_rig.transform.localPosition = Vector3.Lerp(IdleOffset, new Vector3(Offset.x * k, Offset.y * k, Offset.z), _blend);
|
||||
}
|
||||
|
||||
public static string Trigger(string _name)
|
||||
@@ -439,7 +437,7 @@ namespace NecromancerTome
|
||||
public class ConsoleCmdNecroArms : ConsoleCmdAbstract
|
||||
{
|
||||
public override string[] getCommands() => new[] { "necroarms" };
|
||||
public override string getDescription() => "NecromancerTome: руки одержимого от первого лица - [pos x y z | parent cam|ufps | anim | mat | set <param> <value> | trigger <name> | tint r g b | albedo on|off | punch <trigger> | fov <угол> | impact <с>]";
|
||||
public override string getDescription() => "NecromancerTome: руки одержимого от первого лица - [pos x y z | parent cam|ufps | anim | mat | set <param> <value> | trigger <name> | tint r g b | albedo on|off | punch <trigger> | fov <deg>]";
|
||||
|
||||
void Out(string _s)
|
||||
{
|
||||
@@ -479,12 +477,6 @@ namespace NecromancerTome
|
||||
{
|
||||
FpArms.ArmsFov = fov;
|
||||
}
|
||||
else if (cmd == "impact" && _params.Count >= 2 && float.TryParse(_params[1].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out float imp))
|
||||
{
|
||||
FpArms.FpImpact = imp;
|
||||
Out("кулак доходит за " + imp.ToString(CultureInfo.InvariantCulture) + " с");
|
||||
return;
|
||||
}
|
||||
else if (cmd == "anim")
|
||||
{
|
||||
Out(FpArms.DumpAnim());
|
||||
@@ -527,13 +519,29 @@ namespace NecromancerTome
|
||||
static bool Ours(Animator _a) => _a != null && _a.GetComponent<AnimationEventBridge>() == null;
|
||||
|
||||
[HarmonyPrefix, HarmonyPatch(nameof(AnimatorMeleeAttackState.OnStateEnter))]
|
||||
public static bool Enter(Animator animator) => !Ours(animator);
|
||||
public static bool Enter(AnimatorMeleeAttackState __instance, Animator animator)
|
||||
{
|
||||
if (!Ours(animator))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
FpArms.OnSwingEnter(__instance.RaycastTime); // миг удара - как у персонажа (13.10)
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix, HarmonyPatch(nameof(AnimatorMeleeAttackState.OnStateUpdate))]
|
||||
public static bool Update(Animator animator) => !Ours(animator);
|
||||
|
||||
[HarmonyPrefix, HarmonyPatch(nameof(AnimatorMeleeAttackState.OnStateExit))]
|
||||
public static bool Exit(Animator animator) => !Ours(animator);
|
||||
public static bool Exit(Animator animator)
|
||||
{
|
||||
if (!Ours(animator))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
FpArms.OnSwingExit();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Миг урона одержимого зомби (ItemActionMelee.OnHoldingUpdate снимает bAttackStarted, когда замах дошёл до
|
||||
@@ -555,4 +563,17 @@ namespace NecromancerTome
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Миг удара одержимого от первого лица задают руки (Possession.FpImpact): пока ForceImpact - «замах дошёл».</summary>
|
||||
[HarmonyPatch(typeof(EntityAlive), nameof(EntityAlive.IsAttackImpact))]
|
||||
public static class Patch_EntityAlive_IsAttackImpact_FpArms
|
||||
{
|
||||
public static void Postfix(EntityAlive __instance, ref bool __result)
|
||||
{
|
||||
if (Possession.ForceImpact && Possession.IsPossessed(__instance))
|
||||
{
|
||||
__result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,6 +365,7 @@ namespace NecromancerTome
|
||||
return;
|
||||
}
|
||||
Active = false;
|
||||
ForceImpact = false;
|
||||
FirstPerson = false;
|
||||
HideBody(null);
|
||||
FpArms.Hide(_player);
|
||||
@@ -838,14 +839,21 @@ namespace NecromancerTome
|
||||
// Нюх (указание 09.10): все существа рядом с собакой - на компасе и карте.
|
||||
DogSense.Tick(world, z, Time.deltaTime);
|
||||
}
|
||||
if (attack && Time.time >= _nextAttack)
|
||||
if (attack && Time.time >= _nextAttack && FpArms.Active)
|
||||
{
|
||||
// От первого лица - как у персонажа (указание 13.10): ЛКМ - удар рук, урон зомби - когда их анимация дошла
|
||||
// до точки удара (FpArms -> FpImpact), а не по его собственному замаху.
|
||||
PossessionPowers.BreakInvis(z, "удар");
|
||||
FpArms.Punch();
|
||||
_nextAttack = Time.time + Mathf.Max(FpArms.SwingTime, z.GetAttackTimeoutTicks() / 20f);
|
||||
}
|
||||
else if (attack && Time.time >= _nextAttack)
|
||||
{
|
||||
PossessionPowers.BreakInvis(z, "удар");
|
||||
z.SetLookPosition(MeleeAim(world, z));
|
||||
if (z.Attack(false))
|
||||
{
|
||||
z.Attack(true);
|
||||
FpArms.QueuePunch(); // руки от первого лица бьют вместе с зомби (13.10), кулак - в момент урона
|
||||
if (IsPet && BearControl.Is(z))
|
||||
{
|
||||
BearControl.Smash(world, _player, z); // удар лапой и по блоку (решено 09.10)
|
||||
@@ -2110,6 +2118,34 @@ namespace NecromancerTome
|
||||
_flyZoomHeld = false;
|
||||
}
|
||||
|
||||
/// <summary>Миг удара рук от первого лица (FpArms): зомби бьёт сразу - его удар начат и сразу «дошёл до точки
|
||||
/// удара» (ForceImpact в EntityAlive.IsAttackImpact, задержка луча ItemActionMelee снята). Урон, дальность,
|
||||
/// цель - его, взгляд - на прицел (MeleeAim).</summary>
|
||||
public static bool ForceImpact;
|
||||
|
||||
public static void FpImpact()
|
||||
{
|
||||
EntityAlive z = Zombie;
|
||||
if (!Active || z == null || z.IsDead())
|
||||
{
|
||||
return;
|
||||
}
|
||||
z.SetLookPosition(MeleeAim(z.world, z));
|
||||
_meleeLookUntil = Time.time + 0.6f;
|
||||
if (!z.Attack(false))
|
||||
{
|
||||
Debug.Log("[NecromancerTome] Possession: удар рук - зомби не может бить сейчас");
|
||||
return;
|
||||
}
|
||||
z.Attack(true);
|
||||
ForceImpact = true;
|
||||
if (z.inventory?.holdingItemData?.actionData != null && z.inventory.holdingItemData.actionData.Count > 0 &&
|
||||
z.inventory.holdingItemData.actionData[0] is ItemActionMelee.InventoryDataMelee d)
|
||||
{
|
||||
d.lastUseTime -= 10f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Ячейки пояса персонажа (окно windowToolbelt) - скрыты в виде от первого лица (указание 13.10: «пояс игрока
|
||||
/// нужно прятать», у зомби свой пояс). Только это окно: группа toolbelt держит и полоски здоровья и сытости персонажа
|
||||
/// (тест 13.10 - пропали вместе с поясом). Ставится каждый кадр - игра может показать окно снова.</summary>
|
||||
|
||||
Reference in New Issue
Block a user