From b0ed42304a92da8f41645c2f083a574fcceca677 Mon Sep 17 00:00:00 2001 From: CommodoreChet <50426713+CommodoreChet@users.noreply.github.com> Date: Thu, 6 Feb 2025 19:51:53 -0600 Subject: [PATCH] KOTH go Brrrr --- .../GameModes/Elimination/EliminationHud.cs | 7 +- .../GameModes/KingOfTheHill/KOTHGamemode.cs | 106 ++++++++++++ .../SUGMA/GameModes/KingOfTheHill/KOTHHud.cs | 153 ++++++++++++++++++ .../Scripts/SUGMA/GameState/KOTHSphereZone.cs | 112 +++++++++++++ .../Scripts/SUGMA/SUGMA_SessionComponent.cs | 2 + 5 files changed, 377 insertions(+), 3 deletions(-) create mode 100644 Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameModes/KingOfTheHill/KOTHGamemode.cs create mode 100644 Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameModes/KingOfTheHill/KOTHHud.cs create mode 100644 Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameState/KOTHSphereZone.cs diff --git a/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameModes/Elimination/EliminationHud.cs b/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameModes/Elimination/EliminationHud.cs index 91f3fcbe0..ccdfb9277 100644 --- a/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameModes/Elimination/EliminationHud.cs +++ b/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameModes/Elimination/EliminationHud.cs @@ -59,7 +59,8 @@ internal class elmHud_Window : HudElementBase private bool _matchEnded; private readonly MatchTimer _timer; - private readonly LabelBox _timerLabel; + internal readonly LabelBox _timerLabel; + internal LabelBox _winnerLabel; public EliminationHud_TeamBanner[] Banners; public elmHud_Window(HudParentBase parent, EliminationGamemode gamemode) : base(parent) @@ -136,7 +137,7 @@ public void MatchEnded(IMyFaction winner) if (_timerLabel == null) return; - var winnerLabel = new LabelBox(_timerLabel) + _winnerLabel = new LabelBox(_timerLabel) { Text = winner != null ? $"A WINNER IS {winner.Name}. {winnerPoints} tickets remaining." @@ -147,7 +148,7 @@ public void MatchEnded(IMyFaction winner) Color = HudConstants.HudBackgroundColor }; - winnerLabel.TextBoard.SetFormatting(GlyphFormat.White.WithColor(Color.Red).WithSize(3) + _winnerLabel.TextBoard.SetFormatting(GlyphFormat.White.WithColor(Color.Red).WithSize(3) .WithAlignment(TextAlignment.Center)); } } diff --git a/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameModes/KingOfTheHill/KOTHGamemode.cs b/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameModes/KingOfTheHill/KOTHGamemode.cs new file mode 100644 index 000000000..1b2922307 --- /dev/null +++ b/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameModes/KingOfTheHill/KOTHGamemode.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using Sandbox.ModAPI; +using SC.SUGMA.GameModes.Elimination; +using SC.SUGMA.GameState; +using SC.SUGMA.Utilities; +using VRage.Game.ModAPI; +using VRageMath; + +namespace SC.SUGMA.GameModes.KOTH +{ + internal partial class KOTHGamemode : EliminationGamemode + { + public KOTHSphereZone ControlPoint; + + private Vector3D _controlPointPosition = new Vector3D(0, 0, 0); + private float _controlPointRadius = 2500f; + + private int ActivationTime = 300; + public int ActivationTimeCounter = 0; + + private int WinTime = 120; + + public override string ReadableName { get; internal set; } = "King Of The Hill"; + + public override string Description { get; internal set; } = + "Factions fight over a Central Capture Point. Either spend 2 minutes uncontested in the center zone, or eliminate the enemy team to win."; + + public KOTHGamemode() + { + ArgumentParser += new ArgumentParser( + new ArgumentParser.ArgumentDefinition( + text => ActivationTime = int.Parse(text), + "at", "activation-time", + $"Delay before Center Zone is Capturable, in Seconds" + ), + new ArgumentParser.ArgumentDefinition( + text => WinTime = int.Parse(text), + "wt", "win-time", + "How long a team has to hold the zone uncontested to win, in Seconds" + ) + ); + } + + public override void StartRound(string[] arguments = null) + { + base.StartRound(arguments); + + if (TrackedFactions.Count <= 1) + return; + + ActivationTimeCounter = ActivationTime; + ControlPoint = null; + + if (!MyAPIGateway.Utilities.IsDedicated) + SUGMA_SessionComponent.I.RegisterComponent("KOTHHud", new KOTHHud(this)); + } + + public override void StopRound() + { + base.StopRound(); + + SUGMA_SessionComponent.I.GetComponent("KOTHHud")?.MatchEnded(ControlPoint._zoneOwner); + SUGMA_SessionComponent.I.UnregisterComponent("KOTHHud"); + + ControlPoint = null; + SUGMA_SessionComponent.I.UnregisterComponent("KOTHZone"); + } + + internal override void DisplayWinMessage() + { + if (ControlPoint._zoneOwner == null) + { + MyAPIGateway.Utilities.ShowNotification("YOU ARE ALL LOSERS.", 10000, "Red"); + return; + } + + MyAPIGateway.Utilities.ShowNotification($"A WINNER IS [{ControlPoint._zoneOwner?.Name}]!", 10000); + } + + public override void UpdateActive() + { + if (ActivationTimeCounter > 0) + { + if (_matchTimer.Ticks % 60 == 0) + { + ActivationTimeCounter--; + + if (ActivationTimeCounter <= 0) + { + ControlPoint = new KOTHSphereZone(_controlPointPosition, _controlPointRadius, WinTime); + SUGMA_SessionComponent.I.RegisterComponent("KOTHZone", ControlPoint); + } + } + } + + if (ControlPoint == null) + return; + + if (ControlPoint.IsCaptured) + { + StopRound(); + } + } + } +} \ No newline at end of file diff --git a/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameModes/KingOfTheHill/KOTHHud.cs b/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameModes/KingOfTheHill/KOTHHud.cs new file mode 100644 index 000000000..3dd313484 --- /dev/null +++ b/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameModes/KingOfTheHill/KOTHHud.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using RichHudFramework; +using RichHudFramework.Client; +using RichHudFramework.UI; +using RichHudFramework.UI.Client; +using RichHudFramework.UI.Rendering; +using SC.SUGMA.GameModes.Elimination; +using SC.SUGMA.GameState; +using SC.SUGMA.Utilities; +using VRage.Game.ModAPI; +using VRage.Utils; +using VRageMath; + +namespace SC.SUGMA.GameModes.KOTH +{ + internal class KOTHHud : ComponentBase + { + private readonly KOTHGamemode _gamemode; + private KOTHHud_Window _window; + + public KOTHHud(KOTHGamemode gamemode) + { + _gamemode = gamemode; + } + + public override void Init(string id) + { + base.Init(id); + + if (!RichHudClient.Registered) + throw new Exception("RichHudAPI was not initialized in time!"); + + _window = new KOTHHud_Window(HudMain.HighDpiRoot, _gamemode); + } + + public override void Close() + { + HudMain.HighDpiRoot.RemoveChild(_window); + } + + public override void UpdateTick() + { + if (SUGMA_SessionComponent.I.CurrentGamemode != null) + _window.Update(); + } + + public void MatchEnded(IMyFaction winner) + { + _window.MatchEnded(winner); + } + } + + internal class KOTHHud_Window : WindowBase + { + private static readonly Material _circleMaterial = + new Material(MyStringId.GetOrCompute("SugmaCircle"), new Vector2(32, 32)); + + private readonly KOTHGamemode _gamemode; + private readonly elmHud_Window _windowBase; + + private TexturedBox _captureIndicator; + + private Label _captureLabel; + + public KOTHHud_Window(HudParentBase parent, KOTHGamemode gamemode) : base(parent) + { + _gamemode = gamemode; + _windowBase = SUGMA_SessionComponent.I.GetComponent("elmHud").Window; + + _captureIndicator = new TexturedBox(_windowBase) + { + Material = _circleMaterial, + ParentAlignment = ParentAlignments.Bottom | ParentAlignments.Center, + Size = Vector2.One * 64, + Offset = new Vector2(0, -15), + ZOffset = sbyte.MaxValue + }; + + _captureLabel = new Label(_captureIndicator) + { + ParentAlignment = ParentAlignments.Center, + Offset = new Vector2(0, -55), + Text = "Initializing KOTH..." + }; + + foreach (var banner in _windowBase.Banners) + { + banner.Visible = false; + } + } + + public void Update() + { + if (_gamemode.ControlPoint == null) + { + int timeLeft = Math.Max(0, _gamemode.ActivationTimeCounter); + if (timeLeft > 0) + { + _captureIndicator.Color = Color.White; + _captureLabel.Text = $"Zone Locked: {timeLeft}s"; + } + else + { + _captureIndicator.Color = Color.White; + _captureLabel.Text = "Waiting for zone creation..."; + } + return; + } + + var zone = _gamemode.ControlPoint; + + bool isActivelyCapturing = zone.ActiveCapturingFaction != null && zone.CaptureTimeCurrent > 0f; + Color capturingColor = isActivelyCapturing + ? zone.ActiveCapturingFaction.CustomColor.ColorMaskToRgb() + : Color.White; + + _captureIndicator.Color = capturingColor.SetAlphaPct(0.5f); + + float current = zone.CaptureTimeCurrent; + float total = zone.CaptureTime; + + if (!isActivelyCapturing && current > 0f) + { + _captureIndicator.Color = Color.White.SetAlphaPct(0.5f); + } + + _captureLabel.Text = $"Capturing: {current:0.0}s / {total:0.0}s"; + } + + public void MatchEnded(IMyFaction winner) + { + _captureIndicator.Visible = false; + _captureLabel.Visible = false; + + _windowBase._winnerLabel.Visible = false; + _windowBase._winnerLabel = new LabelBox(_windowBase._timerLabel) + { + Text = winner != null + ? $"A WINNER IS {winner.Name}" + : "YOU ARE ALL LOSERS", + ParentAlignment = ParentAlignments.Bottom, + Height = EliminationHud_TeamBanner.BaseHeight, + TextPadding = new Vector2(2.5f, 0), + Color = HudConstants.HudBackgroundColor + }; + _windowBase._winnerLabel.Visible = true; + + _windowBase._winnerLabel.TextBoard.SetFormatting(GlyphFormat.White.WithColor(Color.Red).WithSize(3) + .WithAlignment(TextAlignment.Center)); + } + } +} \ No newline at end of file diff --git a/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameState/KOTHSphereZone.cs b/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameState/KOTHSphereZone.cs new file mode 100644 index 000000000..fe7e3fb0b --- /dev/null +++ b/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/GameState/KOTHSphereZone.cs @@ -0,0 +1,112 @@ +using RichHudFramework; +using Sandbox.Game; +using Sandbox.Game.Entities; +using SC.SUGMA.Utilities; +using VRage.Game.ModAPI; +using VRageMath; +using System; +using System.Linq; +using System.Collections.Generic; + +namespace SC.SUGMA.GameState +{ + public class KOTHSphereZone : SphereZone + { + public IMyFaction _zoneOwner; + private Color _baseColor = Color.White.SetAlphaPct(0.25f); + + public bool IsCaptured = false; + public float CaptureTime; + public float CaptureTimeCurrent; + public MySoundPair CaptureSound = new MySoundPair("SUGMA_CaptureSound_TF2"); + + public IMyFaction ActiveCapturingFaction; + + public KOTHSphereZone(Vector3D center, double radius, float captureTime, IMyFaction initialOwner = null) + : base(center, radius) + { + _zoneOwner = initialOwner; + CaptureTime = captureTime; + + SphereDrawColor = (_zoneOwner?.CustomColor.ColorMaskToRgb() ?? Color.White).SetAlphaPct(0.25f); + _baseColor = SphereDrawColor; + } + + public override void UpdateTick() + { + GridFilter = SUGMA_SessionComponent.I.ShareTrackApi.GetTrackedGrids(); + base.UpdateTick(); + + var distinctFactions = new HashSet(); + foreach (var grid in ContainedGrids) + { + var faction = grid.GetFaction(); + if (faction != null) + distinctFactions.Add(faction); + } + + if (distinctFactions.Count == 0) + { + CaptureTimeCurrent = MathHelper.Max(0f, CaptureTimeCurrent - (1f / 120f)); + if (CaptureTimeCurrent <= 0f) + ActiveCapturingFaction = null; + } + else if (distinctFactions.Count > 1) + { + CaptureTimeCurrent = MathHelper.Max(0f, CaptureTimeCurrent - (1f / 120f)); + if (CaptureTimeCurrent <= 0f) + ActiveCapturingFaction = null; + } + else + { + var occupant = distinctFactions.First(); + + if (occupant == _zoneOwner) + { + CaptureTimeCurrent = 0f; + ActiveCapturingFaction = null; + } + else + { + if (ActiveCapturingFaction == null) + { + ActiveCapturingFaction = occupant; + } + else if (ActiveCapturingFaction != occupant) + { + CaptureTimeCurrent = MathHelper.Max(0f, CaptureTimeCurrent - (1f / 60f)); + + if (CaptureTimeCurrent <= 0f) + { + ActiveCapturingFaction = occupant; + CaptureTimeCurrent = 0f; + } + } + + if (ActiveCapturingFaction == occupant) + { + CaptureTimeCurrent += (1f / 60f); + + if (CaptureTimeCurrent >= CaptureTime) + { + _zoneOwner = ActiveCapturingFaction; + CaptureTimeCurrent = 0f; + ActiveCapturingFaction = null; + OnCapture(); + } + } + } + } + + float lerpAmount = (CaptureTime <= 0f ? 0f : CaptureTimeCurrent / CaptureTime); + Color capturingColor = ActiveCapturingFaction?.CustomColor.ColorMaskToRgb() ?? Color.White; + SphereDrawColor = Color.Lerp(_baseColor, capturingColor, lerpAmount).SetAlphaPct(0.25f); + } + + public virtual void OnCapture() + { + IsCaptured = true; + SUtils.PlaySound(CaptureSound); + } + } +} diff --git a/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/SUGMA_SessionComponent.cs b/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/SUGMA_SessionComponent.cs index bc51ba722..b237979cb 100644 --- a/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/SUGMA_SessionComponent.cs +++ b/Gamemode Mods/StarCore SUGMA Gamemodes/Data/Scripts/SUGMA/SUGMA_SessionComponent.cs @@ -10,6 +10,7 @@ using SC.SUGMA.GameModes.Domination; using SC.SUGMA.GameModes.Elimination; using SC.SUGMA.GameModes.TeamDeathmatch; +using SC.SUGMA.GameModes.KOTH; using SC.SUGMA.GameState; using SC.SUGMA.HeartNetworking; using SC.SUGMA.HeartNetworking.Custom; @@ -33,6 +34,7 @@ internal class SUGMA_SessionComponent : MySessionComponentBase ["elm"] = new EliminationGamemode(), ["dom"] = new DominationGamemode(), ["tdm"] = new TeamDeathmatchGamemode(), + ["koth"] = new KOTHGamemode(), }; ///