feat(battle-xp): config section + viewer loader for class XP grants
Adds BattleXpConfig ([ConfigSection("BattleXp")]) with global
XpPerWin/XpPerLoss plus per-mode nullable overrides. Adds BattleXpMode
enum and IViewerRepository.LoadForBattleXpGrantAsync — focused tracked
load with viewer.Classes + Class nav ref included, for the service
about to be introduced.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
28
SVSim.Database/Models/Config/BattleXpConfig.cs
Normal file
28
SVSim.Database/Models/Config/BattleXpConfig.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace SVSim.Database.Models.Config;
|
||||
|
||||
/// <summary>
|
||||
/// Class XP awarded on battle finish. <see cref="XpPerWin"/> / <see cref="XpPerLoss"/>
|
||||
/// are the global defaults; each per-mode nullable slot overrides them for that mode
|
||||
/// when set. Story is clear-only (no loss variant) — <see cref="StoryXpPerClear"/>
|
||||
/// falls back to <see cref="XpPerWin"/> when null.
|
||||
/// </summary>
|
||||
[ConfigSection("BattleXp")]
|
||||
public class BattleXpConfig
|
||||
{
|
||||
public int XpPerWin { get; set; } = 100;
|
||||
public int XpPerLoss { get; set; } = 25;
|
||||
|
||||
public int? PracticeXpPerWin { get; set; }
|
||||
public int? PracticeXpPerLoss { get; set; }
|
||||
public int? RankXpPerWin { get; set; }
|
||||
public int? RankXpPerLoss { get; set; }
|
||||
public int? FreeXpPerWin { get; set; }
|
||||
public int? FreeXpPerLoss { get; set; }
|
||||
public int? ArenaTwoPickXpPerWin { get; set; }
|
||||
public int? ArenaTwoPickXpPerLoss { get; set; }
|
||||
public int? ColosseumXpPerWin { get; set; }
|
||||
public int? ColosseumXpPerLoss { get; set; }
|
||||
public int? StoryXpPerClear { get; set; }
|
||||
|
||||
public static BattleXpConfig ShippedDefaults() => new();
|
||||
}
|
||||
@@ -51,6 +51,14 @@ public interface IViewerRepository
|
||||
/// </summary>
|
||||
Task<Models.Viewer?> LoadForMatchContextAsync(long viewerId);
|
||||
|
||||
/// <summary>
|
||||
/// Focused load for class-XP grants: viewer with owned <c>Classes</c> collection and
|
||||
/// each <c>ViewerClassData.Class</c> nav ref included. Tracked (not AsNoTracking) so
|
||||
/// the caller can mutate <c>Exp</c>/<c>Level</c> and <c>SaveChangesAsync</c>. Returns
|
||||
/// null if the viewer does not exist.
|
||||
/// </summary>
|
||||
Task<Models.Viewer?> LoadForBattleXpGrantAsync(long viewerId, CancellationToken ct = default);
|
||||
|
||||
/// <summary>Sets Viewer.GuildId to <paramref name="guildId"/>. No-op if the viewer does not exist.</summary>
|
||||
Task SetGuildIdAsync(long viewerId, int guildId, CancellationToken ct = default);
|
||||
|
||||
|
||||
@@ -273,6 +273,14 @@ public class ViewerRepository : IViewerRepository
|
||||
.FirstOrDefaultAsync(v => v.Id == viewerId);
|
||||
}
|
||||
|
||||
public Task<Models.Viewer?> LoadForBattleXpGrantAsync(long viewerId, CancellationToken ct = default)
|
||||
{
|
||||
return _dbContext.Set<Models.Viewer>()
|
||||
.Include(v => v.Classes)
|
||||
.ThenInclude(c => c.Class)
|
||||
.FirstOrDefaultAsync(v => v.Id == viewerId, ct);
|
||||
}
|
||||
|
||||
public async Task SetGuildIdAsync(long viewerId, int guildId, CancellationToken ct = default)
|
||||
{
|
||||
var viewer = await _dbContext.Set<Models.Viewer>()
|
||||
|
||||
11
SVSim.Database/Services/BattleXp/BattleXpMode.cs
Normal file
11
SVSim.Database/Services/BattleXp/BattleXpMode.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace SVSim.Database.Services.BattleXp;
|
||||
|
||||
public enum BattleXpMode
|
||||
{
|
||||
Practice,
|
||||
Rank,
|
||||
Free,
|
||||
ArenaTwoPick,
|
||||
Colosseum,
|
||||
Story,
|
||||
}
|
||||
Reference in New Issue
Block a user