Руки одержимого: пол по звукам zombiefemale, сдвиг -1.7 м под камеру, команда necroarms (состояние, pos, parent)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-10-13 15:41:26 +03:00
co-authored by Claude Opus 5.5
parent f5f4d92cef
commit a27caefc34
+97 -3
View File
@@ -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);
/// <summary>Сдвиг рук от камеры (подбор necroarms pos). У персонажа игра опускает руки на 1.7 м: начало их
/// скелета - у ног (vp_FPWeapon PositionOffset 0,-1.7,0.02 для SDCS).</summary>
public static Vector3 Offset = new Vector3(0f, -1.7f, 0.02f);
/// <summary>Родитель: true - сама камера, false - её UFPSRoot (как у персонажа).</summary>
public static bool ParentCamera = true;
static GameObject _rig;
static SDCSUtils.TransformCatalog _bones;
@@ -38,6 +46,18 @@ namespace NecromancerTome
public static bool Active => _rig != null;
/// <summary>Пол зомби. IsMale у зомби в entityclasses.xml не задан (все «мужчины» по умолчанию, тест 13.10:
/// Арлин получила мужские руки) - женщину выдают звуки zombiefemale*.</summary>
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);
}
/// <summary>Состояние рук для necroarms: где они относительно камеры, видны ли, кто рисует их слой.</summary>
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<SkinnedMeshRenderer>(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;
}
/// <summary>Пересобрать руки на месте (после necroarms pos/parent).</summary>
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: руки убраны");
}
}
/// <summary>
/// necroarms - руки одержимого от первого лица (13.10): без параметров - состояние; pos x y z - сдвиг от камеры;
/// parent cam|ufps - к чему крепить. Сдвиг и родитель применяются сразу (руки пересобираются).
/// </summary>
[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<string> _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());
}
}
}