Урна: дух летит к делу лицом, над местом жестикулирует руками, дело - в середине жеста, назад лицом к урне
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
23587d78e0
commit
7e93e19c5f
@@ -153,29 +153,52 @@ namespace NecromancerTome
|
||||
|
||||
float _nextWork;
|
||||
public bool Stuck;
|
||||
/// <summary>Дело найдено пробным шагом, дух полетел; выполнить в _actAt (16.10: «делал действие не сразу»).</summary>
|
||||
bool _pending;
|
||||
float _actAt;
|
||||
|
||||
/// <summary>Работа духа (CinerariumWork): раз в StepSeconds одно дело, только с черепом и только на сервере.</summary>
|
||||
/// <summary>Работа духа (CinerariumWork): раз в StepSeconds одно дело, только с черепом и только на сервере.
|
||||
/// Сначала пробный шаг (ничего не меняет) - куда лететь; само дело - когда дух долетел и машет руками. Если за это
|
||||
/// время что-то поменялось, настоящий шаг сделает то, что нужно на тот момент.</summary>
|
||||
void Work(World _world)
|
||||
{
|
||||
if (!HasSkull || _world == null || !ConnectionManager.Instance.IsServer || Time.time < _nextWork)
|
||||
if (!HasSkull || _world == null || !ConnectionManager.Instance.IsServer)
|
||||
{
|
||||
_pending = false;
|
||||
return;
|
||||
}
|
||||
CinerariumGenieMotion motion = _genie != null ? _genie.GetComponent<CinerariumGenieMotion>() : null;
|
||||
if (_pending)
|
||||
{
|
||||
if (Time.time < _actAt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_pending = false;
|
||||
CinerariumWork.Step(_world, this, out bool _, false);
|
||||
_nextWork = Time.time + CinerariumWork.StepSeconds;
|
||||
return;
|
||||
}
|
||||
if (Time.time < _nextWork)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_nextWork = Time.time + CinerariumWork.StepSeconds;
|
||||
Vector3i? at = CinerariumWork.Step(_world, this, out bool stuck);
|
||||
Vector3i? at = CinerariumWork.Step(_world, this, out bool stuck, true);
|
||||
if (stuck != Stuck)
|
||||
{
|
||||
Stuck = stuck;
|
||||
Debug.Log("[NecromancerTome] Cinerarium: дух урны " + ToWorldPos() + (stuck ? " почернел - готовое класть некуда" : " ожил"));
|
||||
}
|
||||
CinerariumGenieMotion motion = _genie != null ? _genie.GetComponent<CinerariumGenieMotion>() : null;
|
||||
if (motion != null)
|
||||
{
|
||||
motion.SetStuck(Stuck && at == null);
|
||||
if (at.HasValue)
|
||||
{
|
||||
motion.Visit(World.blockToTransformPos(at.Value) - Origin.position);
|
||||
}
|
||||
}
|
||||
if (at.HasValue)
|
||||
{
|
||||
_pending = true;
|
||||
_actAt = Time.time + (motion != null ? CinerariumGenieMotion.ActDelay : 0f);
|
||||
motion?.Visit(World.blockToTransformPos(at.Value) - Origin.position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -226,10 +226,20 @@ namespace NecromancerTome
|
||||
{
|
||||
float _yaw;
|
||||
float _phase;
|
||||
/// <summary>Полёт к месту работы и обратно: точка в координатах модели урны и сколько осталось.</summary>
|
||||
/// <summary>Полёт к месту работы и обратно (16.10: «возвращается задом», «делал действие не сразу, а хоть руками
|
||||
/// пошевелив»): туда - лицом к месту, над местом - жест руками (дело делается в его середине, см. ActDelay),
|
||||
/// обратно - развернувшись лицом к урне. Точка - в координатах модели урны; _t - время с начала полёта, -1 - дома.</summary>
|
||||
Vector3 _visit;
|
||||
float _visitLeft;
|
||||
public const float VisitSeconds = 2.2f;
|
||||
float _t = -1f;
|
||||
public static float OutSeconds = 1.3f;
|
||||
public static float WorkSeconds = 1.6f;
|
||||
public static float BackSeconds = 1.3f;
|
||||
/// <summary>Через сколько после начала полёта дело выполняется - в середине жеста.</summary>
|
||||
public static float ActDelay => OutSeconds + WorkSeconds * 0.5f;
|
||||
/// <summary>Жест: плечи вперёд до GestureFwd градусов, локти ходят на ±GestureWave вразнобой.</summary>
|
||||
public static float GestureFwd = 55f;
|
||||
public static float GestureWave = 30f;
|
||||
bool _gesturing;
|
||||
/// <summary>Почернел и замер (готовое класть некуда, решение 06.10).</summary>
|
||||
bool _stuck;
|
||||
/// <summary>Кости рук (плечо, предплечье, кисть по сторонам) и их поворот в позе покоя.</summary>
|
||||
@@ -240,6 +250,11 @@ namespace NecromancerTome
|
||||
/// <summary>Опустить руки из позы покоя: направление плеча и предплечья задаётся в осях модели (вперёд - +Z),
|
||||
/// поворот - от текущего направления кости к нужному, так что оси самих костей значения не имеют.</summary>
|
||||
public void Pose()
|
||||
{
|
||||
Pose(CinerariumGenie.ArmFwd, CinerariumGenie.Elbow, CinerariumGenie.Elbow);
|
||||
}
|
||||
|
||||
void Pose(float _fwd, float _elbowL, float _elbowR)
|
||||
{
|
||||
if (_arm == null)
|
||||
{
|
||||
@@ -283,9 +298,9 @@ namespace NecromancerTome
|
||||
continue;
|
||||
}
|
||||
float sign = Mathf.Sign(transform.InverseTransformPoint(up.position).x);
|
||||
float o = CinerariumGenie.ArmOut * Mathf.Deg2Rad, f = CinerariumGenie.ArmFwd * Mathf.Deg2Rad;
|
||||
float o = CinerariumGenie.ArmOut * Mathf.Deg2Rad, f = _fwd * Mathf.Deg2Rad;
|
||||
Vector3 upDir = new Vector3(sign * Mathf.Sin(o), -Mathf.Cos(o) * Mathf.Cos(f), Mathf.Sin(f)).normalized;
|
||||
Vector3 foreDir = Vector3.RotateTowards(upDir, Vector3.forward, CinerariumGenie.Elbow * Mathf.Deg2Rad, 0f);
|
||||
Vector3 foreDir = Vector3.RotateTowards(upDir, Vector3.forward, (side == 0 ? _elbowL : _elbowR) * Mathf.Deg2Rad, 0f);
|
||||
up.rotation = Quaternion.FromToRotation(fore.position - up.position, transform.TransformDirection(upDir)) * up.rotation;
|
||||
fore.rotation = Quaternion.FromToRotation(hand.position - fore.position, transform.TransformDirection(foreDir)) * fore.rotation;
|
||||
}
|
||||
@@ -302,7 +317,7 @@ namespace NecromancerTome
|
||||
return;
|
||||
}
|
||||
_visit = transform.parent.InverseTransformPoint(_worldUnity);
|
||||
_visitLeft = VisitSeconds;
|
||||
_t = 0f;
|
||||
}
|
||||
|
||||
public void SetStuck(bool _on)
|
||||
@@ -359,21 +374,54 @@ namespace NecromancerTome
|
||||
}
|
||||
float yawTarget = _yaw + CinerariumGenie.TurnSpeed * Time.deltaTime;
|
||||
Vector3 home = new Vector3(0f, CinerariumGenie.OffsetY, 0f) + Quaternion.Euler(0f, _yaw, 0f) * new Vector3(0f, 0f, CinerariumGenie.Forward);
|
||||
if (_visitLeft > 0f)
|
||||
if (_t >= 0f)
|
||||
{
|
||||
// Туда и обратно по синусу: на полпути - над местом работы, лицом к нему.
|
||||
_visitLeft -= Time.deltaTime;
|
||||
float k = Mathf.Sin(Mathf.Clamp01(1f - _visitLeft / VisitSeconds) * Mathf.PI);
|
||||
Vector3 over = _visit + new Vector3(0f, CinerariumGenie.OffsetY + 0.6f, 0f) + Quaternion.Euler(0f, _yaw, 0f) * new Vector3(0f, 0f, CinerariumGenie.Forward);
|
||||
transform.localPosition = Vector3.Lerp(home, over, k);
|
||||
Vector3 d = _visit - home;
|
||||
if (d.x * d.x + d.z * d.z > 0.01f)
|
||||
_t += Time.deltaTime;
|
||||
if (_t < OutSeconds + WorkSeconds + BackSeconds)
|
||||
{
|
||||
yawTarget = Mathf.LerpAngle(_yaw, Mathf.Atan2(d.x, d.z) * Mathf.Rad2Deg, Time.deltaTime * 4f);
|
||||
Vector3 over = _visit + new Vector3(0f, CinerariumGenie.OffsetY + 0.6f, 0f) + Quaternion.Euler(0f, _yaw, 0f) * new Vector3(0f, 0f, CinerariumGenie.Forward);
|
||||
Vector3 d = _visit - new Vector3(0f, CinerariumGenie.OffsetY, 0f);
|
||||
bool far = d.x * d.x + d.z * d.z > 0.01f;
|
||||
float toPlace = far ? Mathf.Atan2(d.x, d.z) * Mathf.Rad2Deg : _yaw;
|
||||
float k, face, w = 0f;
|
||||
if (_t < OutSeconds)
|
||||
{
|
||||
k = Mathf.SmoothStep(0f, 1f, _t / OutSeconds);
|
||||
face = toPlace;
|
||||
}
|
||||
else if (_t < OutSeconds + WorkSeconds)
|
||||
{
|
||||
k = 1f;
|
||||
face = toPlace;
|
||||
w = Mathf.Sin((_t - OutSeconds) / WorkSeconds * Mathf.PI);
|
||||
}
|
||||
else
|
||||
{
|
||||
k = 1f - Mathf.SmoothStep(0f, 1f, (_t - OutSeconds - WorkSeconds) / BackSeconds);
|
||||
face = far ? toPlace + 180f : _yaw; // назад - лицом к урне
|
||||
}
|
||||
_yaw = Mathf.MoveTowardsAngle(_yaw, face, 300f * Time.deltaTime) % 360f;
|
||||
transform.localPosition = Vector3.Lerp(home, over, k);
|
||||
transform.localRotation = Quaternion.Euler(0f, _yaw, 0f);
|
||||
if (w > 0f)
|
||||
{
|
||||
float wave = Mathf.Sin(_t * Mathf.PI * 2f * 1.5f) * GestureWave * w;
|
||||
Pose(Mathf.Lerp(CinerariumGenie.ArmFwd, GestureFwd, w), CinerariumGenie.Elbow + 15f * w + wave, CinerariumGenie.Elbow + 15f * w - wave);
|
||||
_gesturing = true;
|
||||
}
|
||||
else if (_gesturing)
|
||||
{
|
||||
Pose();
|
||||
_gesturing = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
_t = -1f;
|
||||
if (_gesturing)
|
||||
{
|
||||
Pose();
|
||||
_gesturing = false;
|
||||
}
|
||||
_yaw = yawTarget % 360f;
|
||||
transform.localRotation = Quaternion.Euler(0f, _yaw, 0f);
|
||||
return;
|
||||
}
|
||||
_yaw = yawTarget % 360f;
|
||||
float bob = CinerariumGenie.BobPeriod > 0.01f
|
||||
|
||||
@@ -41,8 +41,25 @@ namespace NecromancerTome
|
||||
|
||||
static readonly HashSet<TileEntity> _seen = new HashSet<TileEntity>();
|
||||
|
||||
/// <summary>Одно дело. Возвращает, где дух работал (для полёта модели), или null; _stuck - есть что забрать, но некуда.</summary>
|
||||
public static Vector3i? Step(World _world, TEFeatureCinerarium _urn, out bool _stuck)
|
||||
/// <summary>Пробный шаг: найти дело и место, ничего не меняя.</summary>
|
||||
static bool _dry;
|
||||
|
||||
/// <summary>Одно дело. Возвращает, где дух работал (для полёта модели), или null; _stuck - есть что забрать, но некуда.
|
||||
/// _dryRun - только узнать, где будет дело (дух летит туда заранее), ничего не трогая.</summary>
|
||||
public static Vector3i? Step(World _world, TEFeatureCinerarium _urn, out bool _stuck, bool _dryRun)
|
||||
{
|
||||
_dry = _dryRun;
|
||||
try
|
||||
{
|
||||
return DoStep(_world, _urn, out _stuck);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_dry = false;
|
||||
}
|
||||
}
|
||||
|
||||
static Vector3i? DoStep(World _world, TEFeatureCinerarium _urn, out bool _stuck)
|
||||
{
|
||||
_stuck = false;
|
||||
Zone z = Scan(_world, _urn);
|
||||
@@ -51,7 +68,10 @@ namespace NecromancerTome
|
||||
{
|
||||
if (Refill(c, c.FuelSlots, c.GetFuelTypes(), false, z) || Refill(c, c.CatalystSlots, c.GetCatalystTypes(), true, z))
|
||||
{
|
||||
c.SetModified();
|
||||
if (!_dry)
|
||||
{
|
||||
c.SetModified();
|
||||
}
|
||||
return z.Where[c];
|
||||
}
|
||||
}
|
||||
@@ -62,8 +82,11 @@ namespace NecromancerTome
|
||||
ItemStack[] items = c.Items;
|
||||
if (Unload(items, z, ref blocked))
|
||||
{
|
||||
c.Items = items;
|
||||
c.SetModified();
|
||||
if (!_dry)
|
||||
{
|
||||
c.Items = items;
|
||||
c.SetModified();
|
||||
}
|
||||
return z.Where[c];
|
||||
}
|
||||
}
|
||||
@@ -72,7 +95,10 @@ namespace NecromancerTome
|
||||
ItemStack[] output = w.Output;
|
||||
if (Unload(output, z, ref blocked))
|
||||
{
|
||||
w.Output = output;
|
||||
if (!_dry)
|
||||
{
|
||||
w.Output = output;
|
||||
}
|
||||
return z.Where[w];
|
||||
}
|
||||
}
|
||||
@@ -151,6 +177,10 @@ namespace NecromancerTome
|
||||
_blocked = true;
|
||||
return false;
|
||||
}
|
||||
if (_dry)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
_world.SetBlockRPC(_p, BlockValue.Air);
|
||||
if (!replant.isair)
|
||||
{
|
||||
@@ -232,6 +262,10 @@ namespace NecromancerTome
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_dry)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
Take(pick.src, pick.slot, 1);
|
||||
_world.SetBlockRPC(p, bv);
|
||||
Debug.Log("[NecromancerTome] Cinerarium: дух посадил " + bv.Block.GetBlockName() + " в " + p);
|
||||
@@ -351,6 +385,10 @@ namespace NecromancerTome
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_dry)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (want == null)
|
||||
{
|
||||
_slots[i] = new ItemStack(s.itemValue.Clone(), take);
|
||||
@@ -382,6 +420,15 @@ namespace NecromancerTome
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_dry)
|
||||
{
|
||||
if (HasRoom(s, _z))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
_blocked = true;
|
||||
continue;
|
||||
}
|
||||
int before = s.count;
|
||||
// Сначала ящики зоны (к таким же, потом в пустое), они полны - тогда сумка урны (решение 06.10).
|
||||
Place(s, _z.Boxes);
|
||||
|
||||
Reference in New Issue
Block a user