Skip to content

Commit

Permalink
Added new Flags & Tickets system + new UIs
Browse files Browse the repository at this point in the history
  • Loading branch information
benvalkin committed Nov 23, 2024
1 parent 17c1788 commit fcc5c22
Show file tree
Hide file tree
Showing 40 changed files with 1,077 additions and 269 deletions.
18 changes: 17 additions & 1 deletion UncreatedWarfare.Tests/TeamScoreTableTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ private void AssertTotalSameAsStartingValue(TeamScoreTable tests)

ttl += tests[null];

Assert.That(ttl, Is.EqualTo(tests.TotalScore).Within(Threshold));
Assert.That(ttl, Is.EqualTo(tests.MaxScore).Within(Threshold));
}

[Test]
Expand Down Expand Up @@ -242,4 +242,20 @@ public void TestRemoveToNeutral()

AssertTotalSameAsStartingValue(tests);
}
[Test]
public void HelpHelpMe()
{
TeamScoreTable score = new TeamScoreTable(CreateTeamList(2), 64);

Team team1 = score.Teams[0];
Team team2 = score.Teams[1];

score.MaximizeTeam(team1);
Console.WriteLine(score.ToGraph(true));
Console.WriteLine(score);

score.IncrementPoints(team2, 32);
Console.WriteLine(score.ToGraph(true));
Console.WriteLine(score);
}
}
2 changes: 1 addition & 1 deletion UncreatedWarfare/Commands/_DebugCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ private void captureui()

Context.AssertRanByPlayer();

CaptureUI captureUi = _serviceProvider.GetRequiredService<CaptureUI>();
CaptureHUD captureUi = _serviceProvider.GetRequiredService<CaptureHUD>();

