144 lines
6.1 KiB
C#
144 lines
6.1 KiB
C#
// <copyright file="HeykelSavasiContext.cs" company="MUnique">
|
|
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
|
// </copyright>
|
|
|
|
namespace MUnique.OpenMU.GameLogic.MiniGames;
|
|
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using MUnique.OpenMU.DataModel.Configuration;
|
|
|
|
/// <summary>
|
|
/// The context of the Heykel Savasi (statue war) event.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This is the foundation class for the event: it tracks which team every participating player
|
|
/// belongs to, and enforces a team-balance rule when players attempt to join a team.
|
|
/// </remarks>
|
|
public class HeykelSavasiContext : MiniGameContext
|
|
{
|
|
/// <summary>
|
|
/// The maximum allowed difference between the red and blue team sizes after a join.
|
|
/// </summary>
|
|
private const int MaxTeamDifference = 2;
|
|
|
|
/// <summary>
|
|
/// The X1/Y1 anchor of the red team's base spawn gate.
|
|
/// Must match Gates.cs targetGates 600 (RedBase).
|
|
/// </summary>
|
|
private const byte RedBaseAnchorX1 = 20;
|
|
|
|
/// <summary>
|
|
/// The X1/Y1 anchor of the red team's base spawn gate.
|
|
/// Must match Gates.cs targetGates 600 (RedBase).
|
|
/// </summary>
|
|
private const byte RedBaseAnchorY1 = 20;
|
|
|
|
/// <summary>
|
|
/// The X1/Y1 anchor of the blue team's base spawn gate.
|
|
/// Must match Gates.cs targetGates 601 (BlueBase).
|
|
/// </summary>
|
|
private const byte BlueBaseAnchorX1 = 220;
|
|
|
|
/// <summary>
|
|
/// The X1/Y1 anchor of the blue team's base spawn gate.
|
|
/// Must match Gates.cs targetGates 601 (BlueBase).
|
|
/// </summary>
|
|
private const byte BlueBaseAnchorY1 = 220;
|
|
|
|
private readonly ConcurrentDictionary<Player, HeykelSavasiTeam> _teams = new();
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="HeykelSavasiContext"/> class.
|
|
/// </summary>
|
|
/// <param name="key">The key of this context.</param>
|
|
/// <param name="definition">The definition of the mini game.</param>
|
|
/// <param name="gameContext">The game context, to which this game belongs.</param>
|
|
/// <param name="mapInitializer">The map initializer, which is used when the event starts.</param>
|
|
public HeykelSavasiContext(MiniGameMapKey key, MiniGameDefinition definition, IGameContext gameContext, IMapInitializer mapInitializer)
|
|
: base(key, definition, gameContext, mapInitializer)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the current number of players assigned to the given team.
|
|
/// </summary>
|
|
/// <param name="team">The team.</param>
|
|
/// <returns>The number of players currently assigned to <paramref name="team"/>.</returns>
|
|
public int TeamCount(HeykelSavasiTeam team) => this._teams.Values.Count(t => t == team);
|
|
|
|
/// <summary>
|
|
/// Determines whether a player may currently join the given team, based on the team-balance rule.
|
|
/// </summary>
|
|
/// <param name="team">The team which a player wants to join.</param>
|
|
/// <returns><c>true</c> if the join is allowed; otherwise, <c>false</c>.</returns>
|
|
public bool CanJoin(HeykelSavasiTeam team) => IsJoinAllowed(this.TeamCount(HeykelSavasiTeam.Red), this.TeamCount(HeykelSavasiTeam.Blue), team);
|
|
|
|
/// <summary>
|
|
/// Assigns the given player to the given team.
|
|
/// </summary>
|
|
/// <param name="player">The player.</param>
|
|
/// <param name="team">The team.</param>
|
|
public void AssignTeam(Player player, HeykelSavasiTeam team) => this._teams[player] = team;
|
|
|
|
/// <summary>
|
|
/// Gets the team of the given player.
|
|
/// </summary>
|
|
/// <param name="player">The player.</param>
|
|
/// <returns>The team of the player, or <see cref="HeykelSavasiTeam.None"/> if the player is not assigned to a team.</returns>
|
|
public HeykelSavasiTeam GetTeam(Player player) => this._teams.TryGetValue(player, out var team) ? team : HeykelSavasiTeam.None;
|
|
|
|
/// <summary>
|
|
/// Gets the base spawn gate for the given team.
|
|
/// </summary>
|
|
/// <param name="team">The team.</param>
|
|
/// <returns>The <see cref="ExitGate"/> which is the base spawn point of <paramref name="team"/>.</returns>
|
|
/// <remarks>
|
|
/// The gate is resolved by matching the X1/Y1 anchor of the gates created for this event's map
|
|
/// (see Gates.cs targetGates 600/601, added in Task 0.3). Red anchors at (20, 20), Blue at (220, 220).
|
|
/// </remarks>
|
|
public ExitGate GetTeamSpawnGate(HeykelSavasiTeam team)
|
|
{
|
|
var (anchorX1, anchorY1) = team == HeykelSavasiTeam.Blue
|
|
? (BlueBaseAnchorX1, BlueBaseAnchorY1)
|
|
: (RedBaseAnchorX1, RedBaseAnchorY1);
|
|
|
|
return this.Definition.Entrance!.Map!.ExitGates.First(g => g.X1 == anchorX1 && g.Y1 == anchorY1);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all players which are currently assigned to the given team.
|
|
/// </summary>
|
|
/// <param name="team">The team.</param>
|
|
/// <returns>All players assigned to <paramref name="team"/>.</returns>
|
|
public IEnumerable<Player> PlayersOf(HeykelSavasiTeam team) => this._teams.Where(kv => kv.Value == team).Select(kv => kv.Key);
|
|
|
|
/// <summary>
|
|
/// Determines, in a pure way, whether joining <paramref name="team"/> is allowed given the current
|
|
/// team counts, according to the balance rule: the absolute difference between the red and blue
|
|
/// team sizes must not exceed <see cref="MaxTeamDifference"/> after the prospective join.
|
|
/// </summary>
|
|
/// <param name="redCount">The current number of players in the red team.</param>
|
|
/// <param name="blueCount">The current number of players in the blue team.</param>
|
|
/// <param name="team">The team which a player wants to join.</param>
|
|
/// <returns><c>true</c> if the join is allowed; otherwise, <c>false</c>.</returns>
|
|
internal static bool IsJoinAllowed(int redCount, int blueCount, HeykelSavasiTeam team)
|
|
{
|
|
switch (team)
|
|
{
|
|
case HeykelSavasiTeam.Red:
|
|
redCount++;
|
|
break;
|
|
case HeykelSavasiTeam.Blue:
|
|
blueCount++;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return Math.Abs(redCount - blueCount) <= MaxTeamDifference;
|
|
}
|
|
}
|