using SVSim.BattleNode.Bridge; namespace SVSim.EmulatedEntrypoint.Matching; /// /// Phase 3 hardcoded fixture: one stub bot per class (1..8). ai_id values are /// the "series 1" tier from the client's rm_ai_setting.csv master table /// (1111=Forest, 1121=Sword, … 1181=Portal). The client's /// RankMatchAISettingList.GetSettingData(aiId) calls .First(...) against /// this table and throws InvalidOperationException if the id is absent — so the /// ids MUST match. See data_dumps/client-assets/rm_ai_setting.csv for the full /// catalog (32 rows: classes 1-8 × tiers 1-2 × deck variants 1-2). /// public sealed class BotRoster : IBotRoster { // Cosmetic ids (sleeve / emblem / degree / field) intentionally use safe // default values that match the master tables shipped in the project. // The client-side AI catalog reads ai_id but renders cosmetics from the // sleeveId/emblemId/etc returned here. private static readonly IReadOnlyList Roster = new[] { new AIBotProfile(AiId: 1111, CountryCode: "JPN", UserName: "Forestcraft AI", // Cosmetic ids mirror the Scripted bot's prod-verified values // (ScriptedProfiles / ScriptedBotParticipant.Context). Placeholder // ids of 1 failed to resolve in ResourcesManager.LoadAssetGroupSync — // LoadOpponentAssets (SBattleLoad.cs:933) hangs forever waiting for // missing assets and the "Waiting for opponent" UI never closes. SleeveId: 704141010, EmblemId: 400001100, DegreeId: 120027, FieldId: 5, IsOfficial: 0, ClassId: 1, CharaId: 1, Rank: 10, BattlePoint: 0, IsMasterRank: 0, MasterPoint: 0), new AIBotProfile(AiId: 1121, CountryCode: "JPN", UserName: "Swordcraft AI", // Cosmetic ids mirror the Scripted bot's prod-verified values // (ScriptedProfiles / ScriptedBotParticipant.Context). Placeholder // ids of 1 failed to resolve in ResourcesManager.LoadAssetGroupSync — // LoadOpponentAssets (SBattleLoad.cs:933) hangs forever waiting for // missing assets and the "Waiting for opponent" UI never closes. SleeveId: 704141010, EmblemId: 400001100, DegreeId: 120027, FieldId: 5, IsOfficial: 0, ClassId: 2, CharaId: 2, Rank: 10, BattlePoint: 0, IsMasterRank: 0, MasterPoint: 0), new AIBotProfile(AiId: 1131, CountryCode: "JPN", UserName: "Runecraft AI", // Cosmetic ids mirror the Scripted bot's prod-verified values // (ScriptedProfiles / ScriptedBotParticipant.Context). Placeholder // ids of 1 failed to resolve in ResourcesManager.LoadAssetGroupSync — // LoadOpponentAssets (SBattleLoad.cs:933) hangs forever waiting for // missing assets and the "Waiting for opponent" UI never closes. SleeveId: 704141010, EmblemId: 400001100, DegreeId: 120027, FieldId: 5, IsOfficial: 0, ClassId: 3, CharaId: 3, Rank: 10, BattlePoint: 0, IsMasterRank: 0, MasterPoint: 0), new AIBotProfile(AiId: 1141, CountryCode: "JPN", UserName: "Dragoncraft AI", // Cosmetic ids mirror the Scripted bot's prod-verified values // (ScriptedProfiles / ScriptedBotParticipant.Context). Placeholder // ids of 1 failed to resolve in ResourcesManager.LoadAssetGroupSync — // LoadOpponentAssets (SBattleLoad.cs:933) hangs forever waiting for // missing assets and the "Waiting for opponent" UI never closes. SleeveId: 704141010, EmblemId: 400001100, DegreeId: 120027, FieldId: 5, IsOfficial: 0, ClassId: 4, CharaId: 4, Rank: 10, BattlePoint: 0, IsMasterRank: 0, MasterPoint: 0), new AIBotProfile(AiId: 1151, CountryCode: "JPN", UserName: "Shadowcraft AI", // Cosmetic ids mirror the Scripted bot's prod-verified values // (ScriptedProfiles / ScriptedBotParticipant.Context). Placeholder // ids of 1 failed to resolve in ResourcesManager.LoadAssetGroupSync — // LoadOpponentAssets (SBattleLoad.cs:933) hangs forever waiting for // missing assets and the "Waiting for opponent" UI never closes. SleeveId: 704141010, EmblemId: 400001100, DegreeId: 120027, FieldId: 5, IsOfficial: 0, ClassId: 5, CharaId: 5, Rank: 10, BattlePoint: 0, IsMasterRank: 0, MasterPoint: 0), new AIBotProfile(AiId: 1161, CountryCode: "JPN", UserName: "Bloodcraft AI", // Cosmetic ids mirror the Scripted bot's prod-verified values // (ScriptedProfiles / ScriptedBotParticipant.Context). Placeholder // ids of 1 failed to resolve in ResourcesManager.LoadAssetGroupSync — // LoadOpponentAssets (SBattleLoad.cs:933) hangs forever waiting for // missing assets and the "Waiting for opponent" UI never closes. SleeveId: 704141010, EmblemId: 400001100, DegreeId: 120027, FieldId: 5, IsOfficial: 0, ClassId: 6, CharaId: 6, Rank: 10, BattlePoint: 0, IsMasterRank: 0, MasterPoint: 0), new AIBotProfile(AiId: 1171, CountryCode: "JPN", UserName: "Havencraft AI", // Cosmetic ids mirror the Scripted bot's prod-verified values // (ScriptedProfiles / ScriptedBotParticipant.Context). Placeholder // ids of 1 failed to resolve in ResourcesManager.LoadAssetGroupSync — // LoadOpponentAssets (SBattleLoad.cs:933) hangs forever waiting for // missing assets and the "Waiting for opponent" UI never closes. SleeveId: 704141010, EmblemId: 400001100, DegreeId: 120027, FieldId: 5, IsOfficial: 0, ClassId: 7, CharaId: 7, Rank: 10, BattlePoint: 0, IsMasterRank: 0, MasterPoint: 0), new AIBotProfile(AiId: 1181, CountryCode: "JPN", UserName: "Portalcraft AI", // Cosmetic ids mirror the Scripted bot's prod-verified values // (ScriptedProfiles / ScriptedBotParticipant.Context). Placeholder // ids of 1 failed to resolve in ResourcesManager.LoadAssetGroupSync — // LoadOpponentAssets (SBattleLoad.cs:933) hangs forever waiting for // missing assets and the "Waiting for opponent" UI never closes. SleeveId: 704141010, EmblemId: 400001100, DegreeId: 120027, FieldId: 5, IsOfficial: 0, ClassId: 8, CharaId: 8, Rank: 10, BattlePoint: 0, IsMasterRank: 0, MasterPoint: 0), }; public AIBotProfile Pick(MatchContext selfCtx) { // Deterministic: hash the ctx and pick from the roster. Same ctx → // same bot so a mid-flight retry of /ai_/start returns the same // opponent (no fresh roster pick on each call). var hash = StringComparer.Ordinal.GetHashCode(selfCtx.UserName) ^ StringComparer.Ordinal.GetHashCode(selfCtx.ClassId); var index = (int)((uint)hash % Roster.Count); return Roster[index]; } }