Practice battles work
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SVSim.Database.Enums;
|
||||
using SVSim.Database.Models;
|
||||
using SVSim.Database.Repositories.Viewer;
|
||||
using SVSim.EmulatedEntrypoint.Constants;
|
||||
using SVSim.EmulatedEntrypoint.Models.Dtos;
|
||||
using SVSim.Database.Repositories.Deck;
|
||||
using SVSim.Database.Repositories.Globals;
|
||||
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests;
|
||||
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests.Common;
|
||||
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests.Practice;
|
||||
@@ -13,94 +12,59 @@ namespace SVSim.EmulatedEntrypoint.Controllers;
|
||||
|
||||
public class PracticeController : SVSimController
|
||||
{
|
||||
// Hand-curated AI opponents (audit B14 pattern). Replace with master data when the
|
||||
// practice subsystem is built out.
|
||||
private static readonly List<PracticeOpponent> StubOpponents = new()
|
||||
{
|
||||
new PracticeOpponent
|
||||
{
|
||||
PracticeId = 1,
|
||||
TextId = "Practice_001",
|
||||
ClassId = 1,
|
||||
CharaId = 1,
|
||||
DegreeId = 0,
|
||||
AiDeckLevel = 1,
|
||||
AiLogicLevel = 1,
|
||||
AiMaxLife = 20,
|
||||
Battle3dFieldId = "1",
|
||||
IsCampaignPractice = false
|
||||
},
|
||||
new PracticeOpponent
|
||||
{
|
||||
PracticeId = 2,
|
||||
TextId = "Practice_002",
|
||||
ClassId = 2,
|
||||
CharaId = 2,
|
||||
DegreeId = 0,
|
||||
AiDeckLevel = 2,
|
||||
AiLogicLevel = 2,
|
||||
AiMaxLife = 20,
|
||||
Battle3dFieldId = "1",
|
||||
IsCampaignPractice = false
|
||||
},
|
||||
new PracticeOpponent
|
||||
{
|
||||
PracticeId = 3,
|
||||
TextId = "Practice_003",
|
||||
ClassId = 3,
|
||||
CharaId = 3,
|
||||
DegreeId = 0,
|
||||
AiDeckLevel = 3,
|
||||
AiLogicLevel = 3,
|
||||
AiMaxLife = 25,
|
||||
Battle3dFieldId = "1",
|
||||
IsCampaignPractice = false
|
||||
}
|
||||
};
|
||||
private readonly IDeckRepository _deckRepository;
|
||||
private readonly IGlobalsRepository _globalsRepository;
|
||||
|
||||
private readonly IViewerRepository _viewerRepository;
|
||||
|
||||
public PracticeController(IViewerRepository viewerRepository)
|
||||
public PracticeController(IDeckRepository deckRepository, IGlobalsRepository globalsRepository)
|
||||
{
|
||||
_viewerRepository = viewerRepository;
|
||||
_deckRepository = deckRepository;
|
||||
_globalsRepository = globalsRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// /practice/info — returns the AI opponent catalog. Response data is a JSON array
|
||||
/// directly (not wrapped in an object), per spec.
|
||||
/// directly (not wrapped in an object), per spec. Backed by PracticeOpponents table,
|
||||
/// seeded by SVSim.Bootstrap from prod-captures/practice-info-*.json.
|
||||
/// </summary>
|
||||
[HttpPost("info")]
|
||||
public Task<List<PracticeOpponent>> Info(BaseRequest request)
|
||||
public async Task<List<PracticeOpponent>> Info(BaseRequest request)
|
||||
{
|
||||
return Task.FromResult(StubOpponents);
|
||||
var rows = await _globalsRepository.GetPracticeOpponents();
|
||||
return rows.Select(e => new PracticeOpponent
|
||||
{
|
||||
PracticeId = e.PracticeId,
|
||||
TextId = e.TextId,
|
||||
ClassId = e.ClassId,
|
||||
CharaId = e.CharaId,
|
||||
DegreeId = e.DegreeId,
|
||||
AiDeckLevel = e.AiDeckLevel,
|
||||
AiLogicLevel = e.AiLogicLevel,
|
||||
AiMaxLife = e.AiMaxLife,
|
||||
Battle3dFieldId = e.Battle3dFieldId,
|
||||
IsMaintenance = e.IsMaintenance,
|
||||
IsCampaignPractice = e.IsCampaignPractice,
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// /practice/deck_list — returns viewer's decks scoped by format (always Format.All
|
||||
/// per spec, server can ignore the request field).
|
||||
/// per spec, server can ignore the request field). Fetched via IDeckRepository so the
|
||||
/// DeckCard.Card navigation is Included; going through the heavier viewer-graph query
|
||||
/// drops that ThenInclude and ships 40 zeros instead of real card ids, which then
|
||||
/// NREs the client's SBattleLoad.InitPlayer (CardCreator returns null on id=0).
|
||||
/// </summary>
|
||||
[HttpPost("deck_list")]
|
||||
public async Task<ActionResult<PracticeDeckListResponse>> DeckList(DeckFormatRequest request)
|
||||
{
|
||||
var shortUdidClaim = User.Claims.FirstOrDefault(c => c.Type == ShadowverseClaimTypes.ShortUdidClaim)?.Value;
|
||||
if (shortUdidClaim is null || !long.TryParse(shortUdidClaim, out long shortUdid))
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
if (!TryGetViewerId(out long viewerId)) return Unauthorized();
|
||||
|
||||
Viewer? viewer = await _viewerRepository.GetViewerByShortUdid(shortUdid);
|
||||
if (viewer is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
var byFormat = await _deckRepository.GetDecksByFormats(viewerId, new[] { Format.Rotation, Format.Unlimited });
|
||||
|
||||
return new PracticeDeckListResponse
|
||||
{
|
||||
MaintenanceCardList = new List<long>(),
|
||||
UserDeckRotation = viewer.Decks.Where(d => d.Format == Format.Rotation)
|
||||
.Select(d => new UserDeck(d)).ToList(),
|
||||
UserDeckUnlimited = viewer.Decks.Where(d => d.Format == Format.Unlimited)
|
||||
.Select(d => new UserDeck(d)).ToList()
|
||||
UserDeckRotation = byFormat[Format.Rotation].Select(d => new UserDeck(d)).ToList(),
|
||||
UserDeckUnlimited = byFormat[Format.Unlimited].Select(d => new UserDeck(d)).ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user