Recover stale battle input locks
CI / test (push) Successful in 2m52s
Details
CI / test (push) Successful in 2m52s
Details
This commit is contained in:
parent
d6784b7ca9
commit
ba932c0d17
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Vanguard.Battle
|
namespace Vanguard.Battle
|
||||||
{
|
{
|
||||||
|
|
@ -14,6 +15,11 @@ namespace Vanguard.Battle
|
||||||
|
|
||||||
public bool IsLocked => _reasons.Count > 0;
|
public bool IsLocked => _reasons.Count > 0;
|
||||||
|
|
||||||
|
public string Describe() =>
|
||||||
|
_reasons.Count == 0
|
||||||
|
? "none"
|
||||||
|
: string.Join(",", _reasons.Select(pair => pair.Key + "=" + pair.Value));
|
||||||
|
|
||||||
public event Action<bool> Changed;
|
public event Action<bool> Changed;
|
||||||
|
|
||||||
public void Acquire(string reason)
|
public void Acquire(string reason)
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ namespace Vanguard.UI
|
||||||
private readonly AnimationConfig _animationConfig;
|
private readonly AnimationConfig _animationConfig;
|
||||||
private readonly InputLockService _inputLock;
|
private readonly InputLockService _inputLock;
|
||||||
private bool _feedbackLockHeld;
|
private bool _feedbackLockHeld;
|
||||||
|
private string _lastInputLockSummary;
|
||||||
private readonly Dictionary<string, CardCatalogItemDto> _catalog =
|
private readonly Dictionary<string, CardCatalogItemDto> _catalog =
|
||||||
new Dictionary<string, CardCatalogItemDto>(StringComparer.Ordinal);
|
new Dictionary<string, CardCatalogItemDto>(StringComparer.Ordinal);
|
||||||
private bool _catalogLoading;
|
private bool _catalogLoading;
|
||||||
|
|
@ -342,9 +343,47 @@ namespace Vanguard.UI
|
||||||
|
|
||||||
UpdateClock();
|
UpdateClock();
|
||||||
UpdateFeedback();
|
UpdateFeedback();
|
||||||
|
SanitizeInputLocks();
|
||||||
UpdateAutoPhases();
|
UpdateAutoPhases();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SanitizeInputLocks()
|
||||||
|
{
|
||||||
|
if (!_inputLock.IsLocked)
|
||||||
|
{
|
||||||
|
_lastInputLockSummary = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locks must never outlive the UI/gameplay state that owns them.
|
||||||
|
// This also recovers from a missed close callback after reconnects,
|
||||||
|
// overlays or card menus.
|
||||||
|
if (!_reconnecting)
|
||||||
|
_inputLock.Release("reconnect");
|
||||||
|
if (_overlays == null || !_overlays.IsOpen)
|
||||||
|
_inputLock.Release("overlay");
|
||||||
|
if (_cardMenu == null || !_cardMenu.IsVisible)
|
||||||
|
_inputLock.Release("card-menu");
|
||||||
|
if (!_feedbackPlaying && _feedbackQueue.Count == 0)
|
||||||
|
{
|
||||||
|
_feedbackLockHeld = false;
|
||||||
|
_inputLock.Release("feedback");
|
||||||
|
}
|
||||||
|
|
||||||
|
var summary = _inputLock.Describe();
|
||||||
|
if (!_inputLock.IsLocked)
|
||||||
|
{
|
||||||
|
_lastInputLockSummary = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.Equals(summary, _lastInputLockSummary, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
_lastInputLockSummary = summary;
|
||||||
|
Debug.LogWarning("Battle input lock held: " + summary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateAutoPhases()
|
private void UpdateAutoPhases()
|
||||||
{
|
{
|
||||||
if (_view == null || _sending || _reconnecting)
|
if (_view == null || _sending || _reconnecting)
|
||||||
|
|
|
||||||
|
|
@ -1005,6 +1005,9 @@ Battle; Boot и общие сервисы остаются персистент
|
||||||
завершается автоматически, кнопка «Закончить муллиган» скрывается и не
|
завершается автоматически, кнопка «Закончить муллиган» скрывается и не
|
||||||
блокируется feedback/overlay-локом; при реконнекте авто-завершение
|
блокируется feedback/overlay-локом; при реконнекте авто-завершение
|
||||||
взводится повторно из снапшота.
|
взводится повторно из снапшота.
|
||||||
|
- InputLock чистит зависшие причины, если owning-состояние уже неактивно
|
||||||
|
(нет reconnect/overlay/card-menu/feedback). Это убирает блокировку
|
||||||
|
«Сменить» после хода противника, когда сервер уже прислал ride-интенты.
|
||||||
- Trigger-зона противника зеркальна: над его колодой в сторону центра,
|
- Trigger-зона противника зеркальна: над его колодой в сторону центра,
|
||||||
а не за дальним краем.
|
а не за дальним краем.
|
||||||
- Карты на столе и в руке масштабируются общим `cardScale = 1.25`
|
- Карты на столе и в руке масштабируются общим `cardScale = 1.25`
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,8 @@
|
||||||
`AnimationConfig` (`Resources/AnimationConfig`). Guard/drive/intercept
|
`AnimationConfig` (`Resources/AnimationConfig`). Guard/drive/intercept
|
||||||
показывают имя карты и держатся 1.5–2 с. 3D-карты плавно снапятся,
|
показывают имя карты и держатся 1.5–2 с. 3D-карты плавно снапятся,
|
||||||
поворачиваются в Rest и переворачиваются; подсветка целей и точки броска
|
поворачиваются в Rest и переворачиваются; подсветка целей и точки броска
|
||||||
обновляются на каждом снимке.
|
обновляются на каждом снимке. Зависшие причины `InputLockService`
|
||||||
|
снимаются, когда reconnect/overlay/card-menu/feedback уже неактивны.
|
||||||
- Бой: динамическая плашка `POWER X vs SHIELD Y` (Power атакующего против
|
- Бой: динамическая плашка `POWER X vs SHIELD Y` (Power атакующего против
|
||||||
Power защищающегося + GuardShield), пересчитывается на каждом снимке, в
|
Power защищающегося + GuardShield), пересчитывается на каждом снимке, в
|
||||||
том числе после Guard. От атакующего к цели идёт затухающая 3D-стрелка,
|
том числе после Guard. От атакующего к цели идёт затухающая 3D-стрелка,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue