neon-sprawl/server/NeonSprawl.Server/Game/Mastery/MasteryFixtureOperations.cs

71 lines
1.9 KiB
C#

using NeonSprawl.Server.Game.Skills;
namespace NeonSprawl.Server.Game.Mastery;
/// <summary>Applies dev-only mastery fixture resets (NEO-48).</summary>
public static class MasteryFixtureOperations
{
/// <summary>Validates <paramref name="skillXp"/> entries; returns false when any key is blank or XP is negative.</summary>
public static bool TryNormalizeSkillXp(
IReadOnlyDictionary<string, int>? skillXp,
out Dictionary<string, int> normalized)
{
normalized = new Dictionary<string, int>(StringComparer.Ordinal);
if (skillXp is null || skillXp.Count == 0)
{
return true;
}
foreach (var (skillId, xp) in skillXp)
{
var sid = skillId.Trim();
if (sid.Length == 0 || xp < 0)
{
normalized.Clear();
return false;
}
normalized[sid] = xp;
}
return true;
}
/// <summary>Applies skill XP (batch, then perk reset). Rolls back XP when perk reset fails after XP writes.</summary>
public static bool TryApply(
string playerId,
MasteryFixtureRequest body,
IPlayerPerkStateStore perkStore,
IPlayerSkillProgressionStore xpStore)
{
if (!TryNormalizeSkillXp(body.SkillXp, out var skillXp))
{
return false;
}
var previousXp = new Dictionary<string, int>(xpStore.GetXpTotals(playerId), StringComparer.Ordinal);
if (skillXp.Count > 0 && !xpStore.TrySetSkillXpTotals(playerId, skillXp))
{
return false;
}
if (!body.ResetPerkState)
{
return true;
}
if (perkStore.TryResetPerkState(playerId))
{
return true;
}
if (skillXp.Count > 0)
{
_ = xpStore.TrySetSkillXpTotals(playerId, previousXp);
}
return false;
}
}