diff --git a/HarmonySrc/FpArms.cs b/HarmonySrc/FpArms.cs
index b339884..c30318d 100644
--- a/HarmonySrc/FpArms.cs
+++ b/HarmonySrc/FpArms.cs
@@ -33,7 +33,9 @@ namespace NecromancerTome
"zombieUtilityWorker", "zombieYo",
};
- public static readonly Color Tint = new Color(0.55f, 0.56f, 0.55f, 1f);
+ public static Color Tint = new Color(0.55f, 0.56f, 0.55f, 1f);
+ /// Параметры аниматора рук, выставленные necroarms set (подбор «пустых рук»), - ставятся и при пересборке.
+ static readonly Dictionary _animParams = new Dictionary();
/// Сдвиг рук от камеры (подбор necroarms pos). У персонажа игра опускает руки на 1.7 м: начало их
/// скелета - у ног (vp_FPWeapon PositionOffset 0,-1.7,0.02 для SDCS).
public static Vector3 Offset = new Vector3(0f, -1.7f, 0.02f);
@@ -127,7 +129,19 @@ namespace NecromancerTome
}
// Префаб рук выключен: у персонажа его включает BodyAnimator (State = Visible) - тест 13.10: «активны False».
_rig.SetActive(true);
- Debug.Log("[NecromancerTome] FpArms: снято скриптов " + stripped);
+ // Аниматор у персонажа включает тот же BodyAnimator (тест 13.10: «рука одна и без анимации»).
+ Animator anim = _rig.GetComponent();
+ if (anim != null)
+ {
+ anim.enabled = true;
+ anim.cullingMode = AnimatorCullingMode.AlwaysAnimate;
+ anim.Rebind();
+ foreach (KeyValuePair kv in _animParams)
+ {
+ SetParam(anim, kv.Key, kv.Value);
+ }
+ }
+ Debug.Log("[NecromancerTome] FpArms: снято скриптов " + stripped + ", аниматор " + (anim != null ? anim.runtimeAnimatorController?.name : "нет"));
_rig.name = "necroZombieArmsFP";
int layer = LayerMask.NameToLayer("HoldingItem");
int tinted = 0;
@@ -188,6 +202,122 @@ namespace NecromancerTome
return s;
}
+ static Animator Anim => _rig != null ? _rig.GetComponent() : null;
+
+ static bool SetParam(Animator _a, string _name, float _v)
+ {
+ foreach (AnimatorControllerParameter p in _a.parameters)
+ {
+ if (p.name != _name)
+ {
+ continue;
+ }
+ switch (p.type)
+ {
+ case AnimatorControllerParameterType.Float: _a.SetFloat(_name, _v); break;
+ case AnimatorControllerParameterType.Int: _a.SetInteger(_name, (int)_v); break;
+ case AnimatorControllerParameterType.Bool: _a.SetBool(_name, _v != 0f); break;
+ default: _a.SetTrigger(_name); break;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /// necroarms set: параметр аниматора (запоминается для пересборки; у триггера - только срабатывание).
+ public static string Set(string _name, float _v)
+ {
+ Animator a = Anim;
+ if (a == null)
+ {
+ return "рук нет";
+ }
+ if (!SetParam(a, _name, _v))
+ {
+ return "нет параметра " + _name;
+ }
+ _animParams[_name] = _v;
+ return _name + " = " + _v.ToString(CultureInfo.InvariantCulture);
+ }
+
+ public static string Trigger(string _name)
+ {
+ Animator a = Anim;
+ if (a == null)
+ {
+ return "рук нет";
+ }
+ a.SetTrigger(_name);
+ return "триггер " + _name;
+ }
+
+ /// Аниматор рук: вкл, контроллер, текущие клипы по слоям и все параметры со значениями.
+ public static string DumpAnim()
+ {
+ Animator a = Anim;
+ if (a == null)
+ {
+ return "рук нет";
+ }
+ string s = "аниматор вкл " + a.enabled + ", контроллер " + a.runtimeAnimatorController?.name + ", слоёв " + a.layerCount;
+ for (int l = 0; l < a.layerCount; l++)
+ {
+ string clips = "";
+ foreach (AnimatorClipInfo ci in a.GetCurrentAnimatorClipInfo(l))
+ {
+ clips += " " + ci.clip?.name + "(" + ci.weight.ToString("F2") + ")";
+ }
+ s += "\n слой " + l + " " + a.GetLayerName(l) + " вес " + a.GetLayerWeight(l).ToString("F2") + ":" + clips;
+ }
+ foreach (AnimatorControllerParameter p in a.parameters)
+ {
+ string v = p.type == AnimatorControllerParameterType.Float ? a.GetFloat(p.name).ToString("F2", CultureInfo.InvariantCulture)
+ : p.type == AnimatorControllerParameterType.Int ? a.GetInteger(p.name).ToString()
+ : p.type == AnimatorControllerParameterType.Bool ? a.GetBool(p.name).ToString() : "trigger";
+ s += "\n " + p.name + " = " + v;
+ }
+ return s;
+ }
+
+ /// Материалы рук: шейдер и его цветовые/текстурные свойства (подбор серого).
+ public static string DumpMat()
+ {
+ if (_rig == null)
+ {
+ return "рук нет";
+ }
+ string s = "";
+ foreach (SkinnedMeshRenderer smr in _rig.GetComponentsInChildren(true))
+ {
+ foreach (Material m in smr.sharedMaterials)
+ {
+ if (m == null)
+ {
+ continue;
+ }
+ s += "\n " + smr.name + " / " + m.name + " / " + m.shader.name + ":";
+ for (int i = 0; i < m.shader.GetPropertyCount(); i++)
+ {
+ UnityEngine.Rendering.ShaderPropertyType t = m.shader.GetPropertyType(i);
+ string n = m.shader.GetPropertyName(i);
+ if (t == UnityEngine.Rendering.ShaderPropertyType.Color)
+ {
+ s += " " + n + "=" + m.GetColor(n);
+ }
+ else if (t == UnityEngine.Rendering.ShaderPropertyType.Texture)
+ {
+ s += " " + n + "[" + (m.GetTexture(n) != null ? m.GetTexture(n).name : "-") + "]";
+ }
+ else
+ {
+ s += " " + n + "=" + m.GetFloat(n).ToString("F2", CultureInfo.InvariantCulture);
+ }
+ }
+ }
+ }
+ return s;
+ }
+
/// Пересобрать руки на месте (после necroarms pos/parent).
public static void Rebuild()
{
@@ -226,7 +356,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]";
+ public override string getDescription() => "NecromancerTome: руки одержимого от первого лица - [pos x y z | parent cam|ufps | anim | mat | set | trigger | tint r g b]";
void Out(string _s)
{
@@ -250,6 +380,34 @@ namespace NecromancerTome
FpArms.ParentCamera = _params[1] != "ufps";
FpArms.Rebuild();
}
+ else if (cmd == "anim")
+ {
+ Out(FpArms.DumpAnim());
+ return;
+ }
+ else if (cmd == "mat")
+ {
+ Out(FpArms.DumpMat());
+ return;
+ }
+ else if (cmd == "set" && _params.Count >= 3 && float.TryParse(_params[2].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out float v))
+ {
+ Out(FpArms.Set(_params[1], v));
+ return;
+ }
+ else if (cmd == "trigger" && _params.Count >= 2)
+ {
+ Out(FpArms.Trigger(_params[1]));
+ return;
+ }
+ else if (cmd == "tint" && _params.Count >= 4 &&
+ float.TryParse(_params[1].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out float r) &&
+ float.TryParse(_params[2].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out float g) &&
+ float.TryParse(_params[3].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out float b))
+ {
+ FpArms.Tint = new Color(r, g, b, 1f);
+ FpArms.Rebuild();
+ }
Out(FpArms.Dump());
}
}