feat(battle-node): UnapprovedCardEntry + RelayUList pure transform

Verbatim uList relay shape + transform (deck-sourced summons/fetches),
mirroring RenameTargets. Not yet wired into the handler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-04 11:17:10 -04:00
parent 61080adace
commit c0309061fa
3 changed files with 146 additions and 0 deletions

View File

@@ -1,4 +1,5 @@
using NUnit.Framework;
using SVSim.BattleNode.Protocol.Bodies;
using SVSim.BattleNode.Sessions.Dispatch;
namespace SVSim.UnitTests.BattleNode.Sessions;
@@ -422,4 +423,86 @@ public class KnownListBuilderTests
var mined = KnownListBuilder.MineCopyTokens(orderList, selfMap, new Dictionary<int, long>()).ToList();
Assert.That(mined, Is.EquivalentTo(new[] { (31, 700L, 1), (32, 700L, 1) }));
}
// A uList entry as it arrives in a RawBody. Minimal = the 5 always-present fields
// (capture battle-traffic_tk2_regular.ndjson:75). Optional fields added per-test.
private static Dictionary<string, object?> UListEntry(
long[] idxList, int from, int to, int isSelf, string skill) => new()
{
["idxList"] = idxList.Select(i => (object?)i).ToList(),
["from"] = (long)from, ["to"] = (long)to, ["isSelf"] = (long)isSelf, ["skill"] = skill,
};
[Test]
public void RelayUList_maps_the_minimal_capture_entry_shape()
{
// battle-traffic_tk2_regular.ndjson:75 — a hidden deck-fetch (no cardId), the only uList shape
// in any capture. The 5 always-present fields map; conditionals stay null.
var uList = new List<object?> { UListEntry(new[] { 16L, 22L }, from: 0, to: 10, isSelf: 1, skill: "37|36|0") };
var relayed = KnownListBuilder.RelayUList(uList);
Assert.That(relayed, Is.Not.Null);
Assert.That(relayed!.Count, Is.EqualTo(1));
var e = relayed[0];
Assert.That(e.IdxList, Is.EqualTo(new[] { 16, 22 }));
Assert.That(e.From, Is.EqualTo(0));
Assert.That(e.To, Is.EqualTo(10));
Assert.That(e.IsSelf, Is.EqualTo(1));
Assert.That(e.Skill, Is.EqualTo("37|36|0"));
Assert.That(e.CardId, Is.Null);
Assert.That(e.Clan, Is.Null);
Assert.That(e.Cost, Is.Null);
Assert.That(e.SkillKeyCardIdx, Is.Null);
Assert.That(e.RandomTargetIdx, Is.Null);
Assert.That(e.IsInvoke, Is.Null);
Assert.That(e.AttachTarget, Is.Null);
}
[Test]
public void RelayUList_maps_a_revealed_summon_with_all_conditional_fields()
{
// Decomp-grounded (no capture): a revealed summon-to-field carries cardId + clan + cost etc.
var entry = UListEntry(new[] { 40L }, from: 0, to: 20, isSelf: 1, skill: "5|3|0");
entry["cardId"] = 900111010L;
entry["clan"] = 8L;
entry["cost"] = 2L;
entry["skillKeyCardIdx"] = new List<object?> { 7L };
entry["randomTargetIdx"] = new List<object?> { 2L, 3L };
entry["isInvoke"] = 1L;
entry["attachTarget"] = "12,13";
var relayed = KnownListBuilder.RelayUList(new List<object?> { entry });
var e = relayed![0];
Assert.That(e.To, Is.EqualTo(20));
Assert.That(e.CardId, Is.EqualTo(900111010L));
Assert.That(e.Clan, Is.EqualTo(8));
Assert.That(e.Cost, Is.EqualTo(2));
Assert.That(e.SkillKeyCardIdx, Is.EqualTo(new[] { 7 }));
Assert.That(e.RandomTargetIdx, Is.EqualTo(new[] { 2, 3 }));
Assert.That(e.IsInvoke, Is.EqualTo(1));
Assert.That(e.AttachTarget, Is.EqualTo("12,13"));
}
[Test]
public void RelayUList_preserves_multiple_entries_in_order()
{
var uList = new List<object?>
{
UListEntry(new[] { 16L }, 0, 10, 1, "a"),
UListEntry(new[] { 22L }, 0, 20, 0, "b"),
};
var relayed = KnownListBuilder.RelayUList(uList);
Assert.That(relayed!.Count, Is.EqualTo(2));
Assert.That(relayed[0].Skill, Is.EqualTo("a"));
Assert.That(relayed[1].Skill, Is.EqualTo("b"));
Assert.That(relayed[1].IsSelf, Is.EqualTo(0));
}
[Test]
public void RelayUList_returns_null_for_missing_or_empty()
{
Assert.That(KnownListBuilder.RelayUList(null), Is.Null);
Assert.That(KnownListBuilder.RelayUList(new List<object?>()), Is.Null);
}
}