acdream/src/AcDream.Core/Chat/ChatCommandTargetState.cs
Erik 89e6b207f8 refactor(runtime): close canonical gameplay ownership
Unify the toolbar shortcut manager with Runtime inventory state, route retail-ordered shortcut and spellbook command effects through the canonical owners, and make retained controllers borrow those exact instances. Remove the item-interaction transaction fallback and add graphical/no-window parity plus failure-safe terminal ownership-ledger coverage.

Co-authored-by: Codex <codex@openai.com>
2026-07-26 09:48:51 +02:00

93 lines
2.3 KiB
C#

namespace AcDream.Core.Chat;
/// <summary>
/// Presentation-independent reply/retell targets derived from the committed
/// chat transcript.
/// </summary>
/// <remarks>
/// Incoming tells carry a non-zero sender GUID and update the reply target.
/// Local outgoing tell echoes carry a zero sender GUID and place the target
/// name in <see cref="ChatEntry.Sender"/>. This is the existing retail
/// <c>@reply</c>/<c>@retell</c> behavior formerly owned by <c>ChatVM</c>.
/// </remarks>
public sealed class ChatCommandTargetState : IDisposable
{
private readonly ChatLog _chat;
private readonly object _gate = new();
private string? _lastIncomingTellSender;
private string? _lastOutgoingTellTarget;
private bool _disposed;
public ChatCommandTargetState(ChatLog chat)
{
_chat = chat ?? throw new ArgumentNullException(nameof(chat));
_chat.EntryAppended += OnEntryAppended;
}
public string? LastIncomingTellSender
{
get
{
lock (_gate)
return _lastIncomingTellSender;
}
}
public string? LastOutgoingTellTarget
{
get
{
lock (_gate)
return _lastOutgoingTellTarget;
}
}
public bool IsDisposed
{
get
{
lock (_gate)
return _disposed;
}
}
/// <summary>
/// Forget character-scoped command targets without clearing visible
/// transcript history.
/// </summary>
public void ResetSession()
{
lock (_gate)
{
_lastIncomingTellSender = null;
_lastOutgoingTellTarget = null;
}
}
public void Dispose()
{
lock (_gate)
{
if (_disposed)
return;
_disposed = true;
_chat.EntryAppended -= OnEntryAppended;
}
}
private void OnEntryAppended(ChatEntry entry)
{
if (entry.Kind != ChatKind.Tell || string.IsNullOrEmpty(entry.Sender))
return;
lock (_gate)
{
if (_disposed)
return;
if (entry.SenderGuid != 0u)
_lastIncomingTellSender = entry.Sender;
else
_lastOutgoingTellTarget = entry.Sender;
}
}
}