if (Context.MatchParameter(0, "clear"))
{
Expand Down
24 changes: 24 additions & 0 deletions UncreatedWarfare/Events/Models/Flags/FlagCaptured.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
using Uncreated.Warfare.Fobs;
using Uncreated.Warfare.Layouts.Phases.Flags;
using Uncreated.Warfare.Layouts.Teams;
using Uncreated.Warfare.Players;

namespace Uncreated.Warfare.Events.Models.Flags;
/// <summary>
/// Event listener args which fires after a <see cref="FlagObjective"/> is captured. "Captured" means that a flag's <see cref="FlagObjective.Owner"/> changed to
/// a winning <see cref="Team"/> after they won the flag contest of a neutral flag.
/// </summary>
public class FlagCaptured
{
/// <summary>
/// The flag that was captured.
/// </summary>
public required FlagObjective Flag { get; init; }
/// <summary>
/// The team that captured the flag by means of leading the flag contest.
/// </summary>
public required Team Capturer { get; init; }
}
23 changes: 23 additions & 0 deletions UncreatedWarfare/Events/Models/Flags/FlagContestPointsChanged.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;
using Uncreated.Warfare.Fobs;
using Uncreated.Warfare.Layouts.Phases.Flags;
using Uncreated.Warfare.Layouts.Teams;
using Uncreated.Warfare.Players;

namespace Uncreated.Warfare.Events.Models.Flags;
/// <summary>
/// Event listener args which fires after a flag's <see cref="FlagObjective.Contest"/>'s points change are altered.
/// </summary>
internal class FlagContestPointsChanged
{
/// <summary>
/// The flag that is being contested.
/// </summary>
public required FlagObjective Flag { get; init; }
/// <summary>
/// The change in contest points.
/// </summary>
public required int PointsChange { get; init; }
}
26 changes: 26 additions & 0 deletions UncreatedWarfare/Events/Models/Flags/FlagNeutralized.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using Uncreated.Warfare.Fobs;
using Uncreated.Warfare.Layouts.Phases.Flags;
using Uncreated.Warfare.Layouts.Teams;
using Uncreated.Warfare.Players;

namespace Uncreated.Warfare.Events.Models.Flags;
/// <summary>
/// Event listener args which fires after a <see cref="FlagObjective"/> is neutralized.
/// "Neutralized" means one team successfully reduced the former owner team's contest points to zero,
/// causing <see cref="FlagObjective.Owner"/> to become neutral (<see cref="Team.NoTeam"/>).
/// </summary>
internal class FlagNeutralized
{
/// <summary>
/// The flag that was neutralized.
/// </summary>
public required FlagObjective Flag { get; init; }
/// <summary>
/// The team that neutralized the flag by means of leading the flag contest. This team is not the new <see cref="FlagObjective.Owner"/> of the flag,
/// but rather simply the team that caused it's owner to change.
/// </summary>
public required Team Neutralizer { get; init; }
}
18 changes: 18 additions & 0 deletions UncreatedWarfare/Events/Models/Flags/PlayerEnteredFlagRegion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
using Uncreated.Warfare.Fobs;
using Uncreated.Warfare.Layouts.Phases.Flags;
using Uncreated.Warfare.Players;

namespace Uncreated.Warfare.Events.Models.Flags;
/// <summary>
/// Event listener args which fires after <see cref="WarfarePlayer"/> enteres the region of a <see cref="FlagObjective"/>.
/// </summary>
internal class PlayerEnteredFlagRegion : PlayerEvent
{
/// <summary>
/// The flag that the player entered.
/// </summary>
public required FlagObjective Flag { get; init; }
}
18 changes: 18 additions & 0 deletions UncreatedWarfare/Events/Models/Flags/PlayerExitedFlagRegion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
using Uncreated.Warfare.Fobs;
using Uncreated.Warfare.Layouts.Phases.Flags;
using Uncreated.Warfare.Players;

namespace Uncreated.Warfare.Events.Models.Flags;
/// <summary>
/// Event listener args which fires after <see cref="WarfarePlayer"/> exits the region of a <see cref="FlagObjective"/>.
/// </summary>
internal class PlayerExitedFlagRegion : PlayerEvent
{
/// <summary>
/// The flag that the player exited.
/// </summary>
public required FlagObjective Flag { get; init; }
}
29 changes: 29 additions & 0 deletions UncreatedWarfare/Events/Models/TicketsChanged.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Text;
using Uncreated.Warfare.Layouts.Teams;
using Uncreated.Warfare.Layouts.Tickets;

namespace Uncreated.Warfare.Events.Models;
/// <summary>
/// Event listener args which fires after a a certain <see cref="Layouts.Teams.Team"/> gains or loses tickets.
/// </summary>
internal class TicketsChanged
{
/// <summary>
/// The new number of tickets belong to <see cref="Team"/>.
/// </summary>
public required int NewNumber { get; init; }
/// <summary>
/// The team's change in tickets. A positive indicates a gain, while a negative value indicates a loss.
/// </summary>
public required int Change { get; init; }
/// <summary>
/// The <see cref="Layouts.Teams.Team"/> that gained or lost tickets.
/// </summary>
public required Team Team { get; init; }
/// <summary>
/// The instance of <see cref="ITicketTracker"/> that was resonsible for orchestrating this change.
/// </summary>
public required ITicketTracker TicketTracker { get; init; }
}
2 changes: 1 addition & 1 deletion UncreatedWarfare/FOBs/FOBManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ UniTask ILayoutHostedService.StopAsync(CancellationToken token)
public BuildableFob RegisterFob(IBuildable fobBuildable)
{
GridLocation griddy = new GridLocation(fobBuildable.Position);
string fobName = $"FOB {NATOPhoneticAlphabetHelper.GetProperCase(griddy.LetterX)}-{griddy.Y + 1}";
string fobName = $"{NATOPhoneticAlphabetHelper.GetProperCase(griddy.LetterX)}-{griddy.Y + 1}";

BuildableFob fob = new BuildableFob(_serviceProvider, fobName, fobBuildable);
_fobs.Add(fob);
Expand Down
143 changes: 143 additions & 0 deletions UncreatedWarfare/Layouts/Flags/BaseFlagService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Uncreated.Warfare.Exceptions;
using Uncreated.Warfare.Layouts.Phases.Flags;
using Uncreated.Warfare.Layouts.Teams;
using Uncreated.Warfare.Util;
using Uncreated.Warfare.Zones.Pathing;
using Uncreated.Warfare.Zones;
using Uncreated.Warfare.Services;

namespace Uncreated.Warfare.Layouts.Flags;
public abstract class BaseFlagService : ILayoutHostedService, IFlagRotationService
{
protected readonly ILogger Logger;
protected readonly IServiceProvider ServiceProvider;
protected readonly ITeamManager<Team> TeamManager;
protected IList<Zone>? PathingResult { get; private set; }
protected ZoneStore ZoneStore { get; private set; }
protected FlagPhaseSettings FlagSettings { get; private set; }

/// <summary>
/// Array of all zones in order *including the main bases* at the beginning and end of the list.
/// </summary>
private FlagObjective[]? _flags;

public bool IsActive { get; private set; }

/// <inheritdoc />
public IConfiguration Configuration { get; }

/// <inheritdoc />
public IReadOnlyList<FlagObjective> ActiveFlags { get; private set; } = Array.Empty<FlagObjective>();

/// <inheritdoc />
public ZoneRegion StartingTeam { get; private set; }

/// <inheritdoc />
public ZoneRegion EndingTeam { get; private set; }

public BaseFlagService(IServiceProvider serviceProvider, IConfiguration config)
{
Logger = serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger(GetType());
ServiceProvider = serviceProvider;
TeamManager = serviceProvider.GetRequiredService<ITeamManager<Team>>();
FlagSettings = new FlagPhaseSettings();
Configuration = config;
}

public virtual async UniTask StartAsync(CancellationToken token)
{
Configuration.Bind(FlagSettings);

await CreateZonePaths(token);
await UniTask.SwitchToMainThread(token);
SetupFlags();
}

public virtual UniTask StopAsync(CancellationToken token)
{
foreach (FlagObjective flag in ActiveFlags)
{
flag.Dispose();
}

_flags = null;
ActiveFlags = Array.Empty<FlagObjective>();
IsActive = false;

return UniTask.CompletedTask;
}
private async UniTask CreateZonePaths(CancellationToken token)
{
if (!ContextualTypeResolver.TryResolveType(FlagSettings.Pathing, out Type? pathingProviderType, typeof(IZonePathingProvider)))
{
Logger.LogError("Unknown or invalid pathing provider type: {0}.", FlagSettings.Pathing);

throw new LayoutConfigurationException("Invalid pathing provider type.");
}

// create zone providers from config
IReadOnlyList<Type> zoneProviderTypes = FlagSettings.GetFlagPoolTypes(Logger);

List<IZoneProvider> zoneProviders = new List<IZoneProvider>(zoneProviderTypes.Count);
foreach (Type zoneProviderType in zoneProviderTypes)
{
zoneProviders.Add((IZoneProvider)ReflectionUtility.CreateInstanceFixed(ServiceProvider, zoneProviderType, [this]));
}

ZoneStore = ActivatorUtilities.CreateInstance<ZoneStore>(ServiceProvider, [zoneProviders, false]);

await ZoneStore.Initialize(token);

// load pathing provider
IConfigurationSection config = Configuration.GetSection("PathingData");
IZonePathingProvider pathingProvider = (IZonePathingProvider)ReflectionUtility.CreateInstanceFixed(ServiceProvider, pathingProviderType, [ZoneStore, this, config]);

config.Bind(pathingProvider);

// create zone path
PathingResult = await pathingProvider.CreateZonePathAsync(token);

Logger.LogInformation("Zone path: {{{0}}}.", string.Join(" -> ", PathingResult.Skip(1).SkipLast(1).Select(zone => zone.Name)));
}
private void SetupFlags()
{
if (PathingResult == null || ZoneStore == null)
{
throw new LayoutConfigurationException("Unable to create zone path.");
}

// create zones as objects with colliders

List<FlagObjective> flagList = new List<FlagObjective>(PathingResult.Count);
foreach (Zone zone in PathingResult)
{
ZoneProximity[] zones = ZoneStore.Zones
.Where(z => z.Name.Equals(zone.Name, StringComparison.Ordinal))
.Select(z => new ZoneProximity(ZoneStore.CreateColliderForZone(z), z))
.ToArray();

ZoneRegion region = new ZoneRegion(zones, TeamManager);
flagList.Add(new FlagObjective(region, TeamManager, Team.NoTeam));
}

_flags = flagList.ToArrayFast();
if (_flags.Length < 3)
{
throw new LayoutConfigurationException("Unable to create zone path longer than one zone (not including main bases).");
}

ActiveFlags = new ReadOnlyCollection<FlagObjective>(new ArraySegment<FlagObjective>(_flags, 1, _flags.Length - 2));
StartingTeam = _flags[0].Region;
EndingTeam = _flags[^1].Region;
IsActive = true;
}

public abstract FlagObjective? GetObjective(Team team);
}
Loading

0 comments on commit fcc5c22

Please sign in to comment.