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()); } }