feat(story): populate build/trial/default deck lists on get_deck_list
Wire IBuildDeckRepository into StoryService; GetDeckListAsync now looks up the chapter's CharaId, fetches class-specific prebuilt/trial decks via GetStoryDecksByClass, and loads all DefaultDecks for default_deck_list. Class guard (1-8) leaves build/trial empty for non-class chapters, matching prod behaviour. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Text.Json;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Entities.Story;
|
||||
using SVSim.Database.Enums;
|
||||
using SVSim.Database.Models.Config;
|
||||
using SVSim.Database.Repositories.Deck;
|
||||
using SVSim.Database.Repositories.BuildDeck;
|
||||
using SVSim.Database.Services;
|
||||
using SVSim.Database.Repositories.Story;
|
||||
using SVSim.EmulatedEntrypoint.Models.Dtos;
|
||||
using SVSim.EmulatedEntrypoint.Models.Dtos.Common;
|
||||
using SVSim.EmulatedEntrypoint.Models.Dtos.Story;
|
||||
|
||||
namespace SVSim.EmulatedEntrypoint.Services;
|
||||
@@ -19,6 +23,7 @@ public class StoryService : IStoryService
|
||||
private readonly SVSimDbContext _db;
|
||||
private readonly IGameConfigService _configService;
|
||||
private readonly IDeckRepository _deckRepository;
|
||||
private readonly IBuildDeckRepository _buildDecks;
|
||||
private readonly ILogger<StoryService> _logger;
|
||||
|
||||
public StoryService(
|
||||
@@ -28,6 +33,7 @@ public class StoryService : IStoryService
|
||||
SVSimDbContext db,
|
||||
IGameConfigService configService,
|
||||
IDeckRepository deckRepository,
|
||||
IBuildDeckRepository buildDecks,
|
||||
ILogger<StoryService> logger)
|
||||
{
|
||||
_master = master;
|
||||
@@ -36,6 +42,7 @@ public class StoryService : IStoryService
|
||||
_db = db;
|
||||
_configService = configService;
|
||||
_deckRepository = deckRepository;
|
||||
_buildDecks = buildDecks;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -344,16 +351,85 @@ public class StoryService : IStoryService
|
||||
{
|
||||
var byFormat = await _deckRepository.GetDecksByFormats(
|
||||
viewerId, new[] { SVSim.Database.Enums.Format.Rotation, SVSim.Database.Enums.Format.Unlimited });
|
||||
return new GetDeckListResponse
|
||||
|
||||
var resp = new GetDeckListResponse
|
||||
{
|
||||
UserDeckRotation = byFormat[SVSim.Database.Enums.Format.Rotation]
|
||||
.Select(d => new SVSim.EmulatedEntrypoint.Models.Dtos.UserDeck(d)).ToList(),
|
||||
.Select(d => new UserDeck(d)).ToList(),
|
||||
UserDeckUnlimited = byFormat[SVSim.Database.Enums.Format.Unlimited]
|
||||
.Select(d => new SVSim.EmulatedEntrypoint.Models.Dtos.UserDeck(d)).ToList(),
|
||||
BuildDeckList = new List<BuildDeck>(), // v1: empty
|
||||
.Select(d => new UserDeck(d)).ToList(),
|
||||
MaintenanceCardList = new List<long>(),
|
||||
};
|
||||
|
||||
// The chapter's leader (CharaId == class_id 1-8 for standard classes) drives which
|
||||
// prebuilt/trial decks the story deck-select shows. Non-class chapters (custom leaders,
|
||||
// chara_id outside 1-8) get empty build/trial lists, matching prod.
|
||||
var chapter = await _master.GetChapterByIdAsync(storyId);
|
||||
int classId = chapter?.CharaId ?? 0;
|
||||
if (classId is >= 1 and <= 8)
|
||||
{
|
||||
var storyDecks = await _buildDecks.GetStoryDecksByClass(classId);
|
||||
resp.BuildDeckList = storyDecks
|
||||
.Where(d => d.Kind == StoryDeckKind.Build)
|
||||
.Select(ToBuildDeck).ToList();
|
||||
resp.TrialDeckList = storyDecks
|
||||
.Where(d => d.Kind == StoryDeckKind.Trial)
|
||||
.Select(ToTrialDeck).ToList();
|
||||
}
|
||||
|
||||
// default_deck_list — all 8 starter decks, keyed by deck_no string (same shape as /deck/info).
|
||||
var defaults = await _db.DefaultDecks.OrderBy(d => d.Id).ToListAsync();
|
||||
resp.DefaultDeckList = defaults.ToDictionary(
|
||||
d => d.Id.ToString(),
|
||||
d => new DefaultDeck
|
||||
{
|
||||
DeckNo = d.DeckNo,
|
||||
ClassId = d.ClassId,
|
||||
SleeveId = d.SleeveId,
|
||||
LeaderSkinId = d.LeaderSkinId,
|
||||
DeckName = d.DeckName,
|
||||
CardIdArray = JsonSerializer.Deserialize<List<long>>(d.CardIdArray) ?? new(),
|
||||
IsCompleteDeck = 1,
|
||||
IsAvailableDeck = 1,
|
||||
MaintenanceCardIds = new(),
|
||||
});
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
private static BuildDeck ToBuildDeck(StoryDeckView d) => new()
|
||||
{
|
||||
DeckNo = d.DeckNo,
|
||||
OrderNum = d.OrderNum,
|
||||
ClassId = d.ClassId,
|
||||
SleeveId = d.SleeveId,
|
||||
LeaderSkinId = d.LeaderSkinId,
|
||||
EntryNo = d.EntryNo,
|
||||
CreateDeckTime = null,
|
||||
DeckName = d.DeckName,
|
||||
CardIdArray = d.CardIdArray,
|
||||
IsCompleteDeck = 1,
|
||||
IsAvailableDeck = 1,
|
||||
MaintenanceCardIds = new(),
|
||||
IsRecommend = d.IsRecommend,
|
||||
};
|
||||
|
||||
private static TrialDeck ToTrialDeck(StoryDeckView d) => new()
|
||||
{
|
||||
DeckNo = d.DeckNo,
|
||||
ClassId = d.ClassId,
|
||||
SleeveId = d.SleeveId,
|
||||
LeaderSkinId = d.LeaderSkinId,
|
||||
DeckName = d.DeckName,
|
||||
CardIdArray = d.CardIdArray,
|
||||
IsCompleteDeck = 1,
|
||||
RestrictedCardExists = false,
|
||||
IsAvailableDeck = 1,
|
||||
MaintenanceCardIds = new(),
|
||||
IsIncludeUnPossessionCard = false,
|
||||
DeckFormat = d.DeckFormat ?? 0,
|
||||
IsRecommend = d.IsRecommend,
|
||||
};
|
||||
public async Task<StartResponse> StartAsync(StoryApiType apiType, int[] storyIds, long viewerId)
|
||||
{
|
||||
var resp = new StartResponse();
|
||||
|
||||
Reference in New Issue
Block a user