using SVSim.BattleNode.Bridge; namespace SVSim.EmulatedEntrypoint.Matching; /// /// Phase 3 hardcoded fixture: one stub bot per class (1..8). ai_id /// values 4001..4008 are placeholders per the existing /// docs/api-spec/endpoints/post-login/rank-battle/ai-start.md TODO — /// no live capture exists to validate the real prod catalog. /// 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: 4001, CountryCode: "NONE", UserName: "Forestcraft AI", SleeveId: 1001, EmblemId: 1, DegreeId: 1, FieldId: 1, IsOfficial: 0, ClassId: 1, CharaId: 1, Rank: 10, BattlePoint: 0, IsMasterRank: 0, MasterPoint: 0), new AIBotProfile(AiId: 4002, CountryCode: "NONE", UserName: "Swordcraft AI", SleeveId: 1001, EmblemId: 1, DegreeId: 1, FieldId: 1, IsOfficial: 0, ClassId: 2, CharaId: 2, Rank: 10, BattlePoint: 0, IsMasterRank: 0, MasterPoint: 0), new AIBotProfile(AiId: 4003, CountryCode: "NONE", UserName: "Runecraft AI", SleeveId: 1001, EmblemId: 1, DegreeId: 1, FieldId: 1, IsOfficial: 0, ClassId: 3, CharaId: 3, Rank: 10, BattlePoint: 0, IsMasterRank: 0, MasterPoint: 0), new AIBotProfile(AiId: 4004, CountryCode: "NONE", UserName: "Dragoncraft AI", SleeveId: 1001, EmblemId: 1, DegreeId: 1, FieldId: 1, IsOfficial: 0, ClassId: 4, CharaId: 4, Rank: 10, BattlePoint: 0, IsMasterRank: 0, MasterPoint: 0), new AIBotProfile(AiId: 4005, CountryCode: "NONE", UserName: "Shadowcraft AI", SleeveId: 1001, EmblemId: 1, DegreeId: 1, FieldId: 1, IsOfficial: 0, ClassId: 5, CharaId: 5, Rank: 10, BattlePoint: 0, IsMasterRank: 0, MasterPoint: 0), new AIBotProfile(AiId: 4006, CountryCode: "NONE", UserName: "Bloodcraft AI", SleeveId: 1001, EmblemId: 1, DegreeId: 1, FieldId: 1, IsOfficial: 0, ClassId: 6, CharaId: 6, Rank: 10, BattlePoint: 0, IsMasterRank: 0, MasterPoint: 0), new AIBotProfile(AiId: 4007, CountryCode: "NONE", UserName: "Havencraft AI", SleeveId: 1001, EmblemId: 1, DegreeId: 1, FieldId: 1, IsOfficial: 0, ClassId: 7, CharaId: 7, Rank: 10, BattlePoint: 0, IsMasterRank: 0, MasterPoint: 0), new AIBotProfile(AiId: 4008, CountryCode: "NONE", UserName: "Portalcraft AI", SleeveId: 1001, EmblemId: 1, DegreeId: 1, FieldId: 1, 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]; } }