diff --git a/HarmonySrc/FpArms.cs b/HarmonySrc/FpArms.cs
index dd99db7..a846500 100644
--- a/HarmonySrc/FpArms.cs
+++ b/HarmonySrc/FpArms.cs
@@ -1,4 +1,7 @@
+using System.Collections.Generic;
+using System.Globalization;
using UnityEngine;
+using UnityEngine.Scripting;
namespace NecromancerTome
{
@@ -31,6 +34,11 @@ namespace NecromancerTome
};
public static readonly Color Tint = new Color(0.55f, 0.56f, 0.55f, 1f);
+ /// Сдвиг рук от камеры (подбор necroarms pos). У персонажа игра опускает руки на 1.7 м: начало их
+ /// скелета - у ног (vp_FPWeapon PositionOffset 0,-1.7,0.02 для SDCS).
+ public static Vector3 Offset = new Vector3(0f, -1.7f, 0.02f);
+ /// Родитель: true - сама камера, false - её UFPSRoot (как у персонажа).
+ public static bool ParentCamera = true;
static GameObject _rig;
static SDCSUtils.TransformCatalog _bones;
@@ -38,6 +46,18 @@ namespace NecromancerTome
public static bool Active => _rig != null;
+ /// Пол зомби. IsMale у зомби в entityclasses.xml не задан (все «мужчины» по умолчанию, тест 13.10:
+ /// Арлин получила мужские руки) - женщину выдают звуки zombiefemale*.
+ public static bool IsFemale(EntityAlive _z)
+ {
+ if (_z == null || !EntityClass.list.TryGetValue(_z.entityClass, out EntityClass cls) || cls?.Properties == null)
+ {
+ return false;
+ }
+ string s = cls.Properties.GetString("SoundAlert");
+ return s != null && s.ToLowerInvariant().Contains("female");
+ }
+
public static bool Allows(EntityAlive _z)
{
if (_z == null || !EntityClass.list.TryGetValue(_z.entityClass, out EntityClass cls) || cls?.entityClassName == null)
@@ -65,14 +85,14 @@ namespace NecromancerTome
{
return;
}
- Archetype arch = Archetype.GetArchetype(_z.IsMale ? "BaseMale" : "BaseFemale");
+ Archetype arch = Archetype.GetArchetype(IsFemale(_z) ? "BaseFemale" : "BaseMale");
Transform cam = _player.cameraTransform;
if (arch == null || cam == null)
{
Debug.LogWarning("[NecromancerTome] FpArms: нет архетипа или камеры - рук не будет");
return;
}
- Transform parent = cam.FindInChildren("UFPSRoot") ?? cam;
+ Transform parent = ParentCamera ? cam : cam.FindInChildren("UFPSRoot") ?? cam;
Archetype wasArch = SDCSUtils.tmpArchetype;
SDCSUtils.AssetRefs wasOwner = SDCSUtils.tmpAssetsOwner;
try
@@ -87,7 +107,7 @@ namespace NecromancerTome
SDCSUtils.tmpArchetype = wasArch;
SDCSUtils.tmpAssetsOwner = wasOwner;
}
- _rig.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
+ _rig.transform.SetLocalPositionAndRotation(Offset, Quaternion.identity);
_rig.name = "necroZombieArmsFP";
int layer = LayerMask.NameToLayer("HoldingItem");
int tinted = 0;
@@ -123,6 +143,43 @@ namespace NecromancerTome
", родитель " + parent.name);
}
+ /// Состояние рук для necroarms: где они относительно камеры, видны ли, кто рисует их слой.
+ public static string Dump()
+ {
+ if (_rig == null)
+ {
+ return "рук нет (вид от третьего лица или зомби не из списка)";
+ }
+ Camera main = Camera.main;
+ Transform par = _rig.transform.parent;
+ string s = "активны " + _rig.activeInHierarchy + ", родитель " + (par != null ? par.name + (par.gameObject.activeInHierarchy ? "" : " (ВЫКЛЮЧЕН)") : "нет") +
+ ", сдвиг " + _rig.transform.localPosition.ToString("F2");
+ foreach (SkinnedMeshRenderer smr in _rig.GetComponentsInChildren(true))
+ {
+ Vector3 c = main != null ? main.transform.InverseTransformPoint(smr.bounds.center) : smr.bounds.center;
+ s += "\n " + smr.name + ": вкл " + smr.enabled + "/" + smr.gameObject.activeInHierarchy + ", виден " + smr.isVisible +
+ ", слой " + LayerMask.LayerToName(smr.gameObject.layer) + ", центр от камеры " + c.ToString("F2") + ", размер " + smr.bounds.size.ToString("F2");
+ }
+ foreach (Camera c in Camera.allCameras)
+ {
+ s += "\n камера " + c.name + (c == main ? " (main)" : "") + ": слой HoldingItem " +
+ ((c.cullingMask & (1 << LayerMask.NameToLayer("HoldingItem"))) != 0 ? "рисует" : "нет") + ", near " + c.nearClipPlane.ToString("F3");
+ }
+ return s;
+ }
+
+ /// Пересобрать руки на месте (после necroarms pos/parent).
+ public static void Rebuild()
+ {
+ if (_rig == null || !Possession.Active || Possession.Zombie == null)
+ {
+ return;
+ }
+ EntityPlayerLocal p = Possession.Player;
+ Hide(p);
+ Show(p, Possession.Zombie);
+ }
+
public static void Hide(EntityPlayerLocal _player)
{
if (_rig == null)
@@ -139,4 +196,41 @@ namespace NecromancerTome
Debug.Log("[NecromancerTome] FpArms: руки убраны");
}
}
+
+
+ ///
+ /// necroarms - руки одержимого от первого лица (13.10): без параметров - состояние; pos x y z - сдвиг от камеры;
+ /// parent cam|ufps - к чему крепить. Сдвиг и родитель применяются сразу (руки пересобираются).
+ ///
+ [Preserve]
+ public class ConsoleCmdNecroArms : ConsoleCmdAbstract
+ {
+ public override string[] getCommands() => new[] { "necroarms" };
+ public override string getDescription() => "NecromancerTome: руки одержимого от первого лица - [pos x y z | parent cam|ufps]";
+
+ void Out(string _s)
+ {
+ SdtdConsole.Instance.Output(_s);
+ Debug.Log("[NecromancerTome] necroarms: " + _s);
+ }
+
+ public override void Execute(List _params, CommandSenderInfo _senderInfo)
+ {
+ string cmd = _params.Count > 0 ? _params[0] : "";
+ if (cmd == "pos" && _params.Count >= 4 &&
+ float.TryParse(_params[1].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out float x) &&
+ float.TryParse(_params[2].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out float y) &&
+ float.TryParse(_params[3].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out float z))
+ {
+ FpArms.Offset = new Vector3(x, y, z);
+ FpArms.Rebuild();
+ }
+ else if (cmd == "parent" && _params.Count >= 2)
+ {
+ FpArms.ParentCamera = _params[1] != "ufps";
+ FpArms.Rebuild();
+ }
+ Out(FpArms.Dump());
+ }
+ }
}