necrobody zdance/mimic: зомби танцует, тело повторяет его кости
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e0411ce3e5
commit
a17f2ff375
+139
-1
@@ -88,6 +88,112 @@ namespace NecromancerTome
|
|||||||
return "играет " + clip.name + " (" + NecroBody.F(clip.length) + " с" + (clip.isLooping ? ", петля" : "") + (clip.humanMotion ? ", гуманоид" : ", не гуманоид - может лечь криво") + "), скорость " + NecroBody.F(_speed);
|
return "играет " + clip.name + " (" + NecroBody.F(clip.length) + " с" + (clip.isLooping ? ", петля" : "") + (clip.humanMotion ? ", гуманоид" : ", не гуманоид - может лечь криво") + "), скорость " + NecroBody.F(_speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Тело повторяет движения другого существа (16.10: танец зомби не лёг на тело игрока через Playables - клип
|
||||||
|
/// гуманоидный, а тело игрока - нет). Каждый кадр локальные повороты костей источника копируются на кости тела с теми же
|
||||||
|
/// именами (Hips, Spine, LeftArm...); у бёдер - ещё и сдвиг. Источник - зомби, которого necrobody zdance заставляет танцевать.</summary>
|
||||||
|
static EntityAlive _mimic;
|
||||||
|
static readonly List<Transform> _mimicSrc = new List<Transform>();
|
||||||
|
static readonly List<Transform> _mimicDst = new List<Transform>();
|
||||||
|
|
||||||
|
public static EntityAlive Nearest(float _radius, bool _zombiesOnly)
|
||||||
|
{
|
||||||
|
EntityPlayerLocal pl = Player();
|
||||||
|
if (pl == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
EntityAlive best = null;
|
||||||
|
float bestD = _radius * _radius;
|
||||||
|
foreach (Entity e in GameManager.Instance.World.Entities.list)
|
||||||
|
{
|
||||||
|
if (e is EntityAlive a && a != pl && !a.IsDead() && (!_zombiesOnly || a is EntityZombie))
|
||||||
|
{
|
||||||
|
float d = (a.position - pl.position).sqrMagnitude;
|
||||||
|
if (d < bestD)
|
||||||
|
{
|
||||||
|
bestD = d;
|
||||||
|
best = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Transform ModelRoot(EntityAlive _e)
|
||||||
|
{
|
||||||
|
if (_e == null || _e.emodel == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (_e.emodel.avatarController is AvatarCharacterController ac && ac.CharacterBody?.Animator != null)
|
||||||
|
{
|
||||||
|
return ac.CharacterBody.Animator.transform;
|
||||||
|
}
|
||||||
|
return _e.emodel.GetModelTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Mimic(EntityAlive _src)
|
||||||
|
{
|
||||||
|
StopMimic();
|
||||||
|
Animator a = Anim();
|
||||||
|
Transform src = ModelRoot(_src);
|
||||||
|
if (a == null || src == null)
|
||||||
|
{
|
||||||
|
return "нет тела игрока или источника";
|
||||||
|
}
|
||||||
|
var byName = new Dictionary<string, Transform>();
|
||||||
|
foreach (Transform t in src.GetComponentsInChildren<Transform>(true))
|
||||||
|
{
|
||||||
|
if (!byName.ContainsKey(t.name))
|
||||||
|
{
|
||||||
|
byName[t.name] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (Transform t in a.GetComponentsInChildren<Transform>(true))
|
||||||
|
{
|
||||||
|
if (t != a.transform && byName.TryGetValue(t.name, out Transform s2) && s2 != src)
|
||||||
|
{
|
||||||
|
_mimicDst.Add(t);
|
||||||
|
_mimicSrc.Add(s2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_mimic = _src;
|
||||||
|
return "тело повторяет за " + _src.EntityName + " (id " + _src.entityId + "): общих костей " + _mimicDst.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void StopMimic()
|
||||||
|
{
|
||||||
|
_mimic = null;
|
||||||
|
_mimicSrc.Clear();
|
||||||
|
_mimicDst.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ApplyMimic()
|
||||||
|
{
|
||||||
|
if (_mimic == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_mimic.IsDead() || _mimicSrc.Count == 0 || _mimicSrc[0] == null)
|
||||||
|
{
|
||||||
|
StopMimic();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < _mimicDst.Count; i++)
|
||||||
|
{
|
||||||
|
Transform s = _mimicSrc[i], d = _mimicDst[i];
|
||||||
|
if (s == null || d == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
d.localRotation = s.localRotation;
|
||||||
|
if (d.name == "Hips")
|
||||||
|
{
|
||||||
|
d.localPosition = s.localPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void StopPlay()
|
public static void StopPlay()
|
||||||
{
|
{
|
||||||
if (_graph.IsValid())
|
if (_graph.IsValid())
|
||||||
@@ -148,6 +254,7 @@ namespace NecromancerTome
|
|||||||
public static void Reset()
|
public static void Reset()
|
||||||
{
|
{
|
||||||
StopPlay();
|
StopPlay();
|
||||||
|
StopMimic();
|
||||||
Params.Clear();
|
Params.Clear();
|
||||||
Pose.Clear();
|
Pose.Clear();
|
||||||
Freeze = false;
|
Freeze = false;
|
||||||
@@ -226,6 +333,7 @@ namespace NecromancerTome
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LoopPlay();
|
LoopPlay();
|
||||||
|
ApplyMimic();
|
||||||
if (!Active || Bone("") == null && _anim == null)
|
if (!Active || Bone("") == null && _anim == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -317,7 +425,7 @@ namespace NecromancerTome
|
|||||||
public class ConsoleCmdNecroBody : ConsoleCmdAbstract
|
public class ConsoleCmdNecroBody : ConsoleCmdAbstract
|
||||||
{
|
{
|
||||||
public override string[] getCommands() => new[] { "necrobody" };
|
public override string[] getCommands() => new[] { "necrobody" };
|
||||||
public override string getDescription() => "NecromancerTome: аниматор тела - params [ф]|clips|set п v|unset п|trigger п|speed k|freeze [off]|bones [ф]|show к|pose к x y z|add к dx dy dz|unpose к|item pos|rot|off|show|holdoffset [N]|anims [слово]|play имя [скорость]|play off|save|off";
|
public override string getDescription() => "NecromancerTome: аниматор тела - params [ф]|clips|set п v|unset п|trigger п|speed k|freeze [off]|bones [ф]|show к|pose к x y z|add к dx dy dz|unpose к|item pos|rot|off|show|holdoffset [N]|anims [слово]|play имя [скорость]|play off|zdance [off]|mimic [id]|mimic off|save|off";
|
||||||
|
|
||||||
static bool Num(List<string> _p, int _i, out float _v)
|
static bool Num(List<string> _p, int _i, out float _v)
|
||||||
{
|
{
|
||||||
@@ -524,6 +632,36 @@ namespace NecromancerTome
|
|||||||
Out(sb.ToString());
|
Out(sb.ToString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case "zdance":
|
||||||
|
{
|
||||||
|
EntityAlive z = NecroBody.Nearest(30f, true);
|
||||||
|
if (z == null)
|
||||||
|
{
|
||||||
|
Out("рядом (30 м) нет зомби");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
z.SetDancing(arg != "off");
|
||||||
|
Out(z.EntityName + " (id " + z.entityId + ") " + (arg != "off" ? "танцует" : "перестал танцевать"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "mimic" when arg == "off":
|
||||||
|
NecroBody.StopMimic();
|
||||||
|
Out("тело больше не повторяет");
|
||||||
|
return;
|
||||||
|
case "mimic":
|
||||||
|
{
|
||||||
|
EntityAlive src = null;
|
||||||
|
if (Num(_params, 1, out float mid))
|
||||||
|
{
|
||||||
|
src = GameManager.Instance.World.GetEntity((int)mid) as EntityAlive;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
src = NecroBody.Nearest(30f, false);
|
||||||
|
}
|
||||||
|
Out(src == null ? "нет источника (id или существо в 30 м)" : NecroBody.Mimic(src));
|
||||||
|
return;
|
||||||
|
}
|
||||||
case "play" when arg == "off":
|
case "play" when arg == "off":
|
||||||
NecroBody.StopPlay();
|
NecroBody.StopPlay();
|
||||||
Out("анимация снята");
|
Out("анимация снята");
|
||||||
|
|||||||
Reference in New Issue
Block a user