diff --git a/HarmonySrc/CinerariumLoad.cs b/HarmonySrc/CinerariumLoad.cs
index acdccc4..88939dd 100644
--- a/HarmonySrc/CinerariumLoad.cs
+++ b/HarmonySrc/CinerariumLoad.cs
@@ -8,7 +8,7 @@ namespace NecromancerTome
///
/// Меры нагрузки урн (решение 17.10: «общий бюджет шагов работы духов за кадр (очередь по кругу - нагрузка постоянна при
/// любом числе урн); полная анимация только ближайших ~15-20 духов; замер 100 урн»).
- /// Бюджет: дел (пробный шаг + настоящий) за кадр - не больше StepsPerFrame на все урны сейва; урна, которой не досталось,
+ /// Бюджет: дел (пробный шаг + настоящий) в секунду - не больше StepsPerSecond на все урны сейва (23.10, было за кадр); урна, которой не досталось,
/// пробует в следующем кадре. Урна делает дело раз в CinerariumWork.StepSeconds, поэтому очередь сама идёт по кругу.
/// Анимация: двигаются (полёт, жесты, покачивание) только AnimateNearest ближайших к игроку духов, остальные замирают дома,
/// как уснувшие (CinerariumGenieMotion); работа у всех идёт.
@@ -17,11 +17,24 @@ namespace NecromancerTome
///
public static class CinerariumLoad
{
- public static int StepsPerFrame = 3;
+ /// Дел в секунду на все урны (23.10, было «3 за кадр»). Замер: урна получает тик раз в ~1.4 с, и тики всех урн
+ /// приходят пачкой в одном кадре, в порядке участков от ближних к игроку. Лимит «3 за кадр» доставался трём ближним урнам,
+ /// дальние не получали шага никогда («стою в 19 м - не работают, подхожу - работают»). Теперь бюджет копится между
+ /// тиками (не больше StepsPerSecond за раз), и пачка обслуживает всех, у кого подошёл срок. Консоль: necrourn budget N.
+ public static float StepsPerSecond = 30f;
+ /// Запас бюджета, секунд (пачка тиков раз в ~1.4 с должна уместиться целиком).
+ public const float BurstSeconds = 2f;
+ static float _tokens;
+ static float _lastRefill;
+ /// Очередь (23.10, пользователь: «нужно иметь очередь урн»): урна, которой отказали, ждёт; пока очередь не пуста,
+ /// бюджет сначала ей - новая урна получит шаг, только если после всех ждущих ещё останется. Иначе ближние урны (их участки
+ /// идут первыми) снова забирали бы всё. Запись старше QueueForget секунд выбрасывается (урну сняли или участок выгружен).
+ static readonly Dictionary _queue = new Dictionary();
+ const float QueueForget = 10f;
+ static readonly List _stale = new List();
public static int AnimateNearest = 16;
static int _frame = -1;
- static int _used;
static bool _tide;
static void NewFrame()
@@ -29,7 +42,23 @@ namespace NecromancerTome
if (Time.frameCount != _frame)
{
_frame = Time.frameCount;
- _used = 0;
+ // По реальному времени с прошлого пополнения: NewFrame зовут только кадры с тиками урн (раз в ~1.4 с), и
+ // Time.deltaTime (один кадр) давал полшага на пачку (замер 23.10: 279 шагов за 600 с при лимите 30/с).
+ float now = Time.time;
+ _tokens = Mathf.Min(StepsPerSecond * BurstSeconds, _tokens + StepsPerSecond * Mathf.Max(0f, now - _lastRefill));
+ _lastRefill = now;
+ _stale.Clear();
+ foreach (KeyValuePair kv in _queue)
+ {
+ if (now - kv.Value > QueueForget)
+ {
+ _stale.Add(kv.Key);
+ }
+ }
+ foreach (Vector3i p in _stale)
+ {
+ _queue.Remove(p);
+ }
_tide = SnakeMode.IsBloodTide();
}
}
@@ -38,12 +67,19 @@ namespace NecromancerTome
public static bool TryTakeStep(Vector3i _who)
{
NewFrame();
- if (_used >= StepsPerFrame)
+ bool waiting = _queue.ContainsKey(_who);
+ // Ждущая урна - если есть хоть один шаг; новая - только если хватит и всем ждущим.
+ if (_tokens < 1f || (!waiting && _tokens < 1f + _queue.Count))
{
+ if (!waiting)
+ {
+ _queue[_who] = Time.time;
+ }
Count(_denied, _who);
return false;
}
- _used++;
+ _tokens -= 1f;
+ _queue.Remove(_who);
Count(_granted, _who);
return true;
}
@@ -65,7 +101,7 @@ namespace NecromancerTome
{
float sec = Time.time - _countSince;
int frames = Time.frameCount - _frames0;
- string s = "за " + sec.ToString("0") + " с, кадров " + frames + ", лимит " + StepsPerFrame + "/кадр; дано:";
+ string s = "за " + sec.ToString("0") + " с, кадров " + frames + ", лимит " + StepsPerSecond + "/с, в очереди " + _queue.Count + "; дано:";
foreach (KeyValuePair kv in _granted)
{
s += " " + kv.Key + "=" + kv.Value;
diff --git a/HarmonySrc/NecroDevCommands.cs b/HarmonySrc/NecroDevCommands.cs
index 12464ca..fef6415 100644
--- a/HarmonySrc/NecroDevCommands.cs
+++ b/HarmonySrc/NecroDevCommands.cs
@@ -461,10 +461,10 @@ namespace NecromancerTome
{
if (_p.Count > 0 && _p[0] == "budget")
{
- // necrourn budget [N] - кому из урн дали шаг и кому отказали с прошлого вызова; N - лимит дел на кадр (по умолчанию 3)
+ // necrourn budget [N] - кому из урн дали шаг и кому отказали с прошлого вызова; N - дел в секунду на все урны (30)
if (_p.Count > 1)
{
- CinerariumLoad.StepsPerFrame = int.Parse(_p[1]);
+ CinerariumLoad.StepsPerSecond = float.Parse(_p[1], System.Globalization.CultureInfo.InvariantCulture);
}
Out("necrourn budget: " + CinerariumLoad.Budget());
return;
diff --git a/NecromancerHarmony.dll b/NecromancerHarmony.dll
index 19f4032..3da598c 100644
Binary files a/NecromancerHarmony.dll and b/NecromancerHarmony.dll differ
diff --git a/NecromancerHarmony.pdb b/NecromancerHarmony.pdb
index 6b8a1d3..11730ac 100644
Binary files a/NecromancerHarmony.pdb and b/NecromancerHarmony.pdb differ