diff --git a/src/GameLogic/MiniGames/HeykelSavasiContext.cs b/src/GameLogic/MiniGames/HeykelSavasiContext.cs index ffe0578..2feb621 100644 --- a/src/GameLogic/MiniGames/HeykelSavasiContext.cs +++ b/src/GameLogic/MiniGames/HeykelSavasiContext.cs @@ -58,6 +58,19 @@ public class HeykelSavasiContext : MiniGameContext /// public const int StatuesToBreak = 7; + /// + /// The fixed amount of Zen awarded to each member of the winning team when the event ends. + /// + /// + /// This is the authoritative definition of the reward amount. It is defined here (in GameLogic), not in + /// Persistence.Initialization.VersionSeasonSix.Events.HeykelSavasiInitializer where a same-named + /// constant currently also exists, because MUnique.OpenMU.GameLogic does not - and must not - + /// reference MUnique.OpenMU.Persistence.Initialization (the dependency only goes the other way; + /// same constraint that forced into this class). The initializer's constant + /// is unused by this class and will be reconciled/removed in a later cleanup pass. + /// + public const int WinnerZenReward = 10_000_000; + /// /// The of the destructible /// statue NPC (see HeykelSavasiMap.CreateMonsters). @@ -327,6 +340,49 @@ public class HeykelSavasiContext : MiniGameContext await this.SpawnStatueAsync(HeykelSavasiTeam.Blue, 0).ConfigureAwait(false); } + /// + /// + /// Awards Zen to every member of the winning team (see + /// ); no reward is given on a draw (). Mirrors + /// the exact fallback pattern the framework itself uses for money rewards + /// (MiniGameContext.GiveRewardAsync, MiniGameContext.cs:577-579): if a player's inventory can't + /// hold the money, returns and the player is + /// notified via . Deliberately does not announce the + /// result via MiniGameContext.ShowGoldenMessageAsync: that overload treats its argument as a + /// localization resource key (see e.g. BloodCastleContext.OnObjectRemovedFromMapAsync's + /// nameof(PlayerMessage.BloodCastleCrystalStatusDestroyed) 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 MovePlayersToSafezoneAsync, which - because + /// is no longer at that point - + /// resolves every participant's respawn gate to the map's configured SafezoneMap (Lorencia). + /// + protected override async ValueTask GameEndedAsync(ICollection 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); + } + + /// + /// Gets the players which would receive the winner's Zen reward if the event ended right now, for unit + /// tests only (a full run needs a live with a working + /// /inventory, which isn't practical to construct in a fast unit test). + /// + /// The winning team's players, or an empty sequence if the event hasn't been decided yet. + internal IEnumerable GetRewardTargetsForTest() + => this.GetWinner() == HeykelSavasiTeam.None ? Enumerable.Empty() : this.PlayersOf(this.GetWinner()); + /// /// /// Note: already subscribes every