Руки одержимого: кулак в момент урона зомби, сжатие под угол рук 45°, скрыты только ячейки пояса персонажа
- Удар рук откладывается на замеренную задержку урона зомби (ItemActionMelee.OnHoldingUpdate) минус ход кулака. - Руки сжимаются по экранным осям как при FOV 45 (как у персонажа), сдвиг - родной -1.7. - От первого лица прячется только windowToolbelt: полоски персонажа (та же группа окон) остаются. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7b63f91a3b
commit
f03c018204
+89
-2
@@ -49,7 +49,15 @@ namespace NecromancerTome
|
|||||||
};
|
};
|
||||||
/// <summary>Сдвиг рук от камеры (подбор necroarms pos). У персонажа игра опускает руки на 1.7 м: начало их
|
/// <summary>Сдвиг рук от камеры (подбор necroarms pos). У персонажа игра опускает руки на 1.7 м: начало их
|
||||||
/// скелета - у ног (vp_FPWeapon PositionOffset 0,-1.7,0.02 для SDCS).</summary>
|
/// скелета - у ног (vp_FPWeapon PositionOffset 0,-1.7,0.02 для SDCS).</summary>
|
||||||
public static Vector3 Offset = new Vector3(0f, -1.55f, 0.05f); // подобрано в игре 13.10 (у персонажа -1.7, но руки рисуются с FOV 45)
|
public static Vector3 Offset = new Vector3(0f, -1.7f, 0.02f);
|
||||||
|
/// <summary>Угол обзора, с которым игра рисует руки персонажа (vp_FPWeapon RenderingFieldOfView 45 у SDCS). Наши руки
|
||||||
|
/// рисует общая камера с широким углом - удар уходил выше и правее прицела (тест 13.10). Тот же вид даёт сжатие
|
||||||
|
/// рук по экранным осям: масштаб (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>Родитель: true - сама камера, false - её UFPSRoot (как у персонажа).</summary>
|
/// <summary>Родитель: true - сама камера, false - её UFPSRoot (как у персонажа).</summary>
|
||||||
public static bool ParentCamera = true;
|
public static bool ParentCamera = true;
|
||||||
|
|
||||||
@@ -266,6 +274,55 @@ namespace NecromancerTome
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Удар зомби начат: урон пройдёт, когда его замах дойдёт до точки удара (ItemActionMelee.OnHoldingUpdate,
|
||||||
|
/// у разных зомби по-разному). Кулак рук запускаем с таким опозданием, чтобы пришёл в тот же миг (тест 13.10:
|
||||||
|
/// «сначала анимация, потом пауза и удар»); _hitDelay - замер прошлых ударов этого зомби.</summary>
|
||||||
|
public static void QueuePunch()
|
||||||
|
{
|
||||||
|
if (_rig == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_attackAt = Time.time;
|
||||||
|
_punchAt = Time.time + Mathf.Max(0f, _hitDelay - FpImpact);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Каждый кадр вида от первого лица (Possession.PlaceCamera): отложенный удар и сжатие под угол рук.</summary>
|
||||||
|
public static void Tick(Camera _cam)
|
||||||
|
{
|
||||||
|
if (_rig == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_punchAt > 0f && Time.time >= _punchAt)
|
||||||
|
{
|
||||||
|
_punchAt = 0f;
|
||||||
|
Punch();
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
public static string Trigger(string _name)
|
public static string Trigger(string _name)
|
||||||
{
|
{
|
||||||
Animator a = Anim;
|
Animator a = Anim;
|
||||||
@@ -382,7 +439,7 @@ namespace NecromancerTome
|
|||||||
public class ConsoleCmdNecroArms : ConsoleCmdAbstract
|
public class ConsoleCmdNecroArms : ConsoleCmdAbstract
|
||||||
{
|
{
|
||||||
public override string[] getCommands() => new[] { "necroarms" };
|
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>]";
|
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 <с>]";
|
||||||
|
|
||||||
void Out(string _s)
|
void Out(string _s)
|
||||||
{
|
{
|
||||||
@@ -418,6 +475,16 @@ namespace NecromancerTome
|
|||||||
Out("удар - " + FpArms.PunchTrigger);
|
Out("удар - " + FpArms.PunchTrigger);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (cmd == "fov" && _params.Count >= 2 && float.TryParse(_params[1].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out float fov))
|
||||||
|
{
|
||||||
|
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")
|
else if (cmd == "anim")
|
||||||
{
|
{
|
||||||
Out(FpArms.DumpAnim());
|
Out(FpArms.DumpAnim());
|
||||||
@@ -468,4 +535,24 @@ namespace NecromancerTome
|
|||||||
[HarmonyPrefix, HarmonyPatch(nameof(AnimatorMeleeAttackState.OnStateExit))]
|
[HarmonyPrefix, HarmonyPatch(nameof(AnimatorMeleeAttackState.OnStateExit))]
|
||||||
public static bool Exit(Animator animator) => !Ours(animator);
|
public static bool Exit(Animator animator) => !Ours(animator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Миг урона одержимого зомби (ItemActionMelee.OnHoldingUpdate снимает bAttackStarted, когда замах дошёл до
|
||||||
|
/// точки удара) - FpArms мерит по нему задержку кулака.</summary>
|
||||||
|
[HarmonyPatch(typeof(ItemActionMelee), nameof(ItemActionMelee.OnHoldingUpdate))]
|
||||||
|
public static class Patch_ItemActionMelee_OnHoldingUpdate_FpArms
|
||||||
|
{
|
||||||
|
public static void Prefix(ItemActionData _actionData, out bool __state)
|
||||||
|
{
|
||||||
|
__state = _actionData is ItemActionMelee.InventoryDataMelee d && d.bAttackStarted &&
|
||||||
|
Possession.IsPossessed(_actionData.invData?.holdingEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Postfix(ItemActionData _actionData, bool __state)
|
||||||
|
{
|
||||||
|
if (__state && _actionData is ItemActionMelee.InventoryDataMelee d && !d.bAttackStarted)
|
||||||
|
{
|
||||||
|
FpArms.OnZombieHit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -368,6 +368,7 @@ namespace NecromancerTome
|
|||||||
FirstPerson = false;
|
FirstPerson = false;
|
||||||
HideBody(null);
|
HideBody(null);
|
||||||
FpArms.Hide(_player);
|
FpArms.Hide(_player);
|
||||||
|
ShowPlayerBelt(true);
|
||||||
RatLoot.CloseIfViewing();
|
RatLoot.CloseIfViewing();
|
||||||
WolfBag.CloseWindow();
|
WolfBag.CloseWindow();
|
||||||
RatPhase.Stop(GameManager.Instance?.World, Zombie, "выход из одержимости");
|
RatPhase.Stop(GameManager.Instance?.World, Zombie, "выход из одержимости");
|
||||||
@@ -596,13 +597,12 @@ namespace NecromancerTome
|
|||||||
if (FirstPerson)
|
if (FirstPerson)
|
||||||
{
|
{
|
||||||
FpArms.Show(_player, z);
|
FpArms.Show(_player, z);
|
||||||
// Пояс персонажа в виде от первого лица скрыт (13.10); обратно игра откроет его сама (OnHUD).
|
|
||||||
_player.PlayerUI?.windowManager.Close(SnakeControl.ToolbeltWindow);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
HideBody(null);
|
HideBody(null);
|
||||||
FpArms.Hide(_player);
|
FpArms.Hide(_player);
|
||||||
|
ShowPlayerBelt(true);
|
||||||
}
|
}
|
||||||
Debug.Log("[NecromancerTome] Possession: вид от " + (FirstPerson ? "первого" : "третьего") + " лица");
|
Debug.Log("[NecromancerTome] Possession: вид от " + (FirstPerson ? "первого" : "третьего") + " лица");
|
||||||
}
|
}
|
||||||
@@ -845,7 +845,7 @@ namespace NecromancerTome
|
|||||||
if (z.Attack(false))
|
if (z.Attack(false))
|
||||||
{
|
{
|
||||||
z.Attack(true);
|
z.Attack(true);
|
||||||
FpArms.Punch(); // руки от первого лица бьют вместе с зомби (13.10)
|
FpArms.QueuePunch(); // руки от первого лица бьют вместе с зомби (13.10), кулак - в момент урона
|
||||||
if (IsPet && BearControl.Is(z))
|
if (IsPet && BearControl.Is(z))
|
||||||
{
|
{
|
||||||
BearControl.Smash(world, _player, z); // удар лапой и по блоку (решено 09.10)
|
BearControl.Smash(world, _player, z); // удар лапой и по блоку (решено 09.10)
|
||||||
@@ -2110,6 +2110,18 @@ namespace NecromancerTome
|
|||||||
_flyZoomHeld = false;
|
_flyZoomHeld = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Ячейки пояса персонажа (окно windowToolbelt) - скрыты в виде от первого лица (указание 13.10: «пояс игрока
|
||||||
|
/// нужно прятать», у зомби свой пояс). Только это окно: группа toolbelt держит и полоски здоровья и сытости персонажа
|
||||||
|
/// (тест 13.10 - пропали вместе с поясом). Ставится каждый кадр - игра может показать окно снова.</summary>
|
||||||
|
static void ShowPlayerBelt(bool _show)
|
||||||
|
{
|
||||||
|
XUiV_Window w = _player?.PlayerUI?.xui?.GetWindow("windowToolbelt");
|
||||||
|
if (w != null && w.IsVisible != _show)
|
||||||
|
{
|
||||||
|
w.IsVisible = _show;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Тело одержимого в виде от первого лица - только тень (тест 13.10: изнутри видно тело, у паука на
|
/// <summary>Тело одержимого в виде от первого лица - только тень (тест 13.10: изнутри видно тело, у паука на
|
||||||
/// бегу лапы и череп в кадре; сжатие одной головы не спасало). Видимость - дело своего экрана: другие игроки
|
/// бегу лапы и череп в кадре; сжатие одной головы не спасало). Видимость - дело своего экрана: другие игроки
|
||||||
/// рисуют этого зомби сами и видят его как обычно; так же игра прячет тело персонажа от первого лица.
|
/// рисуют этого зомби сами и видят его как обычно; так же игра прячет тело персонажа от первого лица.
|
||||||
@@ -2171,6 +2183,8 @@ namespace NecromancerTome
|
|||||||
: Vector3.Lerp(_fpEye, want, 1f - Mathf.Exp(-FpSmooth * Time.deltaTime));
|
: Vector3.Lerp(_fpEye, want, 1f - Mathf.Exp(-FpSmooth * Time.deltaTime));
|
||||||
HideBody(z);
|
HideBody(z);
|
||||||
_camera.SetPositionAndRotation(_fpEye, rot);
|
_camera.SetPositionAndRotation(_fpEye, rot);
|
||||||
|
FpArms.Tick(_player?.playerCamera);
|
||||||
|
ShowPlayerBelt(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Vector3 eye = anchor + Vector3.up * CamUp - rot * Vector3.forward * CamBack;
|
Vector3 eye = anchor + Vector3.up * CamUp - rot * Vector3.forward * CamBack;
|
||||||
|
|||||||
@@ -1320,8 +1320,8 @@ namespace NecromancerTome
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Пояс в режиме змеи скрыт (указание 28.09: «прятать оверлей с поясом»), так же в
|
/// <summary>Пояс в режиме змеи скрыт (указание 28.09: «прятать оверлей с поясом»), так же в
|
||||||
/// чертогах, у одержимого питомца (06.10) и у одержимого от первого лица (13.10: «пояс игрока нужно прятать
|
/// чертогах и у одержимого питомца (06.10); от первого лица - только ячейки пояса (Possession.ShowPlayerBelt), не
|
||||||
/// в одержимости от первого лица»; потом у зомби будут свои пояса). Игра каждый
|
/// вся группа: в ней же полоски здоровья и сытости персонажа. Игра каждый
|
||||||
/// кадр в EntityPlayerLocal.OnHUD открывает окно "toolbelt", если оно закрыто, - поэтому не
|
/// кадр в EntityPlayerLocal.OnHUD открывает окно "toolbelt", если оно закрыто, - поэтому не
|
||||||
/// закрываем его раз за разом, а не даём открыться: все Open сходятся в openInternal.</summary>
|
/// закрываем его раз за разом, а не даём открыться: все Open сходятся в openInternal.</summary>
|
||||||
[HarmonyPatch(typeof(GUIWindowManager), "openInternal")]
|
[HarmonyPatch(typeof(GUIWindowManager), "openInternal")]
|
||||||
@@ -1329,7 +1329,7 @@ namespace NecromancerTome
|
|||||||
{
|
{
|
||||||
public static bool Prefix(GUIWindow _w)
|
public static bool Prefix(GUIWindow _w)
|
||||||
{
|
{
|
||||||
return !((SnakeControl.Controlling || MindControl.Controlling || (Possession.Active && (Possession.IsPet || Possession.FirstPerson))) && _w != null && _w.Id == SnakeControl.ToolbeltWindow);
|
return !((SnakeControl.Controlling || MindControl.Controlling || (Possession.Active && Possession.IsPet)) && _w != null && _w.Id == SnakeControl.ToolbeltWindow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user