139 lines
6.0 KiB
C#
139 lines
6.0 KiB
C#
using System.Collections.Generic;
|
||
using HarmonyLib;
|
||
using UnityEngine;
|
||
|
||
namespace NecromancerTome
|
||
{
|
||
/// <summary>
|
||
/// Тело (вид от третьего лица и для других игроков) держит нож-для-нитей обычным хватом ножа, а не луком (16.10, скрин
|
||
/// от третьего лица: «у персонажа запястье вывихнуто» - анимация деревянного лука тянет правую кисть с ножом). Хват
|
||
/// предмета один на все модели (HoldType 68 нужен рукам первого лица - основа позы HandPose.PresetThread), поэтому
|
||
/// подменяется только параметр аниматора WeaponHoldType/WeaponCarry у тел, кроме рук первого лица, и место ножа в
|
||
/// руке тела. Подбор на лету: necroheld tphold N (0 - выключить подмену).
|
||
/// </summary>
|
||
public static class ThreadKnifeBody
|
||
{
|
||
/// <summary>0 - тело держит хватом предмета (68, лук). 16.10 в игре (necrobody) пользователь выбрал именно его: нож в левой
|
||
/// руке тела, «Нож был нормальным», нужна только поправка кисти и места ножа (ниже). Подмена на 36 - на пробу командой.</summary>
|
||
public static int TpHold = 0;
|
||
|
||
/// <summary>Поправка тела, подобранная 16.10 командами necrobody: левая кисть к ладони на 20° («лучезапястный сустав
|
||
/// выгнуло... словно перелом»: LeftHand Z 302 -> 322), нож в кулаке - item pos 3 -4 -2 см («Вот так идеально»).</summary>
|
||
public static float WristBend = 20f;
|
||
public static Vector3 ItemPos = new Vector3(0.03f, -0.04f, -0.02f);
|
||
static readonly Dictionary<Animator, Transform> _leftHand = new Dictionary<Animator, Transform>();
|
||
|
||
/// <summary>Из HandPoseDriver.LateUpdate: у всех игроков в мире с ножом-для-нитей, кроме своего вида от первого лица.</summary>
|
||
public static void LateUpdate()
|
||
{
|
||
World w = GameManager.Instance?.World;
|
||
if (w?.Players?.list == null || TpHold > 0)
|
||
{
|
||
return;
|
||
}
|
||
foreach (EntityPlayer p in w.Players.list)
|
||
{
|
||
if (p == null || p.inventory?.holdingItem?.Name != CinerariumThreads.ThreadKnifeName || p.emodel == null ||
|
||
p.emodel.IsFPV && !p.isEntityRemote)
|
||
{
|
||
continue;
|
||
}
|
||
AvatarCharacterController ac = p.emodel.avatarController as AvatarCharacterController;
|
||
Animator a = ac?.CharacterBody?.Animator;
|
||
if (a == null)
|
||
{
|
||
continue;
|
||
}
|
||
if (!_leftHand.TryGetValue(a, out Transform lh) || lh == null)
|
||
{
|
||
lh = null;
|
||
foreach (Transform t in a.GetComponentsInChildren<Transform>(true))
|
||
{
|
||
if (t.name == "LeftHand")
|
||
{
|
||
lh = t;
|
||
break;
|
||
}
|
||
}
|
||
if (_leftHand.Count > 16)
|
||
{
|
||
_leftHand.Clear();
|
||
}
|
||
_leftHand[a] = lh;
|
||
}
|
||
if (lh != null)
|
||
{
|
||
lh.localRotation *= Quaternion.Euler(0f, 0f, WristBend);
|
||
}
|
||
if (ac.heldItemTransform != null && !NecroBody.ItemPos.HasValue)
|
||
{
|
||
ac.heldItemTransform.localPosition = ItemPos;
|
||
}
|
||
}
|
||
}
|
||
|
||
public static bool Applies(EntityAlive _e)
|
||
{
|
||
return TpHold > 0 && _e != null && _e.inventory?.holdingItem?.Name == CinerariumThreads.ThreadKnifeName;
|
||
}
|
||
}
|
||
|
||
[HarmonyPatch(typeof(AvatarMultiBodyController), nameof(AvatarMultiBodyController._setInt), new[] { typeof(int), typeof(int), typeof(bool) })]
|
||
public static class Patch_AvatarMultiBodyController_setInt_ThreadKnife
|
||
{
|
||
public static bool Prefix(AvatarMultiBodyController __instance, int _propertyHash, int _value)
|
||
{
|
||
bool hold = _propertyHash == AvatarController.weaponHoldTypeHash;
|
||
if (!hold && _propertyHash != AvatarController.weaponCarryHash || !ThreadKnifeBody.Applies(__instance.Entity))
|
||
{
|
||
return true;
|
||
}
|
||
BodyAnimator fp = (__instance as AvatarLocalPlayerController)?.FPSArms;
|
||
int tp = hold ? ThreadKnifeBody.TpHold : AnimationDelayData.AnimationDelay[ThreadKnifeBody.TpHold].Carry;
|
||
foreach (BodyAnimator b in __instance.BodyAnimators)
|
||
{
|
||
Animator a = b.Animator;
|
||
int v = b == fp ? _value : tp;
|
||
if (a != null && a.GetInteger(_propertyHash) != v)
|
||
{
|
||
a.SetInteger(_propertyHash, v);
|
||
}
|
||
}
|
||
return false; // без рассылки по сети: у других игроков мод ставит то же сам
|
||
}
|
||
}
|
||
|
||
/// <summary>Место ножа в руке тела - по хвату ножа (игра берёт смещение по HoldType предмета).</summary>
|
||
[HarmonyPatch(typeof(AvatarMultiBodyController), nameof(AvatarMultiBodyController.SetInRightHand))]
|
||
public static class Patch_AvatarMultiBodyController_SetInRightHand_ThreadKnife
|
||
{
|
||
public static void Postfix(AvatarMultiBodyController __instance, Transform _transform)
|
||
{
|
||
EntityAlive e = __instance.Entity;
|
||
if (_transform == null || !ThreadKnifeBody.Applies(e) || e.emodel.IsFPV && !e.isEntityRemote)
|
||
{
|
||
return;
|
||
}
|
||
AnimationGunjointOffsetData.AnimationGunjointOffsets o = AnimationGunjointOffsetData.AnimationGunjointOffset[ThreadKnifeBody.TpHold];
|
||
_transform.localPosition = o.position;
|
||
_transform.localRotation = Quaternion.Euler(o.rotation);
|
||
}
|
||
}
|
||
|
||
[HarmonyPatch(typeof(AvatarMultiBodyController), nameof(AvatarMultiBodyController.SwitchModelAndView))]
|
||
public static class Patch_AvatarMultiBodyController_SwitchModelAndView_ThreadKnife
|
||
{
|
||
public static void Postfix(AvatarMultiBodyController __instance, bool _bFPV)
|
||
{
|
||
Transform t = __instance.heldItemTransform;
|
||
if (t == null || _bFPV || !ThreadKnifeBody.Applies(__instance.Entity))
|
||
{
|
||
return;
|
||
}
|
||
AnimationGunjointOffsetData.AnimationGunjointOffsets o = AnimationGunjointOffsetData.AnimationGunjointOffset[ThreadKnifeBody.TpHold];
|
||
t.localPosition = o.position;
|
||
t.localEulerAngles = o.rotation;
|
||
}
|
||
}
|
||
}
|