feat(battle-xp): wire story first-clear /finish through IBattleXpService
- StoryConfig.ClassXpPerClear retired; equivalent knob moves to BattleXpConfig.StoryXpPerClear (falls back to XpPerWin when null). - StoryService.FinishAsync now grants class XP via IBattleXpService with BattleXpMode.Story on the firstClear && isPlayShape branch. Persists post-grant Level/Exp to Viewer.Classes. - Wire fields (get_class_experience, class_level) stringified as before. - StoryConfig kept as an empty [ConfigSection] for future story-specific knobs. StoryServiceTests updated to reflect the new default path (XpPerWin=100 crosses classexp.csv L1=50 → L2 with 50 carry). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,9 @@ using SVSim.Database.Enums;
|
||||
using SVSim.Database.Models.Config;
|
||||
using SVSim.Database.Repositories.Deck;
|
||||
using SVSim.Database.Repositories.BuildDeck;
|
||||
using SVSim.Database.Repositories.Viewer;
|
||||
using SVSim.Database.Services;
|
||||
using SVSim.Database.Services.BattleXp;
|
||||
using SVSim.Database.Services.Inventory;
|
||||
using SVSim.Database.Repositories.Story;
|
||||
using SVSim.EmulatedEntrypoint.Models.Dtos;
|
||||
@@ -25,6 +27,8 @@ public class StoryService : IStoryService
|
||||
private readonly IGameConfigService _configService;
|
||||
private readonly IDeckRepository _deckRepository;
|
||||
private readonly IBuildDeckRepository _buildDecks;
|
||||
private readonly IViewerRepository _viewers;
|
||||
private readonly IBattleXpService _xp;
|
||||
private readonly ILogger<StoryService> _logger;
|
||||
|
||||
public StoryService(
|
||||
@@ -35,6 +39,8 @@ public class StoryService : IStoryService
|
||||
IGameConfigService configService,
|
||||
IDeckRepository deckRepository,
|
||||
IBuildDeckRepository buildDecks,
|
||||
IViewerRepository viewers,
|
||||
IBattleXpService xp,
|
||||
ILogger<StoryService> logger)
|
||||
{
|
||||
_master = master;
|
||||
@@ -44,6 +50,8 @@ public class StoryService : IStoryService
|
||||
_configService = configService;
|
||||
_deckRepository = deckRepository;
|
||||
_buildDecks = buildDecks;
|
||||
_viewers = viewers;
|
||||
_xp = xp;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -577,12 +585,15 @@ public class StoryService : IStoryService
|
||||
{
|
||||
// XP grant requires a class_id (only sent on play-shape). No-battle chapters
|
||||
// have no class context — prod returns get_class_experience=0 for them.
|
||||
var xp = _configService.Get<StoryConfig>().ClassXpPerClear;
|
||||
resp.GetClassExperience = xp.ToString();
|
||||
// class_experience / class_level updates would consult the viewer's per-class XP
|
||||
// table — placeholder zeros; wire to viewer.Classes[class_id] when that path exists.
|
||||
resp.ClassExperience = 0;
|
||||
resp.ClassLevel = "0";
|
||||
var xpViewer = await _viewers.LoadForBattleXpGrantAsync(viewerId);
|
||||
if (xpViewer is not null && req.ClassId.HasValue)
|
||||
{
|
||||
var xp = await _xp.GrantAsync(xpViewer, req.ClassId.Value, isWin: true, BattleXpMode.Story);
|
||||
await _db.SaveChangesAsync();
|
||||
resp.GetClassExperience = xp.GetXp.ToString();
|
||||
resp.ClassExperience = xp.TotalXp;
|
||||
resp.ClassLevel = xp.Level.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user