94 lines
3.3 KiB
C#
94 lines
3.3 KiB
C#
namespace NeonSprawl.Server.Game.Factions;
|
|
|
|
/// <summary>
|
|
/// Orchestrates auditable faction standing apply: standing mutation + append-only audit row (NEO-136).
|
|
/// Quest reward wiring: <see cref="Rewards.RewardRouterOperations"/> (NEO-138).
|
|
/// NEO-141 telemetry hook site: successful apply path in <see cref="TryApplyDelta"/>.
|
|
/// </summary>
|
|
public static class ReputationOperations
|
|
{
|
|
/// <summary>
|
|
/// Applies a signed standing delta for one player+faction, clamps to faction bounds, and appends one audit row.
|
|
/// Callers must not invoke <see cref="IFactionStandingStore.TryApplyStandingDelta"/> alone for game apply.
|
|
/// </summary>
|
|
public static ReputationApplyOutcome TryApplyDelta(
|
|
string playerId,
|
|
string factionId,
|
|
int deltaAmount,
|
|
string deltaId,
|
|
string sourceKind,
|
|
string sourceId,
|
|
IFactionStandingStore standingStore,
|
|
IReputationDeltaStore auditStore,
|
|
TimeProvider timeProvider)
|
|
{
|
|
if (deltaAmount == 0)
|
|
{
|
|
return Deny(ReputationApplyReasonCodes.InvalidDelta);
|
|
}
|
|
|
|
var normalizedPlayerId = FactionStandingIds.NormalizePlayerId(playerId);
|
|
var normalizedFactionId = FactionStandingIds.NormalizeFactionId(factionId);
|
|
var normalizedDeltaId = deltaId.Trim();
|
|
if (normalizedDeltaId.Length == 0)
|
|
{
|
|
return Deny(ReputationApplyReasonCodes.InvalidDeltaId);
|
|
}
|
|
|
|
var normalizedSourceKind = sourceKind.Trim();
|
|
var normalizedSourceId = sourceId.Trim();
|
|
if (normalizedSourceKind.Length == 0 || normalizedSourceId.Length == 0)
|
|
{
|
|
return Deny(ReputationApplyReasonCodes.InvalidSource);
|
|
}
|
|
|
|
var mutation = standingStore.TryApplyStandingDelta(normalizedPlayerId, normalizedFactionId, deltaAmount);
|
|
if (!mutation.Success)
|
|
{
|
|
return new ReputationApplyOutcome(
|
|
false,
|
|
mutation.ReasonCode,
|
|
mutation.PreviousStanding,
|
|
mutation.NewStanding,
|
|
null);
|
|
}
|
|
|
|
var appliedDelta = mutation.NewStanding - mutation.PreviousStanding;
|
|
var row = new ReputationDeltaRow(
|
|
normalizedDeltaId,
|
|
normalizedPlayerId,
|
|
normalizedFactionId,
|
|
appliedDelta,
|
|
mutation.NewStanding,
|
|
normalizedSourceKind,
|
|
normalizedSourceId,
|
|
timeProvider.GetUtcNow());
|
|
|
|
if (auditStore.TryAppend(row))
|
|
{
|
|
// --- Telemetry hook site (NEO-141): future E9.M1 catalog event `reputation_delta` ---
|
|
return new ReputationApplyOutcome(
|
|
true,
|
|
null,
|
|
mutation.PreviousStanding,
|
|
mutation.NewStanding,
|
|
row);
|
|
}
|
|
|
|
if (appliedDelta != 0)
|
|
{
|
|
_ = standingStore.TryApplyStandingDelta(normalizedPlayerId, normalizedFactionId, -appliedDelta);
|
|
}
|
|
|
|
return new ReputationApplyOutcome(
|
|
false,
|
|
ReputationApplyReasonCodes.AuditAppendFailed,
|
|
mutation.PreviousStanding,
|
|
mutation.PreviousStanding,
|
|
null);
|
|
}
|
|
|
|
private static ReputationApplyOutcome Deny(string reasonCode) =>
|
|
new(false, reasonCode, 0, 0, null);
|
|
}
|