From 454137888546a892ddcb366d82cdfe55f9d1b08e Mon Sep 17 00:00:00 2001 From: gamer147 Date: Sat, 4 Jul 2026 08:44:11 -0400 Subject: [PATCH] feat(battle-xp): config section + viewer loader for class XP grants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../Models/Config/BattleXpConfig.cs | 28 +++++++++++++++++++ .../Repositories/Viewer/IViewerRepository.cs | 8 ++++++ .../Repositories/Viewer/ViewerRepository.cs | 8 ++++++ .../Services/BattleXp/BattleXpMode.cs | 11 ++++++++ 4 files changed, 55 insertions(+) create mode 100644 SVSim.Database/Models/Config/BattleXpConfig.cs create mode 100644 SVSim.Database/Services/BattleXp/BattleXpMode.cs diff --git a/SVSim.Database/Models/Config/BattleXpConfig.cs b/SVSim.Database/Models/Config/BattleXpConfig.cs new file mode 100644 index 00000000..0a83c413 --- /dev/null +++ b/SVSim.Database/Models/Config/BattleXpConfig.cs @@ -0,0 +1,28 @@ +namespace SVSim.Database.Models.Config; + +/// +/// Class XP awarded on battle finish. / +/// are the global defaults; each per-mode nullable slot overrides them for that mode +/// when set. Story is clear-only (no loss variant) — +/// falls back to when null. +/// +[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(); +} diff --git a/SVSim.Database/Repositories/Viewer/IViewerRepository.cs b/SVSim.Database/Repositories/Viewer/IViewerRepository.cs index da2e98ca..ea93d8ad 100644 --- a/SVSim.Database/Repositories/Viewer/IViewerRepository.cs +++ b/SVSim.Database/Repositories/Viewer/IViewerRepository.cs @@ -51,6 +51,14 @@ public interface IViewerRepository /// Task LoadForMatchContextAsync(long viewerId); + /// + /// Focused load for class-XP grants: viewer with owned Classes collection and + /// each ViewerClassData.Class nav ref included. Tracked (not AsNoTracking) so + /// the caller can mutate Exp/Level and SaveChangesAsync. Returns + /// null if the viewer does not exist. + /// + Task LoadForBattleXpGrantAsync(long viewerId, CancellationToken ct = default); + /// Sets Viewer.GuildId to . No-op if the viewer does not exist. Task SetGuildIdAsync(long viewerId, int guildId, CancellationToken ct = default); diff --git a/SVSim.Database/Repositories/Viewer/ViewerRepository.cs b/SVSim.Database/Repositories/Viewer/ViewerRepository.cs index 9b7b4e24..ed06edef 100644 --- a/SVSim.Database/Repositories/Viewer/ViewerRepository.cs +++ b/SVSim.Database/Repositories/Viewer/ViewerRepository.cs @@ -273,6 +273,14 @@ public class ViewerRepository : IViewerRepository .FirstOrDefaultAsync(v => v.Id == viewerId); } + public Task LoadForBattleXpGrantAsync(long viewerId, CancellationToken ct = default) + { + return _dbContext.Set() + .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() diff --git a/SVSim.Database/Services/BattleXp/BattleXpMode.cs b/SVSim.Database/Services/BattleXp/BattleXpMode.cs new file mode 100644 index 00000000..5ab2c06d --- /dev/null +++ b/SVSim.Database/Services/BattleXp/BattleXpMode.cs @@ -0,0 +1,11 @@ +namespace SVSim.Database.Services.BattleXp; + +public enum BattleXpMode +{ + Practice, + Rank, + Free, + ArenaTwoPick, + Colosseum, + Story, +}