feat(inventory): TrySpendAsync covers all 4 wallets + freeplay
Crystal/Rupy/RedEther freeplay no-op (returns configured amount, balance unchanged); SpotPoint always real. Insufficient returns current balance; success returns post-deduction balance. SVSimTestFactory gains freeplayEnabled ctor overload that upserts the Freeplay GameConfigSection row after EnsureSeedData. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -38,9 +38,48 @@ internal sealed class InventoryTransaction : IInventoryTransaction
|
||||
_log = log;
|
||||
}
|
||||
|
||||
// Implementations land in later tasks. Throw NotImplementedException to keep the build green.
|
||||
public Task<SpendResult> TrySpendAsync(SpendCurrency currency, long cost, CancellationToken ct = default)
|
||||
=> throw new NotImplementedException();
|
||||
{
|
||||
ThrowIfCommitted();
|
||||
if (cost < 0) cost = 0;
|
||||
|
||||
if (_freeplay.Enabled && currency != SpendCurrency.SpotPoint)
|
||||
{
|
||||
long amount = checked((long)_freeplay.CurrencyAmount);
|
||||
_ops.Add(new SpendOp(currency, cost, amount));
|
||||
return Task.FromResult(new SpendResult(SpendOutcome.Success, amount));
|
||||
}
|
||||
|
||||
ulong current = ReadBalance(currency);
|
||||
if (current < (ulong)cost)
|
||||
return Task.FromResult(new SpendResult(SpendOutcome.Insufficient, (long)current));
|
||||
|
||||
ulong post = current - (ulong)cost;
|
||||
WriteBalance(currency, post);
|
||||
_ops.Add(new SpendOp(currency, cost, (long)post));
|
||||
return Task.FromResult(new SpendResult(SpendOutcome.Success, (long)post));
|
||||
}
|
||||
|
||||
private ulong ReadBalance(SpendCurrency c) => c switch
|
||||
{
|
||||
SpendCurrency.Crystal => Viewer.Currency.Crystals,
|
||||
SpendCurrency.Rupee => Viewer.Currency.Rupees,
|
||||
SpendCurrency.RedEther => Viewer.Currency.RedEther,
|
||||
SpendCurrency.SpotPoint => Viewer.Currency.SpotPoints,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(c)),
|
||||
};
|
||||
|
||||
private void WriteBalance(SpendCurrency c, ulong value)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case SpendCurrency.Crystal: Viewer.Currency.Crystals = value; break;
|
||||
case SpendCurrency.Rupee: Viewer.Currency.Rupees = value; break;
|
||||
case SpendCurrency.RedEther: Viewer.Currency.RedEther = value; break;
|
||||
case SpendCurrency.SpotPoint: Viewer.Currency.SpotPoints = value; break;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(c));
|
||||
}
|
||||
}
|
||||
|
||||
public Task<SpendResult> TryDebitAsync(UserGoodsType type, long detailId, int num, CancellationToken ct = default)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
Reference in New Issue
Block a user