feat(hs): grant fixed Zen to winning team at game end

This commit is contained in:
Acentech Dev
2026-07-21 00:34:08 +03:00
parent 83cb9b9fe4
commit 5f03a011db

View File

@@ -58,6 +58,19 @@ public class HeykelSavasiContext : MiniGameContext
/// </summary>
public const int StatuesToBreak = 7;
/// <summary>
/// The fixed amount of Zen awarded to each member of the winning team when the event ends.
/// </summary>
/// <remarks>
/// This is the authoritative definition of the reward amount. It is defined here (in GameLogic), not in
/// <c>Persistence.Initialization.VersionSeasonSix.Events.HeykelSavasiInitializer</c> where a same-named
/// constant currently also exists, because <c>MUnique.OpenMU.GameLogic</c> does not - and must not -
/// reference <c>MUnique.OpenMU.Persistence.Initialization</c> (the dependency only goes the other way;
/// same constraint that forced <see cref="StatuePositions"/> into this class). The initializer's constant
/// is unused by this class and will be reconciled/removed in a later cleanup pass.
/// </remarks>
public const int WinnerZenReward = 10_000_000;
/// <summary>
/// The <see cref="MUnique.OpenMU.DataModel.Configuration.MonsterDefinition.Number"/> of the destructible
/// statue NPC (see <c>HeykelSavasiMap.CreateMonsters</c>).
@@ -327,6 +340,49 @@ public class HeykelSavasiContext : MiniGameContext
await this.SpawnStatueAsync(HeykelSavasiTeam.Blue, 0).ConfigureAwait(false);
}
/// <inheritdoc />
/// <remarks>
/// Awards <see cref="WinnerZenReward"/> Zen to every member of the winning team (see
/// <see cref="GetWinner"/>); no reward is given on a draw (<see cref="HeykelSavasiTeam.None"/>). Mirrors
/// the exact fallback pattern the framework itself uses for money rewards
/// (<c>MiniGameContext.GiveRewardAsync</c>, MiniGameContext.cs:577-579): if a player's inventory can't
/// hold the money, <see cref="Player.TryAddMoney"/> returns <see langword="false"/> and the player is
/// notified via <see cref="Player.ShowLocalizedBlueMessageAsync"/>. Deliberately does not announce the
/// result via <c>MiniGameContext.ShowGoldenMessageAsync</c>: that overload treats its argument as a
/// localization resource key (see e.g. <c>BloodCastleContext.OnObjectRemovedFromMapAsync</c>'s
/// <c>nameof(PlayerMessage.BloodCastleCrystalStatusDestroyed)</c> usage), not literal text, so there is no
/// existing resource key for a Heykel Savasi win/draw announcement to pass here; adding one is out of
/// scope for this task. Does not warp players itself either: once the base call below returns, the
/// framework's own shutdown path calls <c>MovePlayersToSafezoneAsync</c>, which - because
/// <see cref="MiniGameContext.State"/> is no longer <see cref="MiniGameState.Playing"/> at that point -
/// resolves every participant's respawn gate to the map's configured <c>SafezoneMap</c> (Lorencia).
/// </remarks>
protected override async ValueTask GameEndedAsync(ICollection<Player> finishers)
{
var winner = this.GetWinner();
if (winner != HeykelSavasiTeam.None)
{
foreach (var player in this.PlayersOf(winner).ToList())
{
if (!player.TryAddMoney(WinnerZenReward))
{
await player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.AwardMoneyFailByFullInventory)).ConfigureAwait(false);
}
}
}
await base.GameEndedAsync(finishers).ConfigureAwait(false);
}
/// <summary>
/// Gets the players which would receive the winner's Zen reward if the event ended right now, for unit
/// tests only (a full <see cref="GameEndedAsync"/> run needs a live <see cref="Player"/> with a working
/// <see cref="Player.TryAddMoney"/>/inventory, which isn't practical to construct in a fast unit test).
/// </summary>
/// <returns>The winning team's players, or an empty sequence if the event hasn't been decided yet.</returns>
internal IEnumerable<Player> GetRewardTargetsForTest()
=> this.GetWinner() == HeykelSavasiTeam.None ? Enumerable.Empty<Player>() : this.PlayersOf(this.GetWinner());
/// <inheritdoc />
/// <remarks>
/// Note: <see cref="MiniGameContext.OnObjectAddedToMapAsync"/> already subscribes every