fix(client): restore retail interaction parity
All checks were successful
CI / linux-portable (push) Successful in 3m27s
CI / windows-gate (push) Successful in 6m42s
CI / release (push) Successful in 2m12s

Harden keyboard and camera routing, inventory and vendor interactions, chat/emotes, relog portal flow, and paperdoll rendering. Add retail research, connected gate coverage, and release-gate validation.
This commit is contained in:
Erik 2026-08-26 20:45:11 +02:00
parent 0c699240e0
commit f6fe0f2a4f
151 changed files with 10162 additions and 1211 deletions

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using AcDream.Core.Content;
using DatReaderWriter.DBObjs;
using DatReaderWriter.Types;
namespace AcDream.Core.Input;
@ -124,7 +125,23 @@ public sealed record RetailActionMapRow(
/// <summary>The complete read result: every user-bindable ActionMap row, plus the raw
/// row count read (for conformance pinning against the installed dats).</summary>
public sealed record RetailActionMapSnapshot(IReadOnlyList<RetailActionMapRow> Rows);
public sealed record RetailActionMapSnapshot(
IReadOnlyList<RetailActionMapRow> Rows,
IReadOnlyDictionary<uint, IReadOnlySet<uint>>? ConflictingInputMaps = null)
{
/// <summary>
/// Retail <c>ICIDM::FindConflictingInputMaps</c> policy. A context always
/// conflicts with itself; cross-context conflicts exist only when the
/// DAT <c>ActionMap.ConflictingMaps</c> table names the other context.
/// Contexts absent from that table therefore do not conflict across maps.
/// </summary>
public bool InputMapsConflict(uint leftInputMapId, uint rightInputMapId) =>
leftInputMapId == rightInputMapId
|| (ConflictingInputMaps?.TryGetValue(
leftInputMapId,
out IReadOnlySet<uint>? conflicts) == true
&& conflicts.Contains(rightInputMapId));
}
/// <summary>
/// Retail's 19 named <c>InputMapID -&gt; ID_InputMap_*</c> string-table keys
@ -220,7 +237,16 @@ public static class RetailActionMapReader
}
}
return new RetailActionMapSnapshot(rows);
var conflictingInputMaps = new Dictionary<uint, IReadOnlySet<uint>>();
foreach (var entry in actionMap.ConflictingMaps)
{
InputsConflictsValue value = entry.Value;
uint inputMapId = value.InputMap != 0u ? value.InputMap : entry.Key;
conflictingInputMaps[inputMapId] =
new HashSet<uint>(value.ConflictingInputMaps);
}
return new RetailActionMapSnapshot(rows, conflictingInputMaps);
}
private static void CollectDefaults(