diff --git a/Jailbreak.sln b/Jailbreak.sln index 9c873d4f..698517b8 100644 --- a/Jailbreak.sln +++ b/Jailbreak.sln @@ -40,6 +40,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jailbreak.SpecialDay", "mod EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jailbreak.Zones", "mod\Jailbreak.Zones\Jailbreak.Zones.csproj", "{C93A626A-BB44-4309-8DAD-4B28B2941870}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jailbreak.Trail", "mod\Jailbreak.Trail\Jailbreak.Trail.csproj", "{91F4EC7A-993A-4CA0-84C3-9F1100124A9C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -110,6 +112,10 @@ Global {C93A626A-BB44-4309-8DAD-4B28B2941870}.Debug|Any CPU.Build.0 = Debug|Any CPU {C93A626A-BB44-4309-8DAD-4B28B2941870}.Release|Any CPU.ActiveCfg = Release|Any CPU {C93A626A-BB44-4309-8DAD-4B28B2941870}.Release|Any CPU.Build.0 = Release|Any CPU + {91F4EC7A-993A-4CA0-84C3-9F1100124A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91F4EC7A-993A-4CA0-84C3-9F1100124A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91F4EC7A-993A-4CA0-84C3-9F1100124A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91F4EC7A-993A-4CA0-84C3-9F1100124A9C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {9135CCC9-66C5-4A9C-AE3C-91475B5F0437} = {177DA48D-8306-4102-918D-992569878581} @@ -128,5 +134,6 @@ Global {79B7496B-116D-4B87-BBBE-46DD19A9BFEB} = {59311734-3648-43C2-B43C-385718B0D103} {A6249693-5B7E-4E14-A675-C292914F10F3} = {36BA84C0-291C-4930-A7C6-97CDF8F7F0D7} {C93A626A-BB44-4309-8DAD-4B28B2941870} = {36BA84C0-291C-4930-A7C6-97CDF8F7F0D7} + {91F4EC7A-993A-4CA0-84C3-9F1100124A9C} = {36BA84C0-291C-4930-A7C6-97CDF8F7F0D7} EndGlobalSection EndGlobal diff --git a/lang/Jailbreak.English/Generic/GenericCommandNotifications.cs b/lang/Jailbreak.English/Generic/GenericCmdLocale.cs similarity index 89% rename from lang/Jailbreak.English/Generic/GenericCommandNotifications.cs rename to lang/Jailbreak.English/Generic/GenericCmdLocale.cs index 2bcad41c..27229240 100644 --- a/lang/Jailbreak.English/Generic/GenericCommandNotifications.cs +++ b/lang/Jailbreak.English/Generic/GenericCmdLocale.cs @@ -7,7 +7,7 @@ namespace Jailbreak.English.Generic; -public class GenericCommandNotifications : IGenericCommandNotifications, +public class GenericCmdLocale : IGenericCmdLocale, ILanguage { private static readonly FormatObject PREFIX = new HiddenFormatObject( @@ -53,4 +53,10 @@ public IView NoPermissionMessage(string permission) { $"{ChatColors.Red}This command requires the {ChatColors.White}{permission}{ChatColors.Red} permission." }; } + + public IView Error(string message) { + return new SimpleView { + PREFIX, $"{ChatColors.Red}An error occurred: {ChatColors.White}{message}" + }; + } } \ No newline at end of file diff --git a/lang/Jailbreak.English/LastGuard/LastGuardNotifications.cs b/lang/Jailbreak.English/LastGuard/LGLocale.cs similarity index 90% rename from lang/Jailbreak.English/LastGuard/LastGuardNotifications.cs rename to lang/Jailbreak.English/LastGuard/LGLocale.cs index 23c47d30..7c7d2e3e 100644 --- a/lang/Jailbreak.English/LastGuard/LastGuardNotifications.cs +++ b/lang/Jailbreak.English/LastGuard/LGLocale.cs @@ -8,8 +8,7 @@ namespace Jailbreak.English.LastGuard; -public class LastGuardNotifications : ILastGuardNotifications, - ILanguage { +public class LGLocale : ILGLocale, ILanguage { private static readonly FormatObject PREFIX = new HiddenFormatObject( $" {ChatColors.DarkRed}[{ChatColors.LightRed}Last Guard{ChatColors.DarkRed}]") { diff --git a/lang/Jailbreak.English/LastRequest/B4BLocale.cs b/lang/Jailbreak.English/LastRequest/B4BLocale.cs new file mode 100644 index 00000000..94b73493 --- /dev/null +++ b/lang/Jailbreak.English/LastRequest/B4BLocale.cs @@ -0,0 +1,17 @@ +using CounterStrikeSharp.API.Core; +using Jailbreak.Formatting.Base; +using Jailbreak.Formatting.Views.LastRequest; + +namespace Jailbreak.English.LastRequest; + +public class B4BLocale : LastRequestLocale, ILRB4BLocale { + public IView PlayerGoesFirst(CCSPlayerController player) { + return new SimpleView { + PREFIX, "Randomly selected", player, "to go first." + }; + } + + public IView WeaponSelected(CCSPlayerController player, string weapon) { + return new SimpleView { PREFIX, player, "picked", weapon }; + } +} \ No newline at end of file diff --git a/lang/Jailbreak.English/LastRequest/CoinflipLocale.cs b/lang/Jailbreak.English/LastRequest/CoinflipLocale.cs new file mode 100644 index 00000000..fae98ba1 --- /dev/null +++ b/lang/Jailbreak.English/LastRequest/CoinflipLocale.cs @@ -0,0 +1,40 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.Formatting.Base; +using Jailbreak.Formatting.Views.LastRequest; + +namespace Jailbreak.English.LastRequest; + +public class CoinflipLocale : LastRequestLocale, ILRCFLocale { + public IView FailedToChooseInTime(bool choice) { + return new SimpleView { + PREFIX, + "You failed to choose in time, defaulting to " + ChatColors.Green, + choice ? "Heads" : "Tails" + }; + } + + public IView GuardChose(CCSPlayerController guard, bool choice) { + return new SimpleView { + PREFIX, + guard, + " chose " + ChatColors.Green, + choice ? "Heads" : "Tails", + ChatColors.Default + ", flipping..." + }; + } + + public IView CoinLandsOn(bool heads) { + return new SimpleView { + PREFIX, "The coin lands on " + ChatColors.Green, heads ? "Heads" : "Tails" + }; + } + + public IView FailedToChooseInTime(string choice) { + return new SimpleView { + PREFIX, + "You failed to choose in time, defaulting to " + ChatColors.Green, + choice + }; + } +} \ No newline at end of file diff --git a/lang/Jailbreak.English/LastRequest/LastRequestMessages.cs b/lang/Jailbreak.English/LastRequest/LastRequestLocale.cs similarity index 84% rename from lang/Jailbreak.English/LastRequest/LastRequestMessages.cs rename to lang/Jailbreak.English/LastRequest/LastRequestLocale.cs index 6772509f..3a2f2425 100644 --- a/lang/Jailbreak.English/LastRequest/LastRequestMessages.cs +++ b/lang/Jailbreak.English/LastRequest/LastRequestLocale.cs @@ -4,14 +4,14 @@ using Jailbreak.Formatting.Core; using Jailbreak.Formatting.Logistics; using Jailbreak.Formatting.Objects; -using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.LastRequest; using Jailbreak.Public.Extensions; using Jailbreak.Public.Mod.LastRequest; using Jailbreak.Public.Mod.LastRequest.Enums; namespace Jailbreak.English.LastRequest; -public class LastRequestMessages : ILastRequestMessages, +public class LastRequestLocale : ILRLocale, ILanguage { public static readonly FormatObject PREFIX = new HiddenFormatObject( @@ -48,16 +48,6 @@ public IView InvalidLastRequest(string query) { return new SimpleView { PREFIX, "Invalid Last Request: ", query }; } - public IView InvalidPlayerChoice(CCSPlayerController player, string reason) { - return new SimpleView { - PREFIX, - "Invalid player choice: ", - player, - " Reason: ", - reason - }; - } - public IView InformLastRequest(AbstractLastRequest lr) { return new SimpleView { PREFIX, @@ -132,6 +122,26 @@ public IView CannotLR(CCSPlayerController player, string reason) { }; } + public IView LastRequestCountdown(int seconds) { + return new SimpleView { PREFIX, "Starting in", seconds, "..." }; + } + + public IView WinByDefault(CCSPlayerController player) { + return new SimpleView { PREFIX, player, "won by default." }; + } + + public IView WinByHealth(CCSPlayerController player) { + return new SimpleView { PREFIX, player, "won by health." }; + } + + public IView WinByReason(CCSPlayerController player, string reason) { + return new SimpleView { PREFIX, player, "won by", reason + "." }; + } + + public IView Win(CCSPlayerController player) { + return new SimpleView { PREFIX, player, "won." }; + } + public IView DamageBlockedInsideLastRequest => new SimpleView { PREFIX, "You or they are in LR, damage blocked." }; @@ -139,4 +149,14 @@ public IView DamageBlockedNotInSameLR => new SimpleView { PREFIX, "You are not in the same LR as them, damage blocked." }; + + public IView InvalidPlayerChoice(CCSPlayerController player, string reason) { + return new SimpleView { + PREFIX, + "Invalid player choice: ", + player, + " Reason: ", + reason + }; + } } \ No newline at end of file diff --git a/lang/Jailbreak.English/LastRequest/RPSLocale.cs b/lang/Jailbreak.English/LastRequest/RPSLocale.cs new file mode 100644 index 00000000..ad3b6766 --- /dev/null +++ b/lang/Jailbreak.English/LastRequest/RPSLocale.cs @@ -0,0 +1,45 @@ +using CounterStrikeSharp.API.Core; +using Jailbreak.Formatting.Base; +using Jailbreak.Formatting.Views.LastRequest; + +namespace Jailbreak.English.LastRequest; + +public class RPSLocale : LastRequestLocale, ILRRPSLocale { + public IView PlayerMadeChoice(CCSPlayerController player) { + return new SimpleView { PREFIX, player, "made their choice." }; + } + + public IView BothPlayersMadeChoice() { + return new SimpleView { + PREFIX, "Both players have rocked, papered, and scissored! (ew)" + }; + } + + public IView Tie() { + return new SimpleView { PREFIX, "It's a tie! Let's go again!" }; + } + + public IView Results(CCSPlayerController guard, CCSPlayerController prisoner, + int guardPick, int prisonerPick) { + return new SimpleView { + PREFIX, + "Results: ", + guard, + " picked ", + toRPS(guardPick), + " and ", + prisoner, + " picked ", + toRPS(prisonerPick) + }; + } + + private string toRPS(int pick) { + return pick switch { + 0 => "Rock", + 1 => "Paper", + 2 => "Scissors", + _ => "Unknown" + }; + } +} \ No newline at end of file diff --git a/lang/Jailbreak.English/LastRequest/RaceLRMessages.cs b/lang/Jailbreak.English/LastRequest/RaceLocale.cs similarity index 54% rename from lang/Jailbreak.English/LastRequest/RaceLRMessages.cs rename to lang/Jailbreak.English/LastRequest/RaceLocale.cs index 1c21ce36..1ea9220e 100644 --- a/lang/Jailbreak.English/LastRequest/RaceLRMessages.cs +++ b/lang/Jailbreak.English/LastRequest/RaceLocale.cs @@ -1,27 +1,25 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Formatting.Base; -using Jailbreak.Formatting.Logistics; -using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.LastRequest; namespace Jailbreak.English.LastRequest; // ReSharper disable ClassNeverInstantiated.Global -public class RaceLRMessages : IRaceLRMessages, - ILanguage { +public class RaceLocale : LastRequestLocale, ILRRaceLocale { public IView EndRaceInstruction => new SimpleView { { - LastRequestMessages.PREFIX, - $"Type ${ChatColors.Blue}!endrace${ChatColors.White} to set the end point!" + PREFIX, + $"Type {ChatColors.Blue}!endrace{ChatColors.White} to set the end point!" }, SimpleView.NEWLINE, { - LastRequestMessages.PREFIX, - $"Type ${ChatColors.Blue}!endrace${ChatColors.White} to set the end point!" + PREFIX, + $"Type {ChatColors.Blue}!endrace{ChatColors.White} to set the end point!" }, SimpleView.NEWLINE, { - LastRequestMessages.PREFIX, - $"Type ${ChatColors.Blue}!endrace${ChatColors.White} to set the end point!" + PREFIX, + $"Type {ChatColors.Blue}!endrace{ChatColors.White} to set the end point!" }, SimpleView.NEWLINE }; @@ -29,7 +27,7 @@ public IView EndRaceInstruction public IView RaceStartingMessage(CCSPlayerController prisoner) { return new SimpleView { { - LastRequestMessages.PREFIX, prisoner, + PREFIX, prisoner, " is starting a race. Pay attention to where they set the end point!" } }; @@ -38,7 +36,7 @@ public IView RaceStartingMessage(CCSPlayerController prisoner) { public IView NotInRaceLR() { return new SimpleView { { - LastRequestMessages.PREFIX, + PREFIX, $"You must be in a race {ChatColors.Blue + "!lr" + ChatColors.White} to use this command" } }; @@ -46,10 +44,7 @@ public IView NotInRaceLR() { public IView NotInPendingState() { return new SimpleView { - { - LastRequestMessages.PREFIX, - "You must be in the pending state to use this command." - } + { PREFIX, "You must be in the pending state to use this command." } }; } } \ No newline at end of file diff --git a/lang/Jailbreak.English/Logs/LogMessages.cs b/lang/Jailbreak.English/Logs/LogLocale.cs similarity index 88% rename from lang/Jailbreak.English/Logs/LogMessages.cs rename to lang/Jailbreak.English/Logs/LogLocale.cs index 6161b337..2e01ed62 100644 --- a/lang/Jailbreak.English/Logs/LogMessages.cs +++ b/lang/Jailbreak.English/Logs/LogLocale.cs @@ -4,8 +4,7 @@ namespace Jailbreak.English.Logs; -public class - LogMessages : ILogMessages, ILanguage { +public class LogLocale : ILogLocale, ILanguage { public IView BeginJailbreakLogs => new SimpleView { "********************************", diff --git a/lang/Jailbreak.English/Mute/PeaceMessages.cs b/lang/Jailbreak.English/Mute/WardenPeaceLocale.cs similarity index 95% rename from lang/Jailbreak.English/Mute/PeaceMessages.cs rename to lang/Jailbreak.English/Mute/WardenPeaceLocale.cs index 7ad651c3..bb5e9116 100644 --- a/lang/Jailbreak.English/Mute/PeaceMessages.cs +++ b/lang/Jailbreak.English/Mute/WardenPeaceLocale.cs @@ -3,11 +3,11 @@ using Jailbreak.Formatting.Core; using Jailbreak.Formatting.Logistics; using Jailbreak.Formatting.Objects; -using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.Warden; namespace Jailbreak.English.Mute; -public class PeaceMessages : IPeaceMessages, +public class WardenPeaceLocale : IWardenPeaceLocale, ILanguage { private static readonly FormatObject PREFIX = new HiddenFormatObject( diff --git a/lang/Jailbreak.English/Rebel/JihadC4Notifications.cs b/lang/Jailbreak.English/Rebel/C4Locale.cs similarity index 56% rename from lang/Jailbreak.English/Rebel/JihadC4Notifications.cs rename to lang/Jailbreak.English/Rebel/C4Locale.cs index 6c99ecb6..138db4fe 100644 --- a/lang/Jailbreak.English/Rebel/JihadC4Notifications.cs +++ b/lang/Jailbreak.English/Rebel/C4Locale.cs @@ -5,19 +5,16 @@ namespace Jailbreak.English.Rebel; -public class JihadC4Notifications : IJihadC4Notifications, - ILanguage { +public class C4Locale : IC4Locale, ILanguage { public IView JihadC4Pickup - => new SimpleView { - RebelNotifications.PREFIX, "You picked up a Jihad C4!" - }; + => new SimpleView { RebelLocale.PREFIX, "You picked up a Jihad C4!" }; public IView JihadC4Received - => new SimpleView { RebelNotifications.PREFIX, "You received a Jihad C4!" }; + => new SimpleView { RebelLocale.PREFIX, "You received a Jihad C4!" }; public IView JihadC4Usage1 => new SimpleView { - RebelNotifications.PREFIX, + RebelLocale.PREFIX, $"To detonate it, hold it out and press {ChatColors.Yellow + "E" + ChatColors.Default}." }; } \ No newline at end of file diff --git a/lang/Jailbreak.English/Rebel/RebelNotifications.cs b/lang/Jailbreak.English/Rebel/RebelLocale.cs similarity index 92% rename from lang/Jailbreak.English/Rebel/RebelNotifications.cs rename to lang/Jailbreak.English/Rebel/RebelLocale.cs index 4b47e540..017ecca9 100644 --- a/lang/Jailbreak.English/Rebel/RebelNotifications.cs +++ b/lang/Jailbreak.English/Rebel/RebelLocale.cs @@ -7,7 +7,7 @@ namespace Jailbreak.English.Rebel; -public class RebelNotifications : IRebelNotifications, +public class RebelLocale : IRebelLocale, ILanguage { public static readonly FormatObject PREFIX = new HiddenFormatObject( diff --git a/lang/Jailbreak.English/SpecialDay/HNSDayLocale.cs b/lang/Jailbreak.English/SpecialDay/HNSDayLocale.cs new file mode 100644 index 00000000..1cbe00a7 --- /dev/null +++ b/lang/Jailbreak.English/SpecialDay/HNSDayLocale.cs @@ -0,0 +1,27 @@ +using Jailbreak.Formatting.Base; + +namespace Jailbreak.English.SpecialDay; + +public class HNSDayLocale() : TeamDayLocale("Hide and Seek", + "CTs must hide while the Ts seek!", "Ts have 250 HP!") { + public IView StayInArmory + => new SimpleView { PREFIX, "Today is", Name, ", stay in the armory!" }; + + public override IView BeginsIn(int seconds) { + if (seconds == 0) + return new SimpleView { PREFIX, "Ready or not, here they come!" }; + return new SimpleView { + PREFIX, + Name, + "begins in", + seconds, + "seconds." + }; + } + + public IView DamageWarning(int seconds) { + return new SimpleView { + PREFIX, "You will be vulnerable to damage in", seconds, "seconds." + }; + } +} \ No newline at end of file diff --git a/lang/Jailbreak.English/SpecialDay/HNSDayMessages.cs b/lang/Jailbreak.English/SpecialDay/HNSDayMessages.cs deleted file mode 100644 index ccbdd5ff..00000000 --- a/lang/Jailbreak.English/SpecialDay/HNSDayMessages.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Jailbreak.Formatting.Base; -using Jailbreak.Formatting.Views; - -namespace Jailbreak.English.SpecialDay; - -public class HNSDayMessages() : TeamDayMessages("Hide and Seek") { - public override IView SpecialDayStart - => new SimpleView { - { - ISpecialDayMessages.PREFIX, Name, - "has begun! CTs must hide and Ts must seek!" - }, - SimpleView.NEWLINE, - { ISpecialDayMessages.PREFIX, "Ts have 250 HP!" } - }; - - public IView StayInArmory - => new SimpleView { - ISpecialDayMessages.PREFIX, "Today is", Name, ", stay in the armory!" - }; - - public override IView BeginsIn(int seconds) { - if (seconds == 0) - return new SimpleView { - ISpecialDayMessages.PREFIX, "Ready or not, here they come!" - }; - return new SimpleView { - ISpecialDayMessages.PREFIX, - Name, - "begins in", - seconds, - "seconds." - }; - } - - public IView DamageWarning(int seconds) { - return new SimpleView() { - ISpecialDayMessages.PREFIX, - "You will be vulnerable to damage in", - seconds, - "seconds." - }; - } -} \ No newline at end of file diff --git a/lang/Jailbreak.English/SpecialDay/InfectionDayMessages.cs b/lang/Jailbreak.English/SpecialDay/InfectionDayLocale.cs similarity index 69% rename from lang/Jailbreak.English/SpecialDay/InfectionDayMessages.cs rename to lang/Jailbreak.English/SpecialDay/InfectionDayLocale.cs index 089fef5c..7f70b525 100644 --- a/lang/Jailbreak.English/SpecialDay/InfectionDayMessages.cs +++ b/lang/Jailbreak.English/SpecialDay/InfectionDayLocale.cs @@ -1,19 +1,18 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Formatting.Base; -using Jailbreak.Formatting.Views; using Jailbreak.Public.Utils; -using Microsoft.Extensions.Primitives; namespace Jailbreak.English.SpecialDay; -public class InfectionDayMessages() : TeamDayMessages("Infection", - "CTs are infected and try to infect Ts!") { +public class InfectionDayLocale() : TeamDayLocale("Infection", + "CTs are infected and try to infect Ts!", + "Ts can scavenge for any guns, but CTs can only use pistols!") { public override IView SpecialDayEnd() { var winner = PlayerUtil.GetAlive().FirstOrDefault()?.Team ?? CsTeam.Spectator; return new SimpleView { - ISpecialDayMessages.PREFIX, + PREFIX, Name, "ended.", (winner == CsTeam.CounterTerrorist ? ChatColors.Blue : ChatColors.Red) @@ -23,13 +22,13 @@ public override IView SpecialDayEnd() { } public IView YouWereInfectedMessage(CCSPlayerController? player) { - return (player == null || !player.IsValid) ? + return player == null || !player.IsValid ? new SimpleView { - ISpecialDayMessages.PREFIX, + PREFIX, $"{ChatColors.Red}You were {ChatColors.DarkRed}infected{ChatColors.Red}! You are now a zombie!" } : new SimpleView { - ISpecialDayMessages.PREFIX, + PREFIX, $"{ChatColors.Red}You were {ChatColors.DarkRed}infected{ChatColors.Red} by", player, "! You are now a zombie!" @@ -38,9 +37,20 @@ public IView YouWereInfectedMessage(CCSPlayerController? player) { public IView InfectedWarning(CCSPlayerController player) { return new SimpleView { - ISpecialDayMessages.PREFIX, + PREFIX, player, $"was {ChatColors.DarkRed}infected{ChatColors.Default}! {ChatColors.Red}Watch out!" }; } + + public IView DamageWarning(int seconds) { + return new SimpleView { + PREFIX, + "Damage will be enabled for the", + CsTeam.Terrorist, + "team in", + seconds, + "seconds." + }; + } } \ No newline at end of file diff --git a/lang/Jailbreak.English/SpecialDay/SDLocale.cs b/lang/Jailbreak.English/SpecialDay/SDLocale.cs new file mode 100644 index 00000000..14e9a271 --- /dev/null +++ b/lang/Jailbreak.English/SpecialDay/SDLocale.cs @@ -0,0 +1,43 @@ +using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.Formatting.Base; +using Jailbreak.Formatting.Core; +using Jailbreak.Formatting.Logistics; +using Jailbreak.Formatting.Objects; +using Jailbreak.Formatting.Views; + +namespace Jailbreak.English.SpecialDay; + +public class SDLocale : ISDLocale, ILanguage { + public static readonly FormatObject PREFIX = + new HiddenFormatObject( + $" {ChatColors.BlueGrey}[{ChatColors.Green}S{ChatColors.Blue}D{ChatColors.BlueGrey}]") { + // Hide in panorama and center text + Plain = false, Panorama = false, Chat = true + }; + + public IView SpecialDayRunning(string name) { + return new SimpleView { PREFIX, name, "is currently running!" }; + } + + public IView InvalidSpecialDay(string name) { + return new SimpleView { PREFIX, name, "is not a valid special day!" }; + } + + public IView SpecialDayCooldown(int rounds) { + return new SimpleView { + PREFIX, + "You must wait", + rounds, + "more rounds before starting a special day." + }; + } + + public IView TooLateForSpecialDay(int maxTime) { + return new SimpleView { + PREFIX, + "You must start a special day within", + maxTime, + "seconds of the round start." + }; + } +} \ No newline at end of file diff --git a/lang/Jailbreak.English/SpecialDay/SoloDayLocale.cs b/lang/Jailbreak.English/SpecialDay/SoloDayLocale.cs new file mode 100644 index 00000000..fdcc0f0a --- /dev/null +++ b/lang/Jailbreak.English/SpecialDay/SoloDayLocale.cs @@ -0,0 +1,57 @@ +using Jailbreak.Formatting.Base; +using Jailbreak.Formatting.Views; +using Jailbreak.Public.Utils; + +namespace Jailbreak.English.SpecialDay; + +public class SoloDayLocale(string name, params string[] description) + : SDLocale, ISDInstanceLocale { + public string Name => name; + public string[] Description => description; + + public virtual IView SpecialDayStart => GenerateStartMessage(); + + IView ISDInstanceLocale.SpecialDayEnd + => new SimpleView { PREFIX, Name, "ended." }; + + public virtual IView BeginsIn(int seconds) { + return seconds == 0 ? + new SimpleView { PREFIX, Name, "begins now!" } : + new SimpleView { + PREFIX, + Name, + "begins in", + seconds, + "seconds." + }; + } + + public IView GenerateStartMessage() { + var result = new SimpleView { PREFIX, { "Today is a", Name, "day!" } }; + + if (description.Length == 0) return result; + + result.Add(description[0]); + + for (var i = 1; i < description.Length; i++) { + result.Add(SimpleView.NEWLINE); + result.Add(PREFIX); + result.Add(description[i]); + } + + return result; + } + + public IView SpecialDayEnd() { + var lastAlive = PlayerUtil.GetAlive().FirstOrDefault(); + if (lastAlive == null) + return new SimpleView { PREFIX, Name, "ended. No one won!" }; + return new SimpleView { + PREFIX, + Name, + "ended.", + lastAlive, + "won!" + }; + } +} \ No newline at end of file diff --git a/lang/Jailbreak.English/SpecialDay/SoloDayMessages.cs b/lang/Jailbreak.English/SpecialDay/SoloDayMessages.cs deleted file mode 100644 index c31256ea..00000000 --- a/lang/Jailbreak.English/SpecialDay/SoloDayMessages.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Jailbreak.Formatting.Base; -using Jailbreak.Formatting.Views; -using Jailbreak.Public.Utils; - -namespace Jailbreak.English.SpecialDay; - -public class SoloDayMessages(string name, string? description = null) - : ISpecialDayInstanceMessages { - public string Name => name; - public string? Description => description; - - public virtual IView SpecialDayStart - => Description == null ? - new SimpleView { - ISpecialDayMessages.PREFIX, "Today is a", Name, "day." - } : - new SimpleView { - ISpecialDayMessages.PREFIX, - "Today is a", - Name, - "day.", - SimpleView.NEWLINE, - ISpecialDayMessages.PREFIX, - Description - }; - - public virtual IView BeginsIn(int seconds) { - return seconds == 0 ? - new SimpleView { ISpecialDayMessages.PREFIX, Name, "begins now!" } : - new SimpleView { - ISpecialDayMessages.PREFIX, - Name, - "begins in", - seconds, - "seconds." - }; - } - - public IView SpecialDayEnd() { - var lastAlive = PlayerUtil.GetAlive().FirstOrDefault(); - if (lastAlive == null) - return new SimpleView { - ISpecialDayMessages.PREFIX, Name, "has ended! No one won!" - }; - return new SimpleView { - ISpecialDayMessages.PREFIX, - Name, - "ended!", - lastAlive, - "won!" - }; - } -} \ No newline at end of file diff --git a/lang/Jailbreak.English/SpecialDay/SpecialDayMessages.cs b/lang/Jailbreak.English/SpecialDay/SpecialDayMessages.cs deleted file mode 100644 index b2036b1f..00000000 --- a/lang/Jailbreak.English/SpecialDay/SpecialDayMessages.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Jailbreak.Formatting.Base; -using Jailbreak.Formatting.Logistics; -using Jailbreak.Formatting.Views; - -namespace Jailbreak.English.SpecialDay; - -public class SpecialDayMessages : ISpecialDayMessages, - ILanguage { - public IView SpecialDayRunning(string name) { - return new SimpleView { - ISpecialDayMessages.PREFIX, name, "is currently running!" - }; - } - - public IView InvalidSpecialDay(string name) { - return new SimpleView { - ISpecialDayMessages.PREFIX, name, "is not a valid special day!" - }; - } - - public IView SpecialDayCooldown(int rounds) { - return new SimpleView { - ISpecialDayMessages.PREFIX, - "You must wait", - rounds, - "more rounds before starting a special day!" - }; - } - - public IView TooLateForSpecialDay(int maxTime) { - return new SimpleView { - ISpecialDayMessages.PREFIX, - "You must start a special day within", - maxTime, - "seconds of the round start!" - }; - } -} \ No newline at end of file diff --git a/lang/Jailbreak.English/SpecialDay/SpeedrunDayLocale.cs b/lang/Jailbreak.English/SpecialDay/SpeedrunDayLocale.cs new file mode 100644 index 00000000..fffab037 --- /dev/null +++ b/lang/Jailbreak.English/SpecialDay/SpeedrunDayLocale.cs @@ -0,0 +1,134 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.Formatting.Base; + +namespace Jailbreak.English.SpecialDay; + +public class SpeedrunDayLocale() : SoloDayLocale("Speedrunners", + $"Follow the {ChatColors.Blue}blue{ChatColors.Default} player!", + "They will run to a spot on the map.", + $"Each round, the {ChatColors.Red}slowest players{ChatColors.Default} to reach the target will be eliminated.") { + public IView RoundEnded + => new SimpleView { + PREFIX, "Round over! The next one will start shortly." + }; + + public IView NoneEliminated + => new SimpleView { PREFIX, "No one was eliminated this round!" }; + + public IView NoneReachedGoal + => new SimpleView { + { PREFIX, "Not enough players reached the goal this round!" }, + SimpleView.NEWLINE, + { PREFIX, "Going off of distance to target for those who didn't." } + }; + + public IView YouAreRunner(int seconds) { + return new SimpleView { + { PREFIX, "You are the speedrunner!" }, + SimpleView.NEWLINE, { + PREFIX, "You have", seconds, "seconds to run to a spot to set the goal." + } + }; + } + + public IView BeginRound(int round, int eliminationCount, int seconds) { + if (eliminationCount == 1) + return new SimpleView { + { + PREFIX, + $"Round #{ChatColors.Yellow}{round}{ChatColors.Default} begins! The slowest", + "player to reach the goal will be eliminated!" + }, + SimpleView.NEWLINE, + { PREFIX, "You have", seconds, "seconds to reach the goal!" } + }; + + return new SimpleView { + { + PREFIX, + $"Round {ChatColors.Yellow}#{round}{ChatColors.Default} begins! The slowest", + eliminationCount, "players to reach the goal will be eliminated!" + }, + SimpleView.NEWLINE, + { PREFIX, "You have", seconds, "seconds to reach the goal." } + }; + } + + public IView RuntimeLeft(int seconds) { + return new SimpleView { + PREFIX, "You have", seconds, "seconds left to run to a spot!" + }; + } + + public IView RunnerAssigned(CCSPlayerController player) { + return new SimpleView { + PREFIX, player, "is the speedrunner! Follow them closely!" + }; + } + + public IView RunnerReassigned(CCSPlayerController player) { + return new SimpleView { + PREFIX, "The speedrunner left, so", player, "is now the speedrunner!" + }; + } + + public IView PlayerTime(CCSPlayerController player, int position, + float time) { + var place = position switch { + 1 => ChatColors.Green + "FIRST", + 2 => ChatColors.LightYellow + "Second", + 3 => ChatColors.BlueGrey + "3rd", + _ => ChatColors.Grey + "" + position + "th" + }; + if (time < 0) + return new SimpleView { + PREFIX, + player, + "finished in", + -time, + "seconds.", + place, + "place" + (position == 1 ? "!" : ".") + }; + + return new SimpleView { + PREFIX, + player, + "was", + time, + "units away from the goal,", + place, + "place." + }; + } + + public IView PlayerEliminated(CCSPlayerController player) { + return new SimpleView { PREFIX, player, "was eliminated!" }; + } + + public IView PlayerWon(CCSPlayerController player) { + return new SimpleView { PREFIX, player, "won the game!" }; + } + + public IView BestTime(CCSPlayerController player, float time) { + return new SimpleView { + PREFIX, + player, + "beat the best time with", + time, + $"seconds! {ChatColors.Green}FIRST PLACE{ChatColors.Default}!" + }; + } + + public IView ImpossibleLocation(CsTeam team, CCSPlayerController player) { + return new SimpleView { + { + PREFIX, "No one on", team, + "reached the goal. Eliminating one player on each time." + }, + SimpleView.NEWLINE, + { PREFIX, "Randomly selected the path from ", player, "." } + }; + } +} \ No newline at end of file diff --git a/lang/Jailbreak.English/SpecialDay/TeamDayLocale.cs b/lang/Jailbreak.English/SpecialDay/TeamDayLocale.cs new file mode 100644 index 00000000..d1b88e7d --- /dev/null +++ b/lang/Jailbreak.English/SpecialDay/TeamDayLocale.cs @@ -0,0 +1,58 @@ +using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.Formatting.Base; +using Jailbreak.Formatting.Views; +using Jailbreak.Public.Utils; + +namespace Jailbreak.English.SpecialDay; + +public class TeamDayLocale(string name, params string[] description) + : SDLocale, ISDInstanceLocale { + public string Name => name; + + public string[] Description => description; + + public virtual IView SpecialDayStart => GenerateStartMessage(); + + IView ISDInstanceLocale.SpecialDayEnd + => new SimpleView { PREFIX, Name, "ended." }; + + public virtual IView BeginsIn(int seconds) { + return seconds == 0 ? + new SimpleView { PREFIX, Name, "begins now!" } : + new SimpleView { + PREFIX, + Name, + "begins in", + seconds, + "seconds." + }; + } + + public IView GenerateStartMessage() { + var result = new SimpleView { PREFIX, { "Today is a", Name, "day!" } }; + + if (description.Length == 0) return result; + + result.Add(description[0]); + + for (var i = 1; i < description.Length; i++) { + result.Add(SimpleView.NEWLINE); + result.Add(PREFIX); + result.Add(description[i]); + } + + return result; + } + + public virtual IView SpecialDayEnd() { + var winner = PlayerUtil.GetAlive().FirstOrDefault()?.Team + ?? CsTeam.Spectator; + return new SimpleView { + PREFIX, + Name, + "ended.", + winner, + "won!" + }; + } +} \ No newline at end of file diff --git a/lang/Jailbreak.English/SpecialDay/TeamDayMessages.cs b/lang/Jailbreak.English/SpecialDay/TeamDayMessages.cs deleted file mode 100644 index b393b290..00000000 --- a/lang/Jailbreak.English/SpecialDay/TeamDayMessages.cs +++ /dev/null @@ -1,52 +0,0 @@ -using CounterStrikeSharp.API.Modules.Utils; -using Jailbreak.Formatting.Base; -using Jailbreak.Formatting.Views; -using Jailbreak.Public.Utils; - -namespace Jailbreak.English.SpecialDay; - -public class TeamDayMessages(string name, string? description = null) - : ISpecialDayInstanceMessages { - public string Name => name; - public string? Description => description; - - public virtual IView SpecialDayStart - => Description == null ? - new SimpleView { - ISpecialDayMessages.PREFIX, "Today is a", Name, "day." - } : - new SimpleView { - ISpecialDayMessages.PREFIX, - "Today is a", - Name, - "day.", - SimpleView.NEWLINE, - ISpecialDayMessages.PREFIX, - Description - }; - - public virtual IView BeginsIn(int seconds) { - return seconds == 0 ? - new SimpleView { ISpecialDayMessages.PREFIX, Name, "begins now!" } : - new SimpleView { - ISpecialDayMessages.PREFIX, - Name, - "begins in", - seconds, - "seconds." - }; - } - - public virtual IView SpecialDayEnd() { - var winner = PlayerUtil.GetAlive().FirstOrDefault()?.Team - ?? CsTeam.Spectator; - return new SimpleView { - ISpecialDayMessages.PREFIX, - Name, - "ended.", - (winner == CsTeam.CounterTerrorist ? ChatColors.Blue : ChatColors.Red) - + (winner == CsTeam.CounterTerrorist ? "CTs" : "Ts"), - "won!" - }; - } -} \ No newline at end of file diff --git a/lang/Jailbreak.English/SpecialDay/WardayInstanceLocale.cs b/lang/Jailbreak.English/SpecialDay/WardayInstanceLocale.cs new file mode 100644 index 00000000..9dcf9bf3 --- /dev/null +++ b/lang/Jailbreak.English/SpecialDay/WardayInstanceLocale.cs @@ -0,0 +1,17 @@ +using Jailbreak.Formatting.Base; + +namespace Jailbreak.English.SpecialDay; + +public class WardayInstanceLocale() : TeamDayLocale("Warday", + "CTs vs Ts! CTs pick a room, Ts must go fight them!", + "CTs MUST stay in the same room until expansion time.") { + public IView ExpandNow => new SimpleView { PREFIX, "CTs can expand now!" }; + + public IView ExpandIn(int seconds) { + return new SimpleView { + { PREFIX, "CTs can expand in", seconds, "seconds." }, + SimpleView.NEWLINE, + { PREFIX, "They will be given replenished health, armor, and speed." } + }; + } +} \ No newline at end of file diff --git a/lang/Jailbreak.English/SpecialDay/WardayInstanceMessages.cs b/lang/Jailbreak.English/SpecialDay/WardayInstanceMessages.cs deleted file mode 100644 index 6be1f4bc..00000000 --- a/lang/Jailbreak.English/SpecialDay/WardayInstanceMessages.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Jailbreak.Formatting.Base; -using Jailbreak.Formatting.Views; - -namespace Jailbreak.English.SpecialDay; - -public class WardayInstanceMessages() : TeamDayMessages("Warday", - "CTs vs Ts! CTs pick a room, Ts must go fight them!") { - public IView ExpandNow - => new SimpleView { { ISpecialDayMessages.PREFIX, "CTs can expand now!" } }; - - public IView ExpandIn(int seconds) { - return new SimpleView { - { ISpecialDayMessages.PREFIX, "CTs can expand in", seconds, "seconds." }, - SimpleView.NEWLINE, { - ISpecialDayMessages.PREFIX, - "They will be given replenished health, armor, and speed." - } - }; - } -} \ No newline at end of file diff --git a/lang/Jailbreak.English/Warden/RollCommandNotifications.cs b/lang/Jailbreak.English/Warden/RollCommandNotifications.cs deleted file mode 100644 index 878f4450..00000000 --- a/lang/Jailbreak.English/Warden/RollCommandNotifications.cs +++ /dev/null @@ -1,22 +0,0 @@ -using CounterStrikeSharp.API.Modules.Utils; -using Jailbreak.Formatting.Base; -using Jailbreak.Formatting.Core; -using Jailbreak.Formatting.Logistics; -using Jailbreak.Formatting.Objects; -using Jailbreak.Formatting.Views; - -namespace Jailbreak.English.Warden; - -public class RollCommandNotifications : IRollCommandNotications, - ILanguage { - public static readonly FormatObject PREFIX = - new HiddenFormatObject( - $" {ChatColors.Lime}[{ChatColors.Green}Roll{ChatColors.Lime}]") { - // Hide in panorama and center text - Plain = false, Panorama = false, Chat = true - }; - - public IView Roll(int roll) { - return new SimpleView { PREFIX, $"warden has rolled {roll}!" }; - } -} \ No newline at end of file diff --git a/lang/Jailbreak.English/Warden/WardenCmdOpenLocale.cs b/lang/Jailbreak.English/Warden/WardenCmdOpenLocale.cs new file mode 100644 index 00000000..217c94d6 --- /dev/null +++ b/lang/Jailbreak.English/Warden/WardenCmdOpenLocale.cs @@ -0,0 +1,26 @@ +using Jailbreak.Formatting.Base; +using Jailbreak.Formatting.Logistics; +using Jailbreak.Formatting.Views.Warden; + +namespace Jailbreak.English.Warden; + +public class WardenCmdOpenLocale : IWardenCmdOpenLocale, + ILanguage { + public IView CannotOpenYet(int seconds) { + return new SimpleView { + WardenLocale.PREFIX, + "You must wait", + seconds, + "seconds before opening the cells." + }; + } + + public IView AlreadyOpened + => new SimpleView { WardenLocale.PREFIX, "You already opened cells." }; + + public IView CellsOpened + => new SimpleView { WardenLocale.PREFIX, "The warden opened cells." }; + + public IView OpeningFailed + => new SimpleView { WardenLocale.PREFIX, "Failed to open the cells." }; +} \ No newline at end of file diff --git a/lang/Jailbreak.English/Warden/WardenCmdRollLocale.cs b/lang/Jailbreak.English/Warden/WardenCmdRollLocale.cs new file mode 100644 index 00000000..a53e2854 --- /dev/null +++ b/lang/Jailbreak.English/Warden/WardenCmdRollLocale.cs @@ -0,0 +1,14 @@ +using Jailbreak.Formatting.Base; +using Jailbreak.Formatting.Logistics; +using Jailbreak.Formatting.Views.Warden; + +namespace Jailbreak.English.Warden; + +public class WardenCmdRollLocale : IWardenCmdRollLocale, + ILanguage { + public IView Roll(int roll) { + return new SimpleView { + WardenLocale.PREFIX, "warden has rolled", roll, "!" + }; + } +} \ No newline at end of file diff --git a/lang/Jailbreak.English/Warden/WardenNotifications.cs b/lang/Jailbreak.English/Warden/WardenLocale.cs similarity index 94% rename from lang/Jailbreak.English/Warden/WardenNotifications.cs rename to lang/Jailbreak.English/Warden/WardenLocale.cs index 8ecba9ce..ba461a5d 100644 --- a/lang/Jailbreak.English/Warden/WardenNotifications.cs +++ b/lang/Jailbreak.English/Warden/WardenLocale.cs @@ -4,11 +4,11 @@ using Jailbreak.Formatting.Core; using Jailbreak.Formatting.Logistics; using Jailbreak.Formatting.Objects; -using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.Warden; namespace Jailbreak.English.Warden; -public class WardenNotifications : IWardenNotifications, +public class WardenLocale : IWardenLocale, ILanguage { public static readonly FormatObject PREFIX = new HiddenFormatObject( @@ -98,6 +98,9 @@ public IView CurrentWarden(CCSPlayerController? player) { new SimpleView { PREFIX, "There is no warden." }; } + public IView CannotWardenDuringWarmup + => new SimpleView { PREFIX, "You cannot warden during warmup." }; + public IView FireCommandSuccess(CCSPlayerController player) { return new SimpleView { PREFIX, player, "was fired and is no longer the warden." diff --git a/lang/Jailbreak.English/Warden/SpecialTreatmentNotifications.cs b/lang/Jailbreak.English/Warden/WardenSTLocale.cs similarity index 54% rename from lang/Jailbreak.English/Warden/SpecialTreatmentNotifications.cs rename to lang/Jailbreak.English/Warden/WardenSTLocale.cs index 69bd087f..ebd973a6 100644 --- a/lang/Jailbreak.English/Warden/SpecialTreatmentNotifications.cs +++ b/lang/Jailbreak.English/Warden/WardenSTLocale.cs @@ -1,37 +1,28 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Formatting.Base; -using Jailbreak.Formatting.Core; using Jailbreak.Formatting.Logistics; -using Jailbreak.Formatting.Objects; -using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.Warden; namespace Jailbreak.English.Warden; -public class SpecialTreatmentNotifications : ISpecialTreatmentNotifications, +public class WardenSTLocale : IWardenSTLocale, ILanguage { - public static readonly FormatObject PREFIX = - new HiddenFormatObject( - $" {ChatColors.Lime}[{ChatColors.Green}ST{ChatColors.Lime}]") { - // Hide in panorama and center text - Plain = false, Panorama = false, Chat = true - }; - public IView Granted => new SimpleView { - PREFIX, - $"You now have {ChatColors.Green}special treatment{ChatColors.White}!" + WardenLocale.PREFIX, + $"You now have {ChatColors.Green}Special Treatment{ChatColors.White}!" }; public IView Revoked => new SimpleView { - PREFIX, - $"Your special treatment was {ChatColors.Red}removed{ChatColors.White}." + WardenLocale.PREFIX, + $"Your Special Treatment was {ChatColors.Red}removed{ChatColors.White}." }; public IView GrantedTo(CCSPlayerController player) { return new SimpleView { - PREFIX, + WardenLocale.PREFIX, player, $"now has {ChatColors.Green}Special Treatment{ChatColors.White}!" }; @@ -39,7 +30,7 @@ public IView GrantedTo(CCSPlayerController player) { public IView RevokedFrom(CCSPlayerController player) { return new SimpleView { - PREFIX, + WardenLocale.PREFIX, player, $"{ChatColors.Red}no longer {ChatColors.Grey}has Special Treatment." }; diff --git a/mod/Jailbreak.Debug/DebugCommand.cs b/mod/Jailbreak.Debug/DebugCommand.cs index c251d5b0..18b6a1c2 100644 --- a/mod/Jailbreak.Debug/DebugCommand.cs +++ b/mod/Jailbreak.Debug/DebugCommand.cs @@ -24,7 +24,7 @@ public void Start(BasePlugin basePlugin) { commands.Add("lg", new LastGuard(serviceProvider)); commands.Add("zone", new Zone(serviceProvider, basePlugin)); commands.Add("endround", new EndRound(serviceProvider)); - commands.Add("opencells", new OpenCells(serviceProvider)); + commands.Add("testnearopen", new TestNearOpen(serviceProvider)); } [RequiresPermissions("@css/root")] diff --git a/mod/Jailbreak.Debug/PlayerZoneCreator.cs b/mod/Jailbreak.Debug/PlayerZoneCreator.cs index dc21dab4..c7934e46 100644 --- a/mod/Jailbreak.Debug/PlayerZoneCreator.cs +++ b/mod/Jailbreak.Debug/PlayerZoneCreator.cs @@ -1,10 +1,8 @@ -using System.Drawing; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Timers; using Jailbreak.Debug.Subcommands; using Jailbreak.Public.Extensions; using Jailbreak.Public.Mod.Zones; -using Jailbreak.Zones; using Timer = CounterStrikeSharp.API.Modules.Timers.Timer; namespace Jailbreak.Debug; @@ -38,9 +36,9 @@ public override void Dispose() { public ZoneType Type { get; } - public override IZone Build(IZoneFactory factory) { + public override IZone Build(IZoneFactory providedFactory) { Points = ConvexHullUtil.ComputeConvexHull(Points ?? []).ToList(); - return base.Build(factory); + return base.Build(providedFactory); } private void tick() { diff --git a/mod/Jailbreak.Debug/Subcommands/AbstractCommand.cs b/mod/Jailbreak.Debug/Subcommands/AbstractCommand.cs index b75e57fc..98e40072 100644 --- a/mod/Jailbreak.Debug/Subcommands/AbstractCommand.cs +++ b/mod/Jailbreak.Debug/Subcommands/AbstractCommand.cs @@ -9,8 +9,8 @@ namespace Jailbreak.Debug.Subcommands; public abstract class AbstractCommand(IServiceProvider services) { - private readonly IGenericCommandNotifications lang = - services.GetRequiredService(); + private readonly IGenericCmdLocale lang = + services.GetRequiredService(); protected readonly IServiceProvider Services = services; @@ -41,7 +41,7 @@ public abstract void OnCommand(CCSPlayerController? executor, if (!matches.Any()) { if (command.CallingPlayer != null) lang.PlayerNotFound(command.GetArg(argIndex)) - .ToPlayerChat(command.CallingPlayer); + .ToChat(command.CallingPlayer); return null; } @@ -53,7 +53,7 @@ public abstract void OnCommand(CCSPlayerController? executor, if (command.CallingPlayer != null) lang.PlayerFoundMultiple(command.GetArg(argIndex)) - .ToPlayerChat(command.CallingPlayer); + .ToChat(command.CallingPlayer); return null; } @@ -71,14 +71,14 @@ public abstract void OnCommand(CCSPlayerController? executor, if (!matches.Any()) { if (command.CallingPlayer != null) lang.PlayerNotFound(command.GetArg(argIndex)) - .ToPlayerChat(command.CallingPlayer); + .ToChat(command.CallingPlayer); return null; } if (matches.Count() <= 1) return matches; if (command.CallingPlayer != null) lang.PlayerFoundMultiple(command.GetArg(argIndex)) - .ToPlayerChat(command.CallingPlayer); + .ToChat(command.CallingPlayer); return null; } diff --git a/mod/Jailbreak.Debug/Subcommands/Color.cs b/mod/Jailbreak.Debug/Subcommands/Color.cs new file mode 100644 index 00000000..98d3d8a8 --- /dev/null +++ b/mod/Jailbreak.Debug/Subcommands/Color.cs @@ -0,0 +1,50 @@ +using CounterStrikeSharp.API.Core; +using Jailbreak.Public.Extensions; +using Jailbreak.Public.Utils; + +namespace Jailbreak.Debug.Subcommands; + +public class Color(IServiceProvider services) : AbstractCommand(services) { + public override void OnCommand(CCSPlayerController? executor, + WrappedInfo info) { + if (info.ArgCount == 1 || executor == null) { + info.ReplyToCommand("css_color [| [red] [green] [blue]]"); + return; + } + + var alpha = 255; + var red = 255; + var green = 255; + var blue = 255; + + switch (info.ArgCount) { + case 2: + int.TryParse(info.GetArg(1), out alpha); + break; + case <= 4: + info.ReplyToCommand("Invalid number of arguments."); + break; + default: { + var currentColor = executor.GetColor(); + if (currentColor == null) { + info.ReplyToCommand("Failed to get color."); + return; + } + + red = currentColor.Value.R; + green = currentColor.Value.G; + blue = currentColor.Value.B; + break; + } + } + + if (info.ArgCount > 3) { + int.TryParse(info.GetArg(info.ArgCount - 3), out red); + int.TryParse(info.GetArg(info.ArgCount - 2), out green); + int.TryParse(info.GetArg(info.ArgCount - 1), out blue); + } + + executor.SetColor(System.Drawing.Color.FromArgb(alpha, red, green, blue)); + executor.PrintToChat($"Color set to {alpha} {red} {green} {blue}"); + } +} \ No newline at end of file diff --git a/mod/Jailbreak.Debug/Subcommands/LastRequest.cs b/mod/Jailbreak.Debug/Subcommands/LastRequest.cs index 4fdabdf2..94c2f44b 100644 --- a/mod/Jailbreak.Debug/Subcommands/LastRequest.cs +++ b/mod/Jailbreak.Debug/Subcommands/LastRequest.cs @@ -2,7 +2,7 @@ using CounterStrikeSharp.API.Modules.Menu; using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Formatting.Extensions; -using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.LastRequest; using Jailbreak.LastRequest; using Jailbreak.Public.Extensions; using Jailbreak.Public.Mod.LastRequest; @@ -14,7 +14,7 @@ namespace Jailbreak.Debug.Subcommands; public class LastRequest : AbstractCommand { private readonly ILastRequestManager manager; private readonly LastRequestMenuSelector menuSelector; - private readonly ILastRequestMessages messages; + private readonly ILRLocale messages; private readonly LastRequestPlayerSelector playerSelector; private readonly BasePlugin plugin; @@ -27,7 +27,7 @@ public LastRequest(IServiceProvider services, BasePlugin plugin) : menuSelector = new LastRequestMenuSelector( services.GetRequiredService(), type => "css_debug lastrequest " + type, plugin); - messages = services.GetRequiredService(); + messages = services.GetRequiredService(); } // (debug) lastrequest [lr] [player] @@ -57,7 +57,7 @@ public override void OnCommand(CCSPlayerController? executor, var type = LRTypeExtensions.FromString(info.GetArg(1)); if (type is null) { - messages.InvalidLastRequest(info.GetArg(1)).ToPlayerChat(executor); + messages.InvalidLastRequest(info.GetArg(1)).ToChat(executor); return; } diff --git a/mod/Jailbreak.Debug/Subcommands/OpenCells.cs b/mod/Jailbreak.Debug/Subcommands/OpenCells.cs deleted file mode 100644 index 47da6d86..00000000 --- a/mod/Jailbreak.Debug/Subcommands/OpenCells.cs +++ /dev/null @@ -1,11 +0,0 @@ -using CounterStrikeSharp.API.Core; -using Jailbreak.Public.Utils; - -namespace Jailbreak.Debug.Subcommands; - -public class OpenCells(IServiceProvider services) : AbstractCommand(services) { - public override void OnCommand(CCSPlayerController? executor, - WrappedInfo info) { - MapUtil.OpenCells(); - } -} \ No newline at end of file diff --git a/mod/Jailbreak.Debug/Subcommands/TestNearOpen.cs b/mod/Jailbreak.Debug/Subcommands/TestNearOpen.cs new file mode 100644 index 00000000..dea70522 --- /dev/null +++ b/mod/Jailbreak.Debug/Subcommands/TestNearOpen.cs @@ -0,0 +1,17 @@ +using CounterStrikeSharp.API.Core; +using Jailbreak.Public.Utils; + +namespace Jailbreak.Debug.Subcommands; + +public class TestNearOpen(IServiceProvider services) + : AbstractCommand(services) { + public override void OnCommand(CCSPlayerController? executor, + WrappedInfo info) { + if (executor == null) return; + var pos = executor.Pawn.Value?.AbsOrigin; + if (pos == null) return; + MapUtil.OpenCells(Sensitivity.ANY, pos); + info.ReplyToCommand( + "Attempted to open cells using the button nearest to you."); + } +} \ No newline at end of file diff --git a/mod/Jailbreak.Debug/Subcommands/Zone.cs b/mod/Jailbreak.Debug/Subcommands/Zone.cs index 0bd4d11d..9ed78d82 100644 --- a/mod/Jailbreak.Debug/Subcommands/Zone.cs +++ b/mod/Jailbreak.Debug/Subcommands/Zone.cs @@ -1,6 +1,6 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; -using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.Public.Extensions; using Jailbreak.Public.Mod.Zones; using Microsoft.Extensions.DependencyInjection; @@ -146,7 +146,7 @@ await zoneManager.PushZoneWithID(innerZone, innerPair.Value.Item2, if (specifiedType == null) { foreach (var type in Enum.GetValues()) { var zones = zoneManager.GetZones(type).GetAwaiter().GetResult(); - if (!allZones.ContainsKey(type)) { continue; } + if (!allZones.ContainsKey(type)) continue; info.ReplyToCommand($"{type} zones: {zones.Count}"); } @@ -157,11 +157,9 @@ await zoneManager.PushZoneWithID(innerZone, innerPair.Value.Item2, var toList = zoneManager.GetZones(specifiedType.Value) .GetAwaiter() .GetResult(); - foreach (var listZone in toList) { + foreach (var listZone in toList) info.ReplyToCommand( $"Points: {listZone.GetBorderPoints().Count()}/{listZone.GetAllPoints().Count()}Center: {listZone.CalculateCenterPoint()} Area: {listZone.GetArea()}"); - } - return; } @@ -174,15 +172,22 @@ await zoneManager.PushZoneWithID(innerZone, innerPair.Value.Item2, nameof(specifiedType) + " != null"); switch (info.GetArg(1).ToLower()) { case "add": - attemptBeginCreation(executor, info, specifiedType.Value); + attemptBeginCreation(executor, specifiedType.Value); return; case "set": - foreach (var zone in zoneManager.GetZones(specifiedType.Value) + var zones = zoneManager.GetZones(specifiedType.Value) .GetAwaiter() - .GetResult()) - zoneManager.DeleteZone(zone.Id); + .GetResult(); + + Server.NextFrameAsync(async () => { + var copy = zones.ToList(); + + foreach (var zone in copy) await zoneManager.DeleteZone(zone.Id); + + Server.NextFrame(() + => attemptBeginCreation(executor, specifiedType.Value)); + }); - attemptBeginCreation(executor, info, specifiedType.Value); return; case "tpto": case "tp": @@ -199,17 +204,40 @@ await zoneManager.PushZoneWithID(innerZone, innerPair.Value.Item2, } tpDestinations.Sort((a, b) - => a.GetMinDistance(position) <= b.GetMinDistance(position) ? -1 : 1); + => a.GetMinDistanceSquared(position) + <= b.GetMinDistanceSquared(position) ? + -1 : + 1); executor.PlayerPawn.Value.Teleport(tpDestinations.First() .GetCenterPoint()); info.ReplyToCommand("Teleported to zone #" + tpDestinations.First().Id); return; + case "generate": + if (specifiedType != ZoneType.ARMORY + && specifiedType != ZoneType.CELL) { + info.ReplyToCommand("Invalid zone type"); + return; + } + + var entName = specifiedType == ZoneType.CELL ? + "info_player_terrorist" : + "info_player_counterterrorist"; + + var spawns = Utilities + .FindAllEntitiesByDesignerName(entName) + .Where(s => s.AbsOrigin != null) + .Select(s => s.AbsOrigin!); + + var generated = factory.CreateZone(spawns); + generated.Draw(plugin, specifiedType.Value.GetColor(), 120); + info.ReplyToCommand($"Drawing auto-generated {specifiedType} zone"); + return; } } - private void attemptBeginCreation(CCSPlayerController executor, - WrappedInfo info, ZoneType type) { + private void + attemptBeginCreation(CCSPlayerController executor, ZoneType type) { if (creators.ContainsKey(executor.SteamID)) { executor.PrintToChat("You are already creating a zone"); return; @@ -217,7 +245,8 @@ private void attemptBeginCreation(CCSPlayerController executor, if (type.IsSinglePoint()) { if (executor.PlayerPawn.Value?.AbsOrigin != null) { - var zone = factory.CreateZone([executor.PlayerPawn.Value.AbsOrigin!]); + var zone = + factory.CreateZone([executor.PlayerPawn.Value.AbsOrigin!.Clone()]); zone.Draw(plugin, type.GetColor(), 1f); Server.NextFrameAsync(async () => { await zoneManager.PushZone(zone, type); @@ -252,7 +281,7 @@ private void attemptBeginCreation(CCSPlayerController executor, => z.IsInsideZone(player.PlayerPawn.Value!.AbsOrigin!)); if (zone == null) continue; if (result != null) { - player.PrintToChat("Multiple zones found."); + if (print) player.PrintToChat("Multiple zones found."); return null; } @@ -261,7 +290,7 @@ private void attemptBeginCreation(CCSPlayerController executor, } if (result == null || resultType == null) { - player.PrintToChat("No zones found"); + if (print) player.PrintToChat("No zones found"); return null; } @@ -269,30 +298,25 @@ private void attemptBeginCreation(CCSPlayerController executor, } if (!zoneDictionary.TryGetValue(type.Value, out var value)) { - player.PrintToChat("No zones found"); + if (print) player.PrintToChat("No zones found"); return null; } - var validZones = value.Where(zone - => zone.IsInsideZone(player.PlayerPawn.Value!.AbsOrigin!)) - .ToList(); + var validZones = value.MinBy(z + => z.GetMinDistanceSquared(player.PlayerPawn.Value!.AbsOrigin!)); - switch (validZones.Count) { - case 0: - player.PrintToChat("No zones found"); - return null; - case > 1: - player.PrintToChat("Multiple zones found."); - return null; - default: - return (validZones.First()!, type.Value); + if (validZones == null) { + if (print) player.PrintToChat("No zones found"); + return null; } + + return (validZones, type.Value); } private void sendUsage(CCSPlayerController player) { player.PrintToChat("Usage: css_zone [add/set/remove/tp] [type]"); - player.PrintToChat(ChatColors.Default - + " css_zone [addinner/show/list] "); - player.PrintToChat(ChatColors.Default + " css_zone [finish/reload]"); + player.PrintToChat("css_zone [addinner/show/list] "); + player.PrintToChat("css_zone [finish/reload]"); + player.PrintToChat("css_zone generate [cell/armory]"); } } \ No newline at end of file diff --git a/mod/Jailbreak.LastGuard/LastGuard.cs b/mod/Jailbreak.LastGuard/LastGuard.cs index 6105fcdc..eb99329d 100644 --- a/mod/Jailbreak.LastGuard/LastGuard.cs +++ b/mod/Jailbreak.LastGuard/LastGuard.cs @@ -16,8 +16,8 @@ namespace Jailbreak.LastGuard; -public class LastGuard(ILastGuardNotifications notifications, - ILastRequestManager lrManager) : ILastGuardService, IPluginBehavior { +public class LastGuard(ILGLocale notifications, ILastRequestManager lrManager) + : ILastGuardService, IPluginBehavior { public readonly FakeConVar CvAlwaysOverrideCt = new( "css_jb_lg_apply_lower_hp", "If true, the LG will be forced lower health if calculated"); diff --git a/mod/Jailbreak.LastRequest/EndRaceCommand.cs b/mod/Jailbreak.LastRequest/EndRaceCommand.cs index 53ba6440..0e23135e 100644 --- a/mod/Jailbreak.LastRequest/EndRaceCommand.cs +++ b/mod/Jailbreak.LastRequest/EndRaceCommand.cs @@ -2,7 +2,7 @@ using CounterStrikeSharp.API.Core.Attributes.Registration; using CounterStrikeSharp.API.Modules.Commands; using Jailbreak.Formatting.Extensions; -using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.LastRequest; using Jailbreak.Public.Behaviors; using Jailbreak.Public.Mod.LastRequest; using Jailbreak.Public.Mod.LastRequest.Enums; @@ -10,7 +10,7 @@ namespace Jailbreak.LastRequest; public class EndRaceCommand(ILastRequestManager lrManager, - IRaceLRMessages messages) : IPluginBehavior { + ILRRaceLocale messages) : IPluginBehavior { [ConsoleCommand("css_endrace", "Used to set the end point of a race LR")] [CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)] public void Command_EndRace(CCSPlayerController? executor, CommandInfo info) { @@ -18,12 +18,12 @@ public void Command_EndRace(CCSPlayerController? executor, CommandInfo info) { var lr = lrManager.GetActiveLR(executor); if (lr is not { Type: LRType.RACE }) { - messages.NotInRaceLR().ToPlayerChat(executor); + messages.NotInRaceLR().ToChat(executor); return; } if (lr.State != LRState.PENDING) { - messages.NotInPendingState().ToPlayerChat(executor); + messages.NotInPendingState().ToChat(executor); return; } diff --git a/mod/Jailbreak.LastRequest/LastRequestCommand.cs b/mod/Jailbreak.LastRequest/LastRequestCommand.cs index d01994ff..b0b71e67 100644 --- a/mod/Jailbreak.LastRequest/LastRequestCommand.cs +++ b/mod/Jailbreak.LastRequest/LastRequestCommand.cs @@ -5,6 +5,7 @@ using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Formatting.Extensions; using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.LastRequest; using Jailbreak.Public.Behaviors; using Jailbreak.Public.Extensions; using Jailbreak.Public.Mod.LastRequest; @@ -12,9 +13,8 @@ namespace Jailbreak.LastRequest; -public class LastRequestCommand(ILastRequestManager manager, - ILastRequestMessages messages, IGenericCommandNotifications generic, - ILastRequestFactory factory) : IPluginBehavior { +public class LastRequestCommand(ILastRequestManager manager, ILRLocale messages, + IGenericCmdLocale generic, ILastRequestFactory factory) : IPluginBehavior { private LastRequestMenuSelector? menuSelector; private LastRequestPlayerSelector? playerSelector; private BasePlugin? plugin; @@ -32,28 +32,28 @@ public void Command_LastRequest(CCSPlayerController? executor, CommandInfo info) { if (executor == null || !executor.IsReal()) return; if (!manager.IsLREnabled) { - messages.LastRequestNotEnabled().ToPlayerChat(executor); + messages.LastRequestNotEnabled().ToChat(executor); return; } if (executor.Team != CsTeam.Terrorist) { - messages.CannotLR("You are not a Prisoner").ToPlayerChat(executor); + messages.CannotLR("You are not a Prisoner").ToChat(executor); return; } if (!executor.PawnIsAlive) { - messages.CannotLR("You are not alive").ToPlayerChat(executor); + messages.CannotLR("You are not alive").ToChat(executor); return; } if (!playerSelector!.WouldHavePlayers()) { - messages.CannotLR("No players available to LR").ToPlayerChat(executor); + messages.CannotLR("No players available to LR").ToChat(executor); return; } if (manager.IsInLR(executor)) { - messages.CannotLR("You are already in an LR").ToPlayerChat(executor); + messages.CannotLR("You are already in an LR").ToChat(executor); return; } @@ -66,7 +66,7 @@ public void Command_LastRequest(CCSPlayerController? executor, // Validate LR var type = LRTypeExtensions.FromString(info.GetArg(1)); if (type is null) { - messages.InvalidLastRequest(info.GetArg(1)).ToPlayerChat(executor); + messages.InvalidLastRequest(info.GetArg(1)).ToChat(executor); return; } @@ -90,18 +90,17 @@ public void Command_LastRequest(CCSPlayerController? executor, var player = target.Players.First(); if (player.Team != CsTeam.CounterTerrorist) { - messages.CannotLR(player, "They are not a Guard").ToPlayerChat(executor); + messages.CannotLR(player, "They are not a Guard").ToChat(executor); return; } if (!player.PawnIsAlive) { - messages.CannotLR(player, "They are not alive").ToPlayerChat(executor); + messages.CannotLR(player, "They are not alive").ToChat(executor); return; } if (manager.IsInLR(player)) { - messages.CannotLR(player, "They are already in an LR") - .ToPlayerChat(executor); + messages.CannotLR(player, "They are already in an LR").ToChat(executor); return; } diff --git a/mod/Jailbreak.LastRequest/LastRequestFactory.cs b/mod/Jailbreak.LastRequest/LastRequestFactory.cs index eadf903a..af23fead 100644 --- a/mod/Jailbreak.LastRequest/LastRequestFactory.cs +++ b/mod/Jailbreak.LastRequest/LastRequestFactory.cs @@ -1,5 +1,5 @@ using CounterStrikeSharp.API.Core; -using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.LastRequest; using Jailbreak.LastRequest.LastRequests; using Jailbreak.Public.Mod.LastRequest; using Jailbreak.Public.Mod.LastRequest.Enums; @@ -9,21 +9,25 @@ namespace Jailbreak.LastRequest; public class LastRequestFactory(ILastRequestManager manager, IServiceProvider services) : ILastRequestFactory { - private BasePlugin? plugin; + private BasePlugin plugin = null!; public void Start(BasePlugin basePlugin) { plugin = basePlugin; } public AbstractLastRequest CreateLastRequest(CCSPlayerController prisoner, CCSPlayerController guard, LRType type) { return type switch { - LRType.KNIFE_FIGHT => new KnifeFight(plugin!, manager, prisoner, guard), - LRType.GUN_TOSS => new GunToss(plugin!, manager, prisoner, guard), - LRType.NO_SCOPE => new NoScope(plugin!, manager, prisoner, guard), - LRType.ROCK_PAPER_SCISSORS => new RockPaperScissors(plugin!, manager, + LRType.KNIFE_FIGHT => new KnifeFight(plugin, services, prisoner, guard), + LRType.GUN_TOSS => new GunToss(plugin, manager, prisoner, guard), + LRType.NO_SCOPE => new NoScope(plugin, services, prisoner, guard), + LRType.SHOT_FOR_SHOT => new BulletForBullet(plugin, services, prisoner, + guard, false), + LRType.ROCK_PAPER_SCISSORS => new RockPaperScissors(plugin, services, prisoner, guard), - LRType.COINFLIP => new Coinflip(plugin!, manager, prisoner, guard), - LRType.RACE => new Race(plugin!, manager, prisoner, guard, - services.GetRequiredService()), + LRType.COINFLIP => new Coinflip(plugin, services, prisoner, guard), + LRType.RACE => new Race(plugin, manager, prisoner, guard, + services.GetRequiredService()), + LRType.MAG_FOR_MAG => new BulletForBullet(plugin, services, prisoner, + guard, true), _ => throw new ArgumentException("Invalid last request type: " + type, nameof(type)) }; @@ -34,9 +38,11 @@ public bool IsValidType(LRType type) { LRType.KNIFE_FIGHT => true, LRType.GUN_TOSS => true, LRType.NO_SCOPE => true, + LRType.SHOT_FOR_SHOT => true, LRType.ROCK_PAPER_SCISSORS => true, LRType.COINFLIP => true, LRType.RACE => true, + LRType.MAG_FOR_MAG => true, _ => false }; } diff --git a/mod/Jailbreak.LastRequest/LastRequestManager.cs b/mod/Jailbreak.LastRequest/LastRequestManager.cs index 10358ecc..fd708a55 100644 --- a/mod/Jailbreak.LastRequest/LastRequestManager.cs +++ b/mod/Jailbreak.LastRequest/LastRequestManager.cs @@ -6,7 +6,7 @@ using CounterStrikeSharp.API.Modules.Menu; using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Formatting.Extensions; -using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.LastRequest; using Jailbreak.Public; using Jailbreak.Public.Extensions; using Jailbreak.Public.Mod.Damage; @@ -18,8 +18,8 @@ namespace Jailbreak.LastRequest; -public class LastRequestManager(ILastRequestMessages messages, - IServiceProvider provider) : ILastRequestManager, IBlockUserDamage { +public class LastRequestManager(ILRLocale messages, IServiceProvider provider) + : ILastRequestManager, IBlockUserDamage { public readonly FakeConVar CvLRBaseTime = new("css_jb_lr_time_base", "Round time to set when LR is activated, 0 to disable", 60); @@ -51,7 +51,7 @@ public bool ShouldBlockDamage(CCSPlayerController player, if (playerLR == null != (attackerLR == null)) { // One of them is in an LR - messages.DamageBlockedInsideLastRequest.ToPlayerCenter(attacker); + messages.DamageBlockedInsideLastRequest.ToCenter(attacker); return true; } @@ -62,7 +62,7 @@ public bool ShouldBlockDamage(CCSPlayerController player, // Same LR, allow damage return false; - messages.DamageBlockedNotInSameLR.ToPlayerCenter(attacker); + messages.DamageBlockedNotInSameLR.ToCenter(attacker); return true; } diff --git a/mod/Jailbreak.LastRequest/LastRequests/BulletForBullet.cs b/mod/Jailbreak.LastRequest/LastRequests/BulletForBullet.cs new file mode 100644 index 00000000..be73cb65 --- /dev/null +++ b/mod/Jailbreak.LastRequest/LastRequests/BulletForBullet.cs @@ -0,0 +1,160 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Menu; +using CounterStrikeSharp.API.Modules.Timers; +using Jailbreak.Formatting.Extensions; +using Jailbreak.Formatting.Views.LastRequest; +using Jailbreak.Public.Extensions; +using Jailbreak.Public.Mod.LastRequest; +using Jailbreak.Public.Mod.LastRequest.Enums; +using Microsoft.Extensions.DependencyInjection; + +namespace Jailbreak.LastRequest.LastRequests; + +public class BulletForBullet : TeleportingRequest { + private static readonly string[] GUNS = [ + "weapon_deagle", "weapon_usp_silencer", "weapon_tec9", "weapon_ssg08", + "weapon_nova" + ]; + + private static readonly string[] GUN_NAMES = [ + "Desert Eagle", "USP-S", "Tec9", "Scout", "Nova" + ]; + + private readonly ChatMenu chatMenu; + private readonly bool magForMag; + private readonly ILRB4BLocale msg; + private string? CHOSEN_PISTOL; + private int? whosShot, magSize; + + public BulletForBullet(BasePlugin plugin, IServiceProvider provider, + CCSPlayerController prisoner, CCSPlayerController guard, + bool magForMag) : base(plugin, + provider.GetRequiredService(), prisoner, guard) { + this.magForMag = magForMag; + chatMenu = new ChatMenu(magForMag ? "Mag for Mag" : "Shot for Shot"); + foreach (var pistol in GUNS) + chatMenu.AddMenuOption(pistol.GetFriendlyWeaponName(), OnSelect); + + msg = provider.GetRequiredService(); + } + + public override LRType Type + => magForMag ? LRType.MAG_FOR_MAG : LRType.SHOT_FOR_SHOT; + + private void OnSelect(CCSPlayerController player, ChatMenuOption option) { + if (player.Slot != Prisoner.Slot) return; + MenuManager.CloseActiveMenu(player); + + CHOSEN_PISTOL = GUNS[Array.IndexOf(GUN_NAMES, option.Text)]; + + msg.WeaponSelected(player, CHOSEN_PISTOL).ToChat(Prisoner, Guard); + + State = LRState.ACTIVE; + + Prisoner.RemoveWeapons(); + Guard.RemoveWeapons(); + Prisoner.GiveNamedItem(CHOSEN_PISTOL); + Guard.GiveNamedItem(CHOSEN_PISTOL); + + Plugin.AddTimer(0.5f, () => { + magSize = (magForMag ? + Prisoner.GetWeaponBase(CHOSEN_PISTOL)!.VData?.MaxClip1 : + 1) ?? 1; + Prisoner.GetWeaponBase(CHOSEN_PISTOL).SetAmmo(0, 0); + Guard.GetWeaponBase(CHOSEN_PISTOL).SetAmmo(0, 0); + var shooter = new Random().Next(2) == 0 ? Prisoner : Guard; + whosShot = shooter.Slot; + msg.PlayerGoesFirst(shooter).ToChat(Prisoner, Guard); + + shooter.GetWeaponBase(CHOSEN_PISTOL).SetAmmo(magSize.Value, 0); + }); + } + + public override void Setup() { + Plugin.RegisterEventHandler(OnPlayerShoot); + + Prisoner.RemoveWeapons(); + Guard.RemoveWeapons(); + + base.Setup(); + Execute(); + + CHOSEN_PISTOL = string.Empty; + chatMenu.Title = + $"{chatMenu.Title} - {Prisoner.PlayerName} vs {Guard.PlayerName}"; + } + + + public override void Execute() { + State = LRState.PENDING; + MenuManager.OpenChatMenu(Prisoner, chatMenu); + + Plugin.AddTimer(10, timeout, TimerFlags.STOP_ON_MAPCHANGE); + + Plugin.AddTimer(30, () => { + if (State != LRState.ACTIVE) return; + Prisoner.GiveNamedItem("weapon_knife"); + Guard.GiveNamedItem("weapon_knife"); + }); + Plugin.AddTimer(60, () => { + if (State != LRState.ACTIVE) return; + var result = Guard.Health > Prisoner.Health ? + LRResult.GUARD_WIN : + LRResult.PRISONER_WIN; + if (Guard.Health == Prisoner.Health) { + var winner = whosShot != Prisoner.Slot ? Prisoner : Guard; + msg.WinByReason(winner, "equal health"); + + result = whosShot == Prisoner.Slot ? + LRResult.GUARD_WIN : + LRResult.PRISONER_WIN; + } else { + msg.WinByHealth(result == LRResult.GUARD_WIN ? Guard : Prisoner); + } + + if (result == LRResult.GUARD_WIN) + Prisoner.Pawn.Value?.CommitSuicide(false, true); + else + Guard.Pawn.Value?.CommitSuicide(false, true); + }, TimerFlags.STOP_ON_MAPCHANGE); + } + + private void timeout() { + if (CHOSEN_PISTOL == string.Empty) + Manager.EndLastRequest(this, LRResult.TIMED_OUT); + } + + private HookResult OnPlayerShoot(EventBulletImpact @event, + GameEventInfo info) { + if (State != LRState.ACTIVE) return HookResult.Continue; + + var player = @event.Userid; + if (player == null || whosShot == null || !player.IsValid + || magSize == null) + return HookResult.Continue; + + if (player.Slot != Prisoner.Slot && player.Slot != Guard.Slot) + return HookResult.Continue; + if (player.Slot != whosShot) { + PrintToParticipants(player.PlayerName + " cheated."); + player.Pawn.Value?.CommitSuicide(false, true); + return HookResult.Handled; + } + + var bullets = player.GetWeaponBase(CHOSEN_PISTOL!)?.Clip1 ?? 1; + if (bullets > 1) return HookResult.Continue; + + Server.NextFrame(() => { + var opponent = player.Slot == Prisoner.Slot ? Guard : Prisoner; + whosShot = opponent.Slot; + opponent.GetWeaponBase(CHOSEN_PISTOL!)?.SetAmmo(magSize.Value, 0); + }); + return HookResult.Continue; + } + + public override void OnEnd(LRResult result) { + Plugin.DeregisterEventHandler(OnPlayerShoot); + State = LRState.COMPLETED; + } +} \ No newline at end of file diff --git a/mod/Jailbreak.LastRequest/LastRequests/Coinflip.cs b/mod/Jailbreak.LastRequest/LastRequests/Coinflip.cs index 0239841e..fa8bc3a6 100644 --- a/mod/Jailbreak.LastRequest/LastRequests/Coinflip.cs +++ b/mod/Jailbreak.LastRequest/LastRequests/Coinflip.cs @@ -1,7 +1,10 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Menu; +using Jailbreak.Formatting.Extensions; +using Jailbreak.Formatting.Views.LastRequest; using Jailbreak.Public.Mod.LastRequest; using Jailbreak.Public.Mod.LastRequest.Enums; +using Microsoft.Extensions.DependencyInjection; using Timer = CounterStrikeSharp.API.Modules.Timers.Timer; namespace Jailbreak.LastRequest.LastRequests; @@ -19,16 +22,18 @@ public class Coinflip : AbstractLastRequest { ]; private readonly ChatMenu menu; + private readonly ILRCFLocale msg; private readonly Random rnd; private Timer? timeout; - public Coinflip(BasePlugin plugin, ILastRequestManager manager, + public Coinflip(BasePlugin plugin, IServiceProvider provider, CCSPlayerController prisoner, CCSPlayerController guard) : base(plugin, - manager, prisoner, guard) { + provider.GetRequiredService(), prisoner, guard) { rnd = new Random(); menu = new ChatMenu("Heads or Tails?"); menu.AddMenuOption("Heads", (_, _) => decide(true, true)); menu.AddMenuOption("Tails", (_, _) => decide(false, true)); + msg = provider.GetRequiredService(); } public override LRType Type => LRType.COINFLIP; @@ -48,8 +53,7 @@ public override void Execute() { if (State != LRState.ACTIVE) return; MenuManager.CloseActiveMenu(Guard); var choice = rnd.Next(2) == 1; - Guard.PrintToChat( - $"You failed to choose in time, defaulting to {(choice ? "Heads" : "Tails")}"); + msg.FailedToChooseInTime(choice).ToChat(Guard); decide(choice, true); }); } @@ -58,8 +62,7 @@ private void decide(bool heads, bool print) { timeout?.Kill(); if (print) { MenuManager.CloseActiveMenu(Guard); - PrintToParticipants( - $"{Guard.PlayerName} chose {(heads ? "Heads" : "Tails")}... flipping..."); + msg.GuardChose(Guard, heads).ToChat(Guard, Prisoner); State = LRState.ACTIVE; } @@ -69,7 +72,7 @@ private void decide(bool heads, bool print) { Plugin.AddTimer(2, () => decide(heads, false)); } else { var side = rnd.Next(2) == 1; - PrintToParticipants($"The coin lands on {(side ? "Heads" : "Tails")}!"); + msg.CoinLandsOn(side).ToChat(Guard, Prisoner); Manager.EndLastRequest(this, side == heads ? LRResult.GUARD_WIN : LRResult.PRISONER_WIN); } diff --git a/mod/Jailbreak.LastRequest/LastRequests/KnifeFight.cs b/mod/Jailbreak.LastRequest/LastRequests/KnifeFight.cs index f04d5a30..4b1e38a2 100644 --- a/mod/Jailbreak.LastRequest/LastRequests/KnifeFight.cs +++ b/mod/Jailbreak.LastRequest/LastRequests/KnifeFight.cs @@ -1,16 +1,14 @@ using CounterStrikeSharp.API.Core; -using Jailbreak.Public.Mod.LastRequest; using Jailbreak.Public.Mod.LastRequest.Enums; namespace Jailbreak.LastRequest.LastRequests; -public class KnifeFight(BasePlugin plugin, ILastRequestManager manager, +public class KnifeFight(BasePlugin plugin, IServiceProvider provider, CCSPlayerController prisoner, CCSPlayerController guard) - : WeaponizedRequest(plugin, manager, prisoner, guard) { + : WeaponizedRequest(plugin, provider, prisoner, guard) { public override LRType Type => LRType.KNIFE_FIGHT; public override void Execute() { - PrintToParticipants("Go!"); Prisoner.GiveNamedItem("weapon_knife"); Guard.GiveNamedItem("weapon_knife"); State = LRState.ACTIVE; diff --git a/mod/Jailbreak.LastRequest/LastRequests/MagForMag.cs b/mod/Jailbreak.LastRequest/LastRequests/MagForMag.cs deleted file mode 100644 index bd129fc4..00000000 --- a/mod/Jailbreak.LastRequest/LastRequests/MagForMag.cs +++ /dev/null @@ -1,114 +0,0 @@ -using CounterStrikeSharp.API; -using CounterStrikeSharp.API.Core; -using CounterStrikeSharp.API.Modules.Timers; -using Jailbreak.Public.Extensions; -using Jailbreak.Public.Mod.LastRequest; -using Jailbreak.Public.Mod.LastRequest.Enums; - -namespace Jailbreak.LastRequest.LastRequests; - -public class MagForMag(BasePlugin plugin, ILastRequestManager manager, - CCSPlayerController prisoner, CCSPlayerController guard) - : WeaponizedRequest(plugin, manager, prisoner, guard) { - private const int BULLET_COUNT = 7; - private CCSPlayerController? whosShot; - public override LRType Type => LRType.GUN_TOSS; - - public override void Setup() { - Plugin.RegisterEventHandler(OnPlayerShoot); - base.Setup(); - - whosShot = new Random().Next(2) == 0 ? Prisoner : Guard; - PrintToParticipants(whosShot.PlayerName + " will shoot first."); - Prisoner.GiveNamedItem("weapon_deagle"); - Guard.GiveNamedItem("weapon_deagle"); - - var weapon = findWeapon(Prisoner, "weapon_deagle"); - if (weapon != null) setAmmoAmount(weapon, 0, 0); - weapon = findWeapon(Guard, "weapon_deagle"); - if (weapon != null) setAmmoAmount(weapon, 0, 0); - } - - private static CBasePlayerWeapon? findWeapon(CCSPlayerController player, - string name) { - if (!player.IsReal()) return null; - - var pawn = player.PlayerPawn.Value; - - if (pawn == null) return null; - - var weapons = pawn.WeaponServices?.MyWeapons; - - return weapons?.Select(weaponOpt => weaponOpt.Value) - .OfType() - .FirstOrDefault(weapon => weapon.DesignerName.Contains(name)); - } - - private static void setAmmoAmount(CBasePlayerWeapon weapon, int primary, - int reserve) { - weapon.Clip1 = primary; - Utilities.SetStateChanged(weapon, "CBasePlayerWeapon", "m_iClip1"); - weapon.Clip2 = reserve; - Utilities.SetStateChanged(weapon, "CBasePlayerWeapon", "m_pReserveAmmo"); - } - - public override void Execute() { - State = LRState.ACTIVE; - var deagle = findWeapon(whosShot!, "weapon_deagle"); - if (deagle != null) setAmmoAmount(deagle, BULLET_COUNT, 0); - - Plugin.AddTimer(30, () => { - if (State != LRState.ACTIVE) return; - Prisoner.GiveNamedItem("weapon_knife"); - Guard.GiveNamedItem("weapon_knife"); - }); - Plugin.AddTimer(60, () => { - if (State != LRState.ACTIVE) return; - PrintToParticipants("Time's Up!"); - var result = Guard.Health > Prisoner.Health ? - LRResult.GUARD_WIN : - LRResult.PRISONER_WIN; - if (Guard.Health == Prisoner.Health) { - PrintToParticipants("Even health, since " + whosShot!.PlayerName - + " had the shot last, they lose."); - result = whosShot.Slot == Prisoner.Slot ? - LRResult.GUARD_WIN : - LRResult.PRISONER_WIN; - } else { PrintToParticipants("Health was the deciding factor. "); } - - Manager.EndLastRequest(this, result); - if (result == LRResult.GUARD_WIN) - Prisoner.Pawn.Value?.CommitSuicide(false, true); - else - Guard.Pawn.Value?.CommitSuicide(false, true); - }, TimerFlags.STOP_ON_MAPCHANGE); - } - - public HookResult OnPlayerShoot(EventPlayerShoot @event, GameEventInfo info) { - if (State != LRState.ACTIVE) return HookResult.Continue; - - var player = @event.Userid; - if (!player.IsReal()) return HookResult.Continue; - - if (player!.Slot != Prisoner.Slot && player.Slot != Guard.Slot) - return HookResult.Continue; - - var shootersDeagle = findWeapon(player, "weapon_deagle"); - if (shootersDeagle == null) return HookResult.Continue; - - if (shootersDeagle.Clip1 != 0) return HookResult.Continue; - - PrintToParticipants(player.PlayerName + " has shot."); - var opponent = player.Slot == Prisoner.Slot ? Guard : Prisoner; - opponent.PrintToChat("Your shot"); - var deagle = findWeapon(opponent, "weapon_deagle"); - if (deagle != null) setAmmoAmount(deagle, 0, BULLET_COUNT); - whosShot = opponent; - return HookResult.Continue; - } - - public override void OnEnd(LRResult result) { - Plugin.RemoveListener(OnPlayerShoot); - State = LRState.COMPLETED; - } -} \ No newline at end of file diff --git a/mod/Jailbreak.LastRequest/LastRequests/NoScope.cs b/mod/Jailbreak.LastRequest/LastRequests/NoScope.cs index 01dbb85a..1d3a4654 100644 --- a/mod/Jailbreak.LastRequest/LastRequests/NoScope.cs +++ b/mod/Jailbreak.LastRequest/LastRequests/NoScope.cs @@ -2,14 +2,14 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Timers; using Jailbreak.Public.Extensions; -using Jailbreak.Public.Mod.LastRequest; using Jailbreak.Public.Mod.LastRequest.Enums; +using Microsoft.Extensions.DependencyInjection; namespace Jailbreak.LastRequest.LastRequests; -public class NoScope(BasePlugin plugin, ILastRequestManager manager, - CCSPlayerController prisoner, CCSPlayerController guard) - : WeaponizedRequest(plugin, manager, prisoner, guard) { +public class NoScope(BasePlugin plugin, IServiceProvider provider, + CCSPlayerController prisoner, CCSPlayerController guard) : WeaponizedRequest( + plugin, provider.GetRequiredService(), prisoner, guard) { public override LRType Type => LRType.NO_SCOPE; public override void Setup() { diff --git a/mod/Jailbreak.LastRequest/LastRequests/Race.cs b/mod/Jailbreak.LastRequest/LastRequests/Race.cs index 3c640d85..a6e3e21e 100644 --- a/mod/Jailbreak.LastRequest/LastRequests/Race.cs +++ b/mod/Jailbreak.LastRequest/LastRequests/Race.cs @@ -3,7 +3,7 @@ using CounterStrikeSharp.API.Modules.Timers; using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Formatting.Extensions; -using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.LastRequest; using Jailbreak.Public.Extensions; using Jailbreak.Public.Mod.Draw; using Jailbreak.Public.Mod.LastRequest; @@ -14,7 +14,7 @@ namespace Jailbreak.LastRequest.LastRequests; public class Race(BasePlugin plugin, ILastRequestManager manager, CCSPlayerController prisoner, CCSPlayerController guard, - IRaceLRMessages messages) + ILRRaceLocale messages) : TeleportingRequest(plugin, manager, prisoner, guard) { private DateTime raceStart; private Timer? raceTimer; @@ -38,9 +38,9 @@ public override void Setup() { Prisoner.GiveNamedItem("weapon_knife"); }); - messages.EndRaceInstruction.ToPlayerChat(Prisoner); + messages.EndRaceInstruction.ToChat(Prisoner); - messages.RaceStartingMessage(Prisoner).ToPlayerChat(Guard); + messages.RaceStartingMessage(Prisoner).ToChat(Guard); startLocation = Prisoner.Pawn.Value?.AbsOrigin?.Clone(); diff --git a/mod/Jailbreak.LastRequest/LastRequests/RockPaperScissors.cs b/mod/Jailbreak.LastRequest/LastRequests/RockPaperScissors.cs index cee777ed..a6759bec 100644 --- a/mod/Jailbreak.LastRequest/LastRequests/RockPaperScissors.cs +++ b/mod/Jailbreak.LastRequest/LastRequests/RockPaperScissors.cs @@ -1,19 +1,25 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Menu; using CounterStrikeSharp.API.Modules.Timers; +using Jailbreak.Formatting.Extensions; +using Jailbreak.Formatting.Views.LastRequest; using Jailbreak.Public.Mod.LastRequest; using Jailbreak.Public.Mod.LastRequest.Enums; +using Jailbreak.Public.Utils; +using Microsoft.Extensions.DependencyInjection; namespace Jailbreak.LastRequest.LastRequests; public class RockPaperScissors : AbstractLastRequest { private readonly ChatMenu chatMenu; + private readonly ILRRPSLocale msg; private int prisonerChoice = -1, guardChoice = -1; - public RockPaperScissors(BasePlugin plugin, ILastRequestManager manager, + public RockPaperScissors(BasePlugin plugin, IServiceProvider provider, CCSPlayerController prisoner, CCSPlayerController guard) : base(plugin, - manager, prisoner, guard) { + provider.GetRequiredService(), prisoner, guard) { chatMenu = new ChatMenu("Rock Paper Scissors"); + msg = provider.GetRequiredService(); foreach (var option in new[] { "Rock", "Paper", "Scissors" }) chatMenu.AddMenuOption(option, OnSelect); } @@ -32,8 +38,7 @@ private void OnSelect(CCSPlayerController player, ChatMenuOption option) { if (player.Slot != Prisoner.Slot && player.Slot != Guard.Slot) return; MenuManager.CloseActiveMenu(player); - var choice = Array.IndexOf(new[] { "Rock", "Paper", "Scissors" }, - option.Text); + var choice = Array.IndexOf(["Rock", "Paper", "Scissors"], option.Text); if (player.Slot == Prisoner.Slot) prisonerChoice = choice; @@ -41,13 +46,13 @@ private void OnSelect(CCSPlayerController player, ChatMenuOption option) { guardChoice = choice; if (prisonerChoice == -1 || guardChoice == -1) { - PrintToParticipants(player.PlayerName + " has made their choice..."); + msg.PlayerMadeChoice(player).ToChat(Prisoner, Guard); return; } - PrintToParticipants("Both players have made their choice!"); + msg.BothPlayersMadeChoice().ToChat(Prisoner, Guard); if (prisonerChoice == guardChoice) { - PrintToParticipants("It's a tie!"); + msg.Tie().ToAllChat(); Setup(); return; } @@ -67,7 +72,8 @@ public override void Execute() { MenuManager.OpenChatMenu(Prisoner, chatMenu); MenuManager.OpenChatMenu(Guard, chatMenu); - Plugin.AddTimer(20, timeout, TimerFlags.STOP_ON_MAPCHANGE); + Plugin.AddTimer(Math.Min(RoundUtil.GetTimeRemaining() - 1, 25), timeout, + TimerFlags.STOP_ON_MAPCHANGE); } private void timeout() { @@ -82,21 +88,15 @@ private void timeout() { public override void OnEnd(LRResult result) { State = LRState.COMPLETED; - if (result == LRResult.GUARD_WIN) - Prisoner.Pawn.Value!.CommitSuicide(false, true); - else if (result == LRResult.PRISONER_WIN) - Guard.Pawn.Value!.CommitSuicide(false, true); - - PrintToParticipants( - $"Prisoner chose {getChoice(prisonerChoice)}, Guard chose {getChoice(guardChoice)}"); - } + switch (result) { + case LRResult.GUARD_WIN: + Prisoner.Pawn.Value!.CommitSuicide(false, true); + break; + case LRResult.PRISONER_WIN: + Guard.Pawn.Value!.CommitSuicide(false, true); + break; + } - private string getChoice(int choice) { - return choice switch { - 0 => "Rock", - 1 => "Paper", - 2 => "Scissors", - _ => "Unknown" - }; + msg.Results(Guard, Prisoner, guardChoice, prisonerChoice).ToAllChat(); } } \ No newline at end of file diff --git a/mod/Jailbreak.LastRequest/LastRequests/ShotForShot.cs b/mod/Jailbreak.LastRequest/LastRequests/ShotForShot.cs deleted file mode 100644 index 920f2fb0..00000000 --- a/mod/Jailbreak.LastRequest/LastRequests/ShotForShot.cs +++ /dev/null @@ -1,115 +0,0 @@ -using CounterStrikeSharp.API; -using CounterStrikeSharp.API.Core; -using CounterStrikeSharp.API.Modules.Timers; -using Jailbreak.Public.Extensions; -using Jailbreak.Public.Mod.LastRequest; -using Jailbreak.Public.Mod.LastRequest.Enums; - -namespace Jailbreak.LastRequest.LastRequests; - -public class ShotForShot(BasePlugin plugin, ILastRequestManager manager, - CCSPlayerController prisoner, CCSPlayerController guard) - : WeaponizedRequest(plugin, manager, prisoner, guard) { - private CCSPlayerController? whosShot; - public override LRType Type => LRType.SHOT_FOR_SHOT; - - public override void Setup() { - Plugin.RegisterEventHandler(OnPlayerShoot); - base.Setup(); - - whosShot = new Random().Next(2) == 0 ? Prisoner : Guard; - PrintToParticipants(whosShot.PlayerName + " will shoot first."); - Prisoner.GiveNamedItem("weapon_deagle"); - Guard.GiveNamedItem("weapon_deagle"); - - var weapon = findWeapon(Prisoner, "weapon_deagle"); - if (weapon != null) setAmmoAmount(weapon, 0, 0); - weapon = findWeapon(Guard, "weapon_deagle"); - if (weapon != null) setAmmoAmount(weapon, 0, 0); - } - - private static CBasePlayerWeapon? findWeapon(CCSPlayerController player, - string name) { - if (!player.IsReal()) return null; - - var pawn = player.PlayerPawn.Value; - - if (pawn == null) return null; - - var weapons = pawn.WeaponServices?.MyWeapons; - - return weapons?.Select(weaponOpt => weaponOpt.Value) - .OfType() - .FirstOrDefault(weapon => weapon.DesignerName.Contains(name)); - } - - private static void setAmmoAmount(CBasePlayerWeapon weapon, int primary, - int reserve) { - weapon.Clip1 = primary; - Utilities.SetStateChanged(weapon, "CBasePlayerWeapon", "m_iClip1"); - weapon.Clip2 = reserve; - Utilities.SetStateChanged(weapon, "CBasePlayerWeapon", "m_pReserveAmmo"); - } - - public override void Execute() { - State = LRState.ACTIVE; - if (whosShot == null) return; - var deagle = findWeapon(whosShot, "weapon_deagle"); - if (deagle != null) setAmmoAmount(deagle, 1, 0); - - Plugin.AddTimer(30, () => { - if (State != LRState.ACTIVE) return; - Prisoner.GiveNamedItem("weapon_knife"); - Guard.GiveNamedItem("weapon_knife"); - }); - Plugin.AddTimer(60, () => { - if (State != LRState.ACTIVE) return; - PrintToParticipants("Time's Up!"); - var result = Guard.Health > Prisoner.Health ? - LRResult.GUARD_WIN : - LRResult.PRISONER_WIN; - if (Guard.Health == Prisoner.Health) { - PrintToParticipants("Even health, since " + whosShot.PlayerName - + " had the shot last, they lose."); - result = whosShot.Slot == Prisoner.Slot ? - LRResult.GUARD_WIN : - LRResult.PRISONER_WIN; - } else { PrintToParticipants("Health was the deciding factor. "); } - - if (result == LRResult.GUARD_WIN) - Prisoner.Pawn.Value?.CommitSuicide(false, true); - else - Guard.Pawn.Value?.CommitSuicide(false, true); - }, TimerFlags.STOP_ON_MAPCHANGE); - } - - private HookResult OnPlayerShoot(EventPlayerShoot @event, - GameEventInfo info) { - if (State != LRState.ACTIVE) return HookResult.Continue; - - var player = @event.Userid; - if (player == null || whosShot == null || !player.IsReal()) - return HookResult.Continue; - - if (player.Slot != Prisoner.Slot && player.Slot != Guard.Slot) - return HookResult.Continue; - if (player.Slot != whosShot.Slot) { - PrintToParticipants(player.PlayerName + " cheated."); - player.Pawn.Value?.CommitSuicide(false, true); - return HookResult.Handled; - } - - PrintToParticipants(player.PlayerName + " has shot."); - var opponent = player.Slot == Prisoner.Slot ? Guard : Prisoner; - opponent.PrintToChat("Your shot"); - var deagle = findWeapon(opponent, "weapon_deagle"); - if (deagle != null) setAmmoAmount(deagle, 1, 0); - whosShot = opponent; - return HookResult.Continue; - } - - public override void OnEnd(LRResult result) { - Plugin.RemoveListener(OnPlayerShoot); - State = LRState.COMPLETED; - } -} \ No newline at end of file diff --git a/mod/Jailbreak.LastRequest/LastRequests/WeaponizedRequest.cs b/mod/Jailbreak.LastRequest/LastRequests/WeaponizedRequest.cs index f7efe909..f96f2146 100644 --- a/mod/Jailbreak.LastRequest/LastRequests/WeaponizedRequest.cs +++ b/mod/Jailbreak.LastRequest/LastRequests/WeaponizedRequest.cs @@ -1,6 +1,9 @@ using CounterStrikeSharp.API.Core; +using Jailbreak.Formatting.Extensions; +using Jailbreak.Formatting.Views.LastRequest; using Jailbreak.Public.Mod.LastRequest; using Jailbreak.Public.Mod.LastRequest.Enums; +using Microsoft.Extensions.DependencyInjection; namespace Jailbreak.LastRequest.LastRequests; @@ -9,17 +12,19 @@ namespace Jailbreak.LastRequest.LastRequests; /// Automatically strips weapons, counts down, and calls Execute after 4 seconds. /// public abstract class WeaponizedRequest(BasePlugin plugin, - ILastRequestManager manager, CCSPlayerController prisoner, - CCSPlayerController guard) - : TeleportingRequest(plugin, manager, prisoner, guard) { + IServiceProvider provider, CCSPlayerController prisoner, + CCSPlayerController guard) : TeleportingRequest(plugin, + provider.GetRequiredService(), prisoner, guard) { public override void Setup() { base.Setup(); Prisoner.RemoveWeapons(); Guard.RemoveWeapons(); + var msgs = provider.GetRequiredService(); for (var i = 3; i >= 1; i--) { var copy = i; - Plugin.AddTimer(3 - i, () => { PrintToParticipants($"{copy}..."); }); + Plugin.AddTimer(3 - i, + () => { msgs.LastRequestCountdown(copy).ToChat(Prisoner, Guard); }); } Plugin.AddTimer(3, () => { diff --git a/mod/Jailbreak.Logs/LogsManager.cs b/mod/Jailbreak.Logs/LogsManager.cs index 10f76f43..8a101e2f 100644 --- a/mod/Jailbreak.Logs/LogsManager.cs +++ b/mod/Jailbreak.Logs/LogsManager.cs @@ -11,12 +11,12 @@ namespace Jailbreak.Logs; -public class LogsManager(ILogMessages messages, IRichPlayerTag richPlayerTag) +public class LogsManager(ILogLocale locale, IRichPlayerTag richPlayerTag) : IPluginBehavior, IRichLogService { private readonly List logMessages = []; public void Append(string message) { - logMessages.Add(messages.CreateLog(message)); + logMessages.Add(locale.CreateLog(message)); } public IEnumerable GetMessages() { @@ -27,20 +27,20 @@ public IEnumerable GetMessages() { public void PrintLogs(CCSPlayerController? player) { if (player == null || !player.IsReal()) { - messages.BeginJailbreakLogs.ToServerConsole(); + locale.BeginJailbreakLogs.ToServerConsole(); foreach (var log in logMessages) log.ToServerConsole(); - messages.EndJailbreakLogs.ToServerConsole(); + locale.EndJailbreakLogs.ToServerConsole(); return; } - messages.BeginJailbreakLogs.ToPlayerConsole(player); - foreach (var log in logMessages) log.ToPlayerConsole(player); - messages.EndJailbreakLogs.ToPlayerConsole(player); + locale.BeginJailbreakLogs.ToConsole(player); + foreach (var log in logMessages) log.ToConsole(player); + locale.EndJailbreakLogs.ToConsole(player); } public void Append(params FormatObject[] objects) { - logMessages.Add(messages.CreateLog(objects)); + logMessages.Add(locale.CreateLog(objects)); } public FormatObject Player(CCSPlayerController playerController) { @@ -53,12 +53,12 @@ public FormatObject Player(CCSPlayerController playerController) { [GameEventHandler] public HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info) { - messages.BeginJailbreakLogs.ToServerConsole().ToAllConsole(); + locale.BeginJailbreakLogs.ToServerConsole().ToAllConsole(); // By default, print all logs to player consoles at the end of the round. foreach (var log in logMessages) log.ToServerConsole().ToAllConsole(); - messages.EndJailbreakLogs.ToServerConsole().ToAllConsole(); + locale.EndJailbreakLogs.ToServerConsole().ToAllConsole(); return HookResult.Continue; } diff --git a/mod/Jailbreak.Mute/MuteSystem.cs b/mod/Jailbreak.Mute/MuteSystem.cs index 44c4de77..89eb9de3 100644 --- a/mod/Jailbreak.Mute/MuteSystem.cs +++ b/mod/Jailbreak.Mute/MuteSystem.cs @@ -4,7 +4,7 @@ using CounterStrikeSharp.API.Modules.Admin; using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Formatting.Extensions; -using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.Warden; using Jailbreak.Public.Behaviors; using Jailbreak.Public.Extensions; using Jailbreak.Public.Mod.Mute; @@ -19,7 +19,7 @@ public class MuteSystem(IServiceProvider provider) private DateTime ctPeaceEnd = DateTime.MinValue; private DateTime lastPeace = DateTime.MinValue; - private IPeaceMessages? messages; + private IWardenPeaceLocale? messages; private BasePlugin? parent; private DateTime peaceEnd = DateTime.MinValue; @@ -72,7 +72,7 @@ public void UnPeaceMute() { public void Start(BasePlugin basePlugin) { parent = basePlugin; - messages = provider.GetRequiredService(); + messages = provider.GetRequiredService(); warden = provider.GetRequiredService(); basePlugin.RegisterListener(OnPlayerSpeak); @@ -150,14 +150,14 @@ private void OnPlayerSpeak(int playerSlot) { if (!player.PawnIsAlive && !bypassMute(player)) { // Normal players can't speak when dead - messages!.DeadReminder.ToPlayerCenter(player); + messages!.DeadReminder.ToCenter(player); mute(player); return; } if (isMuted(player)) { // Remind any muted players they're muted - messages!.MuteReminder.ToPlayerCenter(player); + messages!.MuteReminder.ToCenter(player); return; } @@ -167,10 +167,10 @@ private void OnPlayerSpeak(int playerSlot) { if (IsPeaceEnabled()) { if (player.Team == CsTeam.CounterTerrorist && DateTime.Now >= ctPeaceEnd) return; - messages!.PeaceReminder.ToPlayerCenter(player); + messages!.PeaceReminder.ToCenter(player); } - if (!player.PawnIsAlive) messages!.AdminDeadReminder.ToPlayerCenter(player); + if (!player.PawnIsAlive) messages!.AdminDeadReminder.ToCenter(player); } private bool isMuted(CCSPlayerController player) { diff --git a/mod/Jailbreak.Rebel/JihadC4/JihadC4Behavior.cs b/mod/Jailbreak.Rebel/C4Bomb/C4Behavior.cs similarity index 86% rename from mod/Jailbreak.Rebel/JihadC4/JihadC4Behavior.cs rename to mod/Jailbreak.Rebel/C4Bomb/C4Behavior.cs index edf7a479..fc3a3a35 100644 --- a/mod/Jailbreak.Rebel/JihadC4/JihadC4Behavior.cs +++ b/mod/Jailbreak.Rebel/C4Bomb/C4Behavior.cs @@ -5,15 +5,17 @@ using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Formatting.Extensions; using Jailbreak.Formatting.Views; +using Jailbreak.Public; using Jailbreak.Public.Behaviors; using Jailbreak.Public.Extensions; using Jailbreak.Public.Mod.Rebel; +using MStatsShared; -namespace Jailbreak.Rebel.JihadC4; +namespace Jailbreak.Rebel.C4Bomb; -public class JihadC4Behavior(IJihadC4Notifications jihadC4Notifications, - IRebelService rebelService) : IPluginBehavior, IJihadC4Service { - private readonly Dictionary bombs = new(); +public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService) + : IPluginBehavior, IC4Service { + private readonly Dictionary bombs = new(); // EmitSound(CBaseEntity* pEnt, const char* sSoundName, int nPitch, float flVolume, float flDelay) private readonly MemoryFunctionVoid @@ -27,15 +29,19 @@ private readonly MemoryFunctionVoid public void TryGiveC4ToPlayer(CCSPlayerController player) { var bombEntity = new CC4(player.GiveNamedItem("weapon_c4")); - bombs.Add(bombEntity, new JihadBombMetadata(0.75f, false)); + bombs.Add(bombEntity, new C4Metadata(0.75f, false)); - jihadC4Notifications.JihadC4Received.ToPlayerChat(player); - jihadC4Notifications.JihadC4Usage1.ToPlayerChat(player); + ic4Locale.JihadC4Received.ToChat(player); + ic4Locale.JihadC4Usage1.ToChat(player); } public void StartDetonationAttempt(CCSPlayerController player, float delay, CC4 bombEntity) { if (plugin == null) return; + var pos = player.Pawn.Value?.AbsOrigin; + if (pos != null) + API.Stats?.PushStat(new ServerStat("JB_BOMB_ATTEMPT", + $"{pos.X:F2} {pos.Y:F2} {pos.Z:F2}")); tryEmitSound(player, "jb.jihad", 1, 1f, 0f); @@ -139,6 +145,7 @@ private void detonate(CCSPlayerController player, CC4 bomb) { new QAngle(), new Vector()); particleSystemEntity.DispatchSpawn(); + var killed = 0; /* Calculate damage here, only applies to alive CTs. */ foreach (var ct in Utilities.GetPlayers() .Where(p => p is { @@ -153,13 +160,18 @@ private void detonate(CCSPlayerController player, CC4 bomb) { var damage = 340f; damage *= (350f - distanceFromBomb) / 350f; float healthRef = ct.PlayerPawn.Value.Health; - if (healthRef <= damage) { ct.CommitSuicide(true, true); } else { + if (healthRef <= damage) { + ct.CommitSuicide(true, true); + killed++; + } else { ct.PlayerPawn.Value.Health -= (int)damage; Utilities.SetStateChanged(ct.PlayerPawn.Value, "CBaseEntity", "m_iHealth"); } } + API.Stats?.PushStat(new ServerStat("JB_BOMB_EXPLODED", killed.ToString())); + // If they didn't have the C4 make sure to remove it. player.CommitSuicide(true, true); bombs.Remove(bomb); @@ -171,7 +183,7 @@ private void tryEmitSound(CBaseEntity entity, string soundEventName, volume, delay); } - private class JihadBombMetadata(float delay, bool isDetonating) { + private class C4Metadata(float delay, bool isDetonating) { public float Delay { get; set; } = delay; public bool IsDetonating { get; set; } = isDetonating; } diff --git a/mod/Jailbreak.Rebel/RebelManager.cs b/mod/Jailbreak.Rebel/RebelManager.cs index 18fb099b..24998c86 100644 --- a/mod/Jailbreak.Rebel/RebelManager.cs +++ b/mod/Jailbreak.Rebel/RebelManager.cs @@ -15,7 +15,7 @@ namespace Jailbreak.Rebel; -public class RebelManager(IRebelNotifications notifs, IRichLogService logs) +public class RebelManager(IRebelLocale notifs, IRichLogService logs) : IPluginBehavior, IRebelService { [Obsolete("No longer used, use FakeConvar")] public static readonly int MAX_REBEL_TIME = 45; @@ -80,7 +80,7 @@ public bool MarkRebel(CCSPlayerController player, long time = -1) { public void UnmarkRebel(CCSPlayerController player) { if (rebelTimes.ContainsKey(player)) { - notifs.NoLongerRebel.ToPlayerChat(player); + notifs.NoLongerRebel.ToChat(player); logs.Append(logs.Player(player), "is no longer a rebel."); } diff --git a/mod/Jailbreak.Rebel/RebelServiceExtension.cs b/mod/Jailbreak.Rebel/RebelServiceExtension.cs index c12f2af0..49ad597f 100644 --- a/mod/Jailbreak.Rebel/RebelServiceExtension.cs +++ b/mod/Jailbreak.Rebel/RebelServiceExtension.cs @@ -1,6 +1,6 @@ using Jailbreak.Public.Extensions; using Jailbreak.Public.Mod.Rebel; -using Jailbreak.Rebel.JihadC4; +using Jailbreak.Rebel.C4Bomb; using Microsoft.Extensions.DependencyInjection; namespace Jailbreak.Rebel; @@ -8,7 +8,7 @@ namespace Jailbreak.Rebel; public static class RebelServiceExtension { public static void AddJailbreakRebel(this IServiceCollection collection) { collection.AddPluginBehavior(); - collection.AddPluginBehavior(); + collection.AddPluginBehavior(); collection.AddPluginBehavior(); } } \ No newline at end of file diff --git a/mod/Jailbreak.SpecialDay/Jailbreak.SpecialDay.csproj b/mod/Jailbreak.SpecialDay/Jailbreak.SpecialDay.csproj index d3abdadc..74aed67d 100644 --- a/mod/Jailbreak.SpecialDay/Jailbreak.SpecialDay.csproj +++ b/mod/Jailbreak.SpecialDay/Jailbreak.SpecialDay.csproj @@ -9,6 +9,7 @@ + diff --git a/mod/Jailbreak.SpecialDay/SpecialDayCommand.cs b/mod/Jailbreak.SpecialDay/SpecialDayCommand.cs index 386dd4a2..49b8c832 100644 --- a/mod/Jailbreak.SpecialDay/SpecialDayCommand.cs +++ b/mod/Jailbreak.SpecialDay/SpecialDayCommand.cs @@ -7,8 +7,8 @@ using CounterStrikeSharp.API.Modules.Menu; using Jailbreak.Formatting.Extensions; using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.Warden; using Jailbreak.Public.Behaviors; -using Jailbreak.Public.Extensions; using Jailbreak.Public.Mod.SpecialDay; using Jailbreak.Public.Mod.SpecialDay.Enums; using Jailbreak.Public.Mod.Warden; @@ -18,8 +18,8 @@ namespace Jailbreak.SpecialDay; public class SpecialDayCommand(IWardenService warden, - ISpecialDayFactory factory, IWardenNotifications wardenMsg, - ISpecialDayMessages sdMsg, ISpecialDayManager sd) : IPluginBehavior { + ISpecialDayFactory factory, IWardenLocale wardenMsg, ISDLocale sdMsg, + ISpecialDayManager sd) : IPluginBehavior { public static FakeConVar CvRoundsBetweenSD = new( "css_jb_sd_round_cooldown", "Rounds between special days", 5); @@ -43,26 +43,25 @@ public void Command_SpecialDay(CCSPlayerController? executor, CommandInfo info) { if (executor != null && !AdminManager.PlayerHasPermissions(executor, "@css/rcon")) { - if (!warden.IsWarden(executor)) { - wardenMsg.NotWarden.ToPlayerChat(executor); + if (!warden.IsWarden(executor) || RoundUtil.IsWarmup()) { + wardenMsg.NotWarden.ToChat(executor); return; } if (sd.IsSDRunning) { // SD is already running if (sd.CurrentSD is ISpecialDayMessageProvider messaged) - sdMsg.SpecialDayRunning(messaged.Messages.Name) - .ToPlayerChat(executor); + sdMsg.SpecialDayRunning(messaged.Locale.Name).ToChat(executor); else sdMsg.SpecialDayRunning(sd.CurrentSD?.Type.ToString() ?? "Unknown") - .ToPlayerChat(executor); + .ToChat(executor); return; } var roundsToNext = sd.RoundsSinceLastSD - CvRoundsBetweenSD.Value; if (roundsToNext < 0) { - sdMsg.SpecialDayCooldown(Math.Abs(roundsToNext)).ToPlayerChat(executor); + sdMsg.SpecialDayCooldown(Math.Abs(roundsToNext)).ToChat(executor); return; } @@ -87,7 +86,7 @@ public void Command_SpecialDay(CCSPlayerController? executor, var type = SDTypeExtensions.FromString(info.GetArg(1)); if (type == null) { if (executor != null) - sdMsg.InvalidSpecialDay(info.GetArg(1)).ToPlayerChat(executor); + sdMsg.InvalidSpecialDay(info.GetArg(1)).ToChat(executor); return; } diff --git a/mod/Jailbreak.SpecialDay/SpecialDayFactory.cs b/mod/Jailbreak.SpecialDay/SpecialDayFactory.cs index 95e07b0a..951e645d 100644 --- a/mod/Jailbreak.SpecialDay/SpecialDayFactory.cs +++ b/mod/Jailbreak.SpecialDay/SpecialDayFactory.cs @@ -18,6 +18,9 @@ public AbstractSpecialDay CreateSpecialDay(SDType type) { SDType.NOSCOPE => new NoScopeDay(plugin, provider), SDType.INFECTION => new InfectionDay(plugin, provider), SDType.CUSTOM => new CustomDay(plugin, provider), + SDType.SPEEDRUN => new SpeedrunDay(plugin, provider), + SDType.OITC => new OneInTheChamberDay(plugin, provider), + SDType.TELEPORT => new TeleportDay(plugin, provider), _ => throw new NotImplementedException() }; } @@ -26,6 +29,6 @@ public bool IsValidType(SDType type) { try { CreateSpecialDay(type); return true; - } catch (NotImplementedException e) { return false; } + } catch (NotImplementedException) { return false; } } } \ No newline at end of file diff --git a/mod/Jailbreak.SpecialDay/SpecialDayManager.cs b/mod/Jailbreak.SpecialDay/SpecialDayManager.cs index 9f75ab59..c2b6491e 100644 --- a/mod/Jailbreak.SpecialDay/SpecialDayManager.cs +++ b/mod/Jailbreak.SpecialDay/SpecialDayManager.cs @@ -22,7 +22,7 @@ public bool InitiateSpecialDay(SDType type) { CurrentSD = factory.CreateSpecialDay(type); IsSDRunning = true; if (CurrentSD is ISpecialDayMessageProvider messaged) - messaged.Messages.SpecialDayStart.ToAllChat(); + messaged.Locale.SpecialDayStart.ToAllChat(); CurrentSD.Setup(); return true; @@ -40,7 +40,7 @@ public HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info) { if (!IsSDRunning || CurrentSD == null) return HookResult.Continue; IsSDRunning = false; if (CurrentSD is ISpecialDayMessageProvider messaged) - messaged.Messages.SpecialDayEnd().ToAllChat(); + messaged.Locale.SpecialDayEnd.ToAllChat(); CurrentSD = null; return HookResult.Continue; } diff --git a/mod/Jailbreak.SpecialDay/SpecialDayMenuSelector.cs b/mod/Jailbreak.SpecialDay/SpecialDayMenuSelector.cs index 32a0741c..45a5213c 100644 --- a/mod/Jailbreak.SpecialDay/SpecialDayMenuSelector.cs +++ b/mod/Jailbreak.SpecialDay/SpecialDayMenuSelector.cs @@ -1,5 +1,6 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Menu; +using Jailbreak.Public.Extensions; using Jailbreak.Public.Mod.SpecialDay; using Jailbreak.Public.Mod.SpecialDay.Enums; using Jailbreak.SpecialDay.SpecialDays; @@ -17,12 +18,17 @@ public SpecialDayMenuSelector(ISpecialDayFactory factory, Func command, BasePlugin plugin) { this.command = command; menu = new CenterHtmlMenu("css_sd [SD]", plugin); - foreach (SDType sd in Enum.GetValues(typeof(SDType))) { + var types = Enum.GetValues(typeof(SDType)); + // Randomize the order of the special days + var randomized = types.Cast().ToList(); + randomized.Shuffle(); + + foreach (var sd in randomized) { if (!factory.IsValidType(sd)) continue; var inst = factory.CreateSpecialDay(sd); var name = inst.Type.ToString(); if (inst is ISpecialDayMessageProvider messaged) - name = messaged.Messages.Name; + name = messaged.Locale.Name; menu.AddMenuOption(name, (p, _) => OnSelectSD(p, sd)); } } diff --git a/mod/Jailbreak.SpecialDay/SpecialDays/ArmoryRestrictedDay.cs b/mod/Jailbreak.SpecialDay/SpecialDays/AbstractArmoryRestrictedDay.cs similarity index 66% rename from mod/Jailbreak.SpecialDay/SpecialDays/ArmoryRestrictedDay.cs rename to mod/Jailbreak.SpecialDay/SpecialDays/AbstractArmoryRestrictedDay.cs index d218704d..932ee98f 100644 --- a/mod/Jailbreak.SpecialDay/SpecialDays/ArmoryRestrictedDay.cs +++ b/mod/Jailbreak.SpecialDay/SpecialDays/AbstractArmoryRestrictedDay.cs @@ -1,27 +1,32 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.English.SpecialDay; using Jailbreak.Formatting.Base; -using Jailbreak.Formatting.Extensions; -using Jailbreak.Formatting.Views; using Jailbreak.Public.Mod.Zones; using Jailbreak.Zones; using Microsoft.Extensions.DependencyInjection; namespace Jailbreak.SpecialDay.SpecialDays; -public abstract class ArmoryRestrictedDay(BasePlugin plugin, - IServiceProvider provider, CsTeam restrictedTeam = CsTeam.Terrorist) - : ZoneRestrictedDay(plugin, provider, restrictedTeam) { +public abstract class AbstractArmoryRestrictedDay : AbstractZoneRestrictedDay { + private readonly IServiceProvider provider; + + protected AbstractArmoryRestrictedDay(BasePlugin plugin, + IServiceProvider provider, + CsTeam restrictedTeam = CsTeam.Terrorist) : base(plugin, provider, + restrictedTeam) { + this.provider = provider; + } + public override IView ZoneReminder => ArmoryReminder; public virtual IView ArmoryReminder => this is ISpecialDayMessageProvider messaged ? new SimpleView { - ISpecialDayMessages.PREFIX, - $"Today is {messaged.Messages.Name}, so stay in armory!" + SDLocale.PREFIX, $"Today is {messaged.Locale.Name}, so stay in armory!" } : - new SimpleView { ISpecialDayMessages.PREFIX, "Stay in armory!" }; + new SimpleView { SDLocale.PREFIX, "Stay in armory!" }; override protected IZone GetZone() { diff --git a/mod/Jailbreak.SpecialDay/SpecialDays/CellRestrictedDay.cs b/mod/Jailbreak.SpecialDay/SpecialDays/AbstractCellRestrictedDay.cs similarity index 65% rename from mod/Jailbreak.SpecialDay/SpecialDays/CellRestrictedDay.cs rename to mod/Jailbreak.SpecialDay/SpecialDays/AbstractCellRestrictedDay.cs index 1bf5832d..71e2d0a4 100644 --- a/mod/Jailbreak.SpecialDay/SpecialDays/CellRestrictedDay.cs +++ b/mod/Jailbreak.SpecialDay/SpecialDays/AbstractCellRestrictedDay.cs @@ -1,26 +1,32 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.English.SpecialDay; using Jailbreak.Formatting.Base; -using Jailbreak.Formatting.Views; using Jailbreak.Public.Mod.Zones; using Jailbreak.Zones; using Microsoft.Extensions.DependencyInjection; namespace Jailbreak.SpecialDay.SpecialDays; -public abstract class CellRestrictedDay(BasePlugin plugin, - IServiceProvider provider, CsTeam restrictedTeam = CsTeam.Terrorist) - : ZoneRestrictedDay(plugin, provider, restrictedTeam) { +public abstract class AbstractCellRestrictedDay : AbstractZoneRestrictedDay { + private readonly IServiceProvider provider; + + protected AbstractCellRestrictedDay(BasePlugin plugin, + IServiceProvider provider, + CsTeam restrictedTeam = CsTeam.Terrorist) : base(plugin, provider, + restrictedTeam) { + this.provider = provider; + } + public override IView ZoneReminder => CellReminder; public virtual IView CellReminder => this is ISpecialDayMessageProvider messaged ? new SimpleView { - ISpecialDayMessages.PREFIX, - $"Today is {messaged.Messages.Name}, so stay in cells!" + SDLocale.PREFIX, $"Today is {messaged.Locale.Name}, so stay in cells!" } : - new SimpleView { ISpecialDayMessages.PREFIX, "Stay in cells!" }; + new SimpleView { SDLocale.PREFIX, "Stay in cells!" }; override protected IZone GetZone() { var manager = provider.GetRequiredService(); diff --git a/mod/Jailbreak.SpecialDay/SpecialDays/ZoneRestrictedDay.cs b/mod/Jailbreak.SpecialDay/SpecialDays/AbstractZoneRestrictedDay.cs similarity index 57% rename from mod/Jailbreak.SpecialDay/SpecialDays/ZoneRestrictedDay.cs rename to mod/Jailbreak.SpecialDay/SpecialDays/AbstractZoneRestrictedDay.cs index 9521c549..0b11d323 100644 --- a/mod/Jailbreak.SpecialDay/SpecialDays/ZoneRestrictedDay.cs +++ b/mod/Jailbreak.SpecialDay/SpecialDays/AbstractZoneRestrictedDay.cs @@ -10,12 +10,18 @@ namespace Jailbreak.SpecialDay.SpecialDays; -public abstract class ZoneRestrictedDay(BasePlugin plugin, - IServiceProvider provider, CsTeam restrictedTeam = CsTeam.Terrorist) - : AbstractSpecialDay(plugin, provider) { +public abstract class AbstractZoneRestrictedDay : AbstractSpecialDay { + protected readonly CsTeam RestrictedTeam; + protected readonly IList Restrictors = new List(); + protected AbstractZoneRestrictedDay(BasePlugin plugin, + IServiceProvider provider, + CsTeam restrictedTeam = CsTeam.Terrorist) : base(plugin, provider) { + RestrictedTeam = restrictedTeam; + } + public abstract IView ZoneReminder { get; } abstract protected IZone GetZone(); @@ -23,12 +29,12 @@ public abstract class ZoneRestrictedDay(BasePlugin plugin, public override void Setup() { base.Setup(); - ZoneReminder.ToTeamChat(restrictedTeam); - GetZone().Draw(plugin, Color.Firebrick, 55); + ZoneReminder.ToTeamChat(RestrictedTeam); + GetZone().Draw(Plugin, Color.Firebrick, 55); - foreach (var t in PlayerUtil.FromTeam(restrictedTeam)) { - var zoneRestrictor = new ZoneMovementRestrictor(plugin, t, GetZone(), - DistanceZone.WIDTH_CELL, () => ZoneReminder.ToPlayerChat(t)); + foreach (var t in PlayerUtil.FromTeam(RestrictedTeam)) { + var zoneRestrictor = new ZoneMovementRestrictor(Plugin, t, GetZone(), + DistanceZone.WIDTH_CELL, () => ZoneReminder.ToChat(t)); Restrictors.Add(zoneRestrictor); } } @@ -36,13 +42,14 @@ public override void Setup() { public override void Execute() { base.Execute(); if (this is ISpecialDayMessageProvider messaged) - messaged.Messages.BeginsIn(0).ToAllChat(); + messaged.Locale.BeginsIn(0).ToAllChat(); foreach (var restrictor in Restrictors) restrictor.Kill(); Restrictors.Clear(); } - public override HookResult OnEnd(EventRoundEnd @event, GameEventInfo info) { + override protected HookResult + OnEnd(EventRoundEnd @event, GameEventInfo info) { var result = base.OnEnd(@event, info); foreach (var restrictor in Restrictors) restrictor.Kill(); Restrictors.Clear(); diff --git a/mod/Jailbreak.SpecialDay/SpecialDays/CustomDay.cs b/mod/Jailbreak.SpecialDay/SpecialDays/CustomDay.cs index 03a1ff53..b0b2c6c3 100644 --- a/mod/Jailbreak.SpecialDay/SpecialDays/CustomDay.cs +++ b/mod/Jailbreak.SpecialDay/SpecialDays/CustomDay.cs @@ -18,8 +18,8 @@ public override SpecialDaySettings Settings OpenCells = false }; - public ISpecialDayInstanceMessages Messages - => new TeamDayMessages("Custom Day", + public ISDInstanceLocale Locale + => new TeamDayLocale("Custom Day", "Listen to the Warden's orders. Anything goes!"); public override void Setup() { diff --git a/mod/Jailbreak.SpecialDay/SpecialDays/FFADay.cs b/mod/Jailbreak.SpecialDay/SpecialDays/FFADay.cs index 08c5dcd5..6f85d29e 100644 --- a/mod/Jailbreak.SpecialDay/SpecialDays/FFADay.cs +++ b/mod/Jailbreak.SpecialDay/SpecialDays/FFADay.cs @@ -12,20 +12,20 @@ public class FFADay(BasePlugin plugin, IServiceProvider provider) public override SDType Type => SDType.FFA; public override SpecialDaySettings Settings => new FFASettings(); - public virtual ISpecialDayInstanceMessages Messages - => new SoloDayMessages("Free for All", + public virtual ISDInstanceLocale Locale + => new SoloDayLocale("Free for All", "Everyone for themselves! No camping, actively pursue!"); public override void Setup() { - Timers[10] += () => Messages.BeginsIn(10).ToAllChat(); - Timers[15] += () => Messages.BeginsIn(5).ToAllChat(); + Timers[10] += () => Locale.BeginsIn(10).ToAllChat(); + Timers[15] += () => Locale.BeginsIn(5).ToAllChat(); Timers[20] += Execute; base.Setup(); } public override void Execute() { base.Execute(); - Messages.BeginsIn(0).ToAllChat(); + Locale.BeginsIn(0).ToAllChat(); } public class FFASettings : SpecialDaySettings { diff --git a/mod/Jailbreak.SpecialDay/SpecialDays/HideAndSeekDay.cs b/mod/Jailbreak.SpecialDay/SpecialDays/HideAndSeekDay.cs index 4b4f2072..2a21ae4f 100644 --- a/mod/Jailbreak.SpecialDay/SpecialDays/HideAndSeekDay.cs +++ b/mod/Jailbreak.SpecialDay/SpecialDays/HideAndSeekDay.cs @@ -12,46 +12,43 @@ namespace Jailbreak.SpecialDay.SpecialDays; public class HideAndSeekDay(BasePlugin plugin, IServiceProvider provider) - : ArmoryRestrictedDay(plugin, provider), ISpecialDayMessageProvider { + : AbstractArmoryRestrictedDay(plugin, provider), ISpecialDayMessageProvider { public override SDType Type => SDType.HNS; - private HNSDayMessages msg => (HNSDayMessages)Messages; + private HNSDayLocale msg => (HNSDayLocale)Locale; public override SpecialDaySettings Settings => new HNSSettings(); public override IView ArmoryReminder => msg.StayInArmory; - public ISpecialDayInstanceMessages Messages => new HNSDayMessages(); + public ISDInstanceLocale Locale => new HNSDayLocale(); public override void Setup() { - Timers[5] += () => msg.DamageWarning(5).ToTeamChat(CsTeam.CounterTerrorist); Timers[10] += () => { - foreach (var ct in PlayerUtil.FromTeam(CsTeam.CounterTerrorist)) { + foreach (var ct in PlayerUtil.FromTeam(CsTeam.CounterTerrorist)) ct.SetSpeed(1.5f); - EnableDamage(ct); - } - ((ISpecialDayMessageProvider)this).Messages.BeginsIn(25).ToAllChat(); + msg.DamageWarning(15).ToTeamChat(CsTeam.CounterTerrorist); + + Locale.BeginsIn(35).ToAllChat(); }; Timers[25] += () => { foreach (var ct in PlayerUtil.FromTeam(CsTeam.CounterTerrorist)) { ct.SetSpeed(1.25f); EnableDamage(ct); } - - Messages.BeginsIn(10).ToAllChat(); }; Timers[30] += () => { foreach (var ct in PlayerUtil.FromTeam(CsTeam.CounterTerrorist)) ct.SetSpeed(1.1f); + Locale.BeginsIn(15).ToAllChat(); }; - Timers[35] += Execute; + Timers[45] += Execute; base.Setup(); - foreach (var ct in PlayerUtil.FromTeam(CsTeam.CounterTerrorist)) { + foreach (var ct in PlayerUtil.FromTeam(CsTeam.CounterTerrorist)) ct.SetSpeed(2f); - } } public override void Execute() { diff --git a/mod/Jailbreak.SpecialDay/SpecialDays/ISpecialDayMessageProvider.cs b/mod/Jailbreak.SpecialDay/SpecialDays/ISpecialDayMessageProvider.cs index 4b63c361..10f8ec4b 100644 --- a/mod/Jailbreak.SpecialDay/SpecialDays/ISpecialDayMessageProvider.cs +++ b/mod/Jailbreak.SpecialDay/SpecialDays/ISpecialDayMessageProvider.cs @@ -3,5 +3,5 @@ namespace Jailbreak.SpecialDay.SpecialDays; public interface ISpecialDayMessageProvider { - public ISpecialDayInstanceMessages Messages { get; } + public ISDInstanceLocale Locale { get; } } \ No newline at end of file diff --git a/mod/Jailbreak.SpecialDay/SpecialDays/InfectionDay.cs b/mod/Jailbreak.SpecialDay/SpecialDays/InfectionDay.cs index 5e437a58..1f704c80 100644 --- a/mod/Jailbreak.SpecialDay/SpecialDays/InfectionDay.cs +++ b/mod/Jailbreak.SpecialDay/SpecialDays/InfectionDay.cs @@ -1,7 +1,6 @@ using System.Drawing; using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; -using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.English.SpecialDay; using Jailbreak.Formatting.Extensions; @@ -13,56 +12,36 @@ namespace Jailbreak.SpecialDay.SpecialDays; -public class InfectionDay(BasePlugin plugin, IServiceProvider provider) - : ArmoryRestrictedDay(plugin, provider, CsTeam.CounterTerrorist), - ISpecialDayMessageProvider { - public override SDType Type => SDType.INFECTION; - - public override SpecialDaySettings Settings => new InfectionSettings(); - private readonly ICollection swappedPrisoners = new HashSet(); - - public class InfectionSettings : SpecialDaySettings { - public InfectionSettings() { - CtTeleport = TeleportType.ARMORY; - TTeleport = TeleportType.ARMORY; - RestrictWeapons = true; +public class InfectionDay : AbstractArmoryRestrictedDay, + ISpecialDayMessageProvider { + private readonly ICollection swappedPrisoners = new HashSet(); - WithRespawns(CsTeam.CounterTerrorist); - } + public InfectionDay(BasePlugin Plugin, IServiceProvider provider) : base( + Plugin, provider, CsTeam.CounterTerrorist) { } - public override ISet? AllowedWeapons(CCSPlayerController player) { - return player.Team == CsTeam.CounterTerrorist ? - Tag.UTILITY.Union(Tag.PISTOLS).ToHashSet() : - null; - } + public override SDType Type => SDType.INFECTION; - public override float FreezeTime(CCSPlayerController player) { - return player.Team == CsTeam.CounterTerrorist ? 5 : 2; - } + public override SpecialDaySettings Settings => new InfectionSettings(); + private InfectionDayLocale msg => (InfectionDayLocale)Locale; - public override int InitialHealth(CCSPlayerController player) { - return player.Team == CsTeam.CounterTerrorist ? 50 : 200; - } - } + public ISDInstanceLocale Locale => new InfectionDayLocale(); public override void Setup() { - Timers[15] += () => Messages.BeginsIn(15).ToAllChat(); + Timers[15] += () => { + Locale.BeginsIn(15).ToAllChat(); + msg.DamageWarning(5).ToAllChat(); + }; + Timers[20] += () => { + foreach (var t in PlayerUtil.FromTeam(CsTeam.Terrorist)) EnableDamage(t); + }; Timers[30] += Execute; base.Setup(); foreach (var ct in PlayerUtil.FromTeam(CsTeam.CounterTerrorist)) ct.SetColor(Color.LimeGreen); - plugin.RegisterEventHandler(OnPlayerDeath); - plugin.RegisterEventHandler(OnRespawn); - - plugin.AddCommandListener("jointeam", onJoinTeam); - } - - private HookResult onJoinTeam(CCSPlayerController? player, - CommandInfo commandinfo) { - if (player == null) return HookResult.Continue; - return HookResult.Handled; + Plugin.RegisterEventHandler(OnPlayerDeath); + Plugin.RegisterEventHandler(OnRespawn); } private HookResult @@ -91,26 +70,26 @@ private HookResult var target = nearest.FirstOrDefault(); if (target != null && target.Team == CsTeam.Terrorist) - msg.InfectedWarning(player).ToPlayerChat(target); + msg.InfectedWarning(player).ToChat(target); var tpSpot = target != null ? target.PlayerPawn.Value!.AbsOrigin!.Clone() : pos; - swappedPrisoners.Add(player.Index); + swappedPrisoners.Add(player.Slot); if (!player.IsValid) return HookResult.Continue; msg.YouWereInfectedMessage( - (@event.Attacker != null && @event.Attacker.IsValid) ? + @event.Attacker != null && @event.Attacker.IsValid ? @event.Attacker : null) - .ToPlayerChat(player); - plugin.AddTimer(0.1f, () => { + .ToChat(player); + Plugin.AddTimer(0.1f, () => { player.SwitchTeam(CsTeam.CounterTerrorist); player.Respawn(); - plugin.AddTimer(0.1f, () => { + Plugin.AddTimer(0.1f, () => { player.RemoveWeapons(); - plugin.AddTimer(3, () => { player.GiveNamedItem("weapon_knife"); }); + Plugin.AddTimer(3, () => { player.GiveNamedItem("weapon_knife"); }); }); if (nearest.Count == 0 || target == null) { player.PlayerPawn.Value!.Teleport(pos); @@ -128,29 +107,51 @@ public HookResult OnRespawn(EventPlayerSpawn @event, GameEventInfo info) { if (player.Team != CsTeam.CounterTerrorist) return HookResult.Continue; var hp = Settings.InitialHealth(player); - if (hp != -1) plugin.AddTimer(0.1f, () => { player.SetHealth(hp); }); + if (hp != -1) Plugin.AddTimer(0.1f, () => { player.SetHealth(hp); }); - var color = swappedPrisoners.Contains(player.Index) ? + var color = swappedPrisoners.Contains(player.Slot) ? Color.DarkOliveGreen : Color.ForestGreen; player.SetColor(color); return HookResult.Continue; } - public override HookResult OnEnd(EventRoundEnd @event, GameEventInfo info) { + override protected HookResult + OnEnd(EventRoundEnd @event, GameEventInfo info) { var result = base.OnEnd(@event, info); + Plugin.DeregisterEventHandler(OnPlayerDeath); + Plugin.DeregisterEventHandler(OnRespawn); + foreach (var index in swappedPrisoners) { - var player = Utilities.GetPlayerFromIndex((int)index); + var player = Utilities.GetPlayerFromSlot(index); if (player == null) continue; player.SwitchTeam(CsTeam.Terrorist); } - plugin.DeregisterEventHandler(OnPlayerDeath); - plugin.DeregisterEventHandler(OnRespawn); - plugin.RemoveCommandListener("jointeam", onJoinTeam, HookMode.Pre); return result; } - public ISpecialDayInstanceMessages Messages => new InfectionDayMessages(); - private InfectionDayMessages msg => (InfectionDayMessages)Messages; + public class InfectionSettings : SpecialDaySettings { + public InfectionSettings() { + CtTeleport = TeleportType.ARMORY; + TTeleport = TeleportType.RANDOM; + RestrictWeapons = true; + + WithRespawns(CsTeam.CounterTerrorist); + } + + public override ISet? AllowedWeapons(CCSPlayerController player) { + return player.Team == CsTeam.CounterTerrorist ? + Tag.UTILITY.Union(Tag.PISTOLS).ToHashSet() : + null; + } + + public override float FreezeTime(CCSPlayerController player) { + return player.Team == CsTeam.CounterTerrorist ? 6 : 2; + } + + public override int InitialHealth(CCSPlayerController player) { + return player.Team == CsTeam.Terrorist ? 75 : 200; + } + } } \ No newline at end of file diff --git a/mod/Jailbreak.SpecialDay/SpecialDays/NoScopeDay.cs b/mod/Jailbreak.SpecialDay/SpecialDays/NoScopeDay.cs index 54ee5f80..4232abde 100644 --- a/mod/Jailbreak.SpecialDay/SpecialDays/NoScopeDay.cs +++ b/mod/Jailbreak.SpecialDay/SpecialDays/NoScopeDay.cs @@ -1,7 +1,5 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; -using CounterStrikeSharp.API.Core.Attributes.Registration; -using CounterStrikeSharp.API.Modules.Cvars; using Jailbreak.English.SpecialDay; using Jailbreak.Formatting.Views; using Jailbreak.Public.Extensions; @@ -15,8 +13,8 @@ public class NoScopeDay(BasePlugin plugin, IServiceProvider provider) : FFADay(plugin, provider) { public override SDType Type => SDType.NOSCOPE; - public override ISpecialDayInstanceMessages Messages - => new SoloDayMessages("No Scope", + public override ISDInstanceLocale Locale + => new SoloDayLocale("No Scope", "Your scope broke! Fight against everyone else. No camping!"); public override SpecialDaySettings Settings => new NoScopeSettings(); diff --git a/mod/Jailbreak.SpecialDay/SpecialDays/OneInTheChamberDay.cs b/mod/Jailbreak.SpecialDay/SpecialDays/OneInTheChamberDay.cs new file mode 100644 index 00000000..c91a2528 --- /dev/null +++ b/mod/Jailbreak.SpecialDay/SpecialDays/OneInTheChamberDay.cs @@ -0,0 +1,74 @@ +using CounterStrikeSharp.API.Core; +using Jailbreak.English.SpecialDay; +using Jailbreak.Formatting.Views; +using Jailbreak.Public.Extensions; +using Jailbreak.Public.Mod.SpecialDay; +using Jailbreak.Public.Mod.SpecialDay.Enums; +using Jailbreak.Public.Utils; + +namespace Jailbreak.SpecialDay.SpecialDays; + +public class OneInTheChamberDay(BasePlugin plugin, IServiceProvider provider) + : FFADay(plugin, provider) { + public override SDType Type => SDType.OITC; + + public override ISDInstanceLocale Locale + => new SoloDayLocale("One in the Chamber", "You only have one bullet.", + "Kill someone to get another bullet.", "One-Hit-Kills! No camping!"); + + public override SpecialDaySettings Settings => new OitcSettings(); + + public override void Setup() { + base.Setup(); + Plugin.RegisterEventHandler(OnPlayerDamage); + Plugin.RegisterEventHandler(OnPlayerDeath); + } + + public override void Execute() { + base.Execute(); + + foreach (var player in PlayerUtil.GetAlive()) { + player.RemoveWeapons(); + player.GiveNamedItem("weapon_knife"); + player.GiveNamedItem("weapon_deagle"); + player.GetWeaponBase("weapon_deagle")?.SetAmmo(1, 0); + } + } + + private HookResult + OnPlayerDamage(EventPlayerHurt @event, GameEventInfo info) { + if (@event.Userid == null || !@event.Userid.IsValid) + return HookResult.Continue; + @event.Userid?.SetHealth(0); + return HookResult.Changed; + } + + private HookResult + OnPlayerDeath(EventPlayerDeath @event, GameEventInfo info) { + if (@event.Attacker == null) return HookResult.Continue; + @event.Attacker.GetWeaponBase("weapon_deagle")?.AddBulletsToMagazine(1); + return HookResult.Continue; + } + + override protected HookResult + OnEnd(EventRoundEnd @event, GameEventInfo info) { + Plugin.DeregisterEventHandler(OnPlayerDamage); + Plugin.DeregisterEventHandler(OnPlayerDeath); + return base.OnEnd(@event, info); + } +} + +public class OitcSettings : SpecialDaySettings { + public OitcSettings() { + CtTeleport = TeleportType.ARMORY; + TTeleport = TeleportType.ARMORY; + RestrictWeapons = true; + WithFriendlyFire(); + + ConVarValues["mp_death_drop_gun"] = 0; + } + + public override ISet AllowedWeapons(CCSPlayerController player) { + return new HashSet { "weapon_deagle", "weapon_knife" }; + } +} \ No newline at end of file diff --git a/mod/Jailbreak.SpecialDay/SpecialDays/SpeedrunDay.cs b/mod/Jailbreak.SpecialDay/SpecialDays/SpeedrunDay.cs new file mode 100644 index 00000000..9eab246e --- /dev/null +++ b/mod/Jailbreak.SpecialDay/SpecialDays/SpeedrunDay.cs @@ -0,0 +1,491 @@ +using System.Drawing; +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Cvars; +using CounterStrikeSharp.API.Modules.Timers; +using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.English.SpecialDay; +using Jailbreak.Formatting.Extensions; +using Jailbreak.Formatting.Views; +using Jailbreak.Public.Extensions; +using Jailbreak.Public.Mod.Draw; +using Jailbreak.Public.Mod.SpecialDay; +using Jailbreak.Public.Mod.SpecialDay.Enums; +using Jailbreak.Public.Mod.Trail; +using Jailbreak.Public.Utils; +using Jailbreak.Trail; +using Microsoft.Extensions.DependencyInjection; +using Timer = CounterStrikeSharp.API.Modules.Timers.Timer; + +namespace Jailbreak.SpecialDay.SpecialDays; + +public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider) + : AbstractSpecialDay(plugin, provider), ISpecialDayMessageProvider { + private const int FIRST_SPEEDRUNNER_TIME = 40; + private const int FIRST_ROUND_FREEZE = 8; + private const int FREEZE_TIME = 2; + private const int MAX_POINTS = 500; + + public static readonly FakeConVar CvInitialSpeedrunTime = + new("css_jb_speedrun_initial_time", "Initial time for the speedrunner", 30); + + public static readonly FakeConVar CvFirstRoundFreeze = + new("css_jb_speedrun_first_round_freeze", + "Duration in seconds to give players time to read the rules of speedrun", + 8); + + private readonly Dictionary> + activeTrails = new(); + + /// + /// Negative values represent players who finished. + /// Positive values represent players who are still alive, and the value + /// being the distance they are from the target. + /// + private readonly SortedDictionary finishTimestamps = new(); + + private readonly Random rng = new(); + private float? bestTime; + private int? bestTimePlayerSlot; + + private AbstractTrail? bestTrail; + private Timer? finishCheckTimer; + + private IGenericCmdLocale generics = null!; + private int round, playersAliveAtStart; + private Timer? roundEndTimer; + + private float? roundStartTime; + private Vector? start; + private Vector? target; + private BeamCircle? targetCircle; + + public override SDType Type => SDType.SPEEDRUN; + + private SpeedrunDayLocale msg => (SpeedrunDayLocale)Locale; + + public override SpecialDaySettings Settings => new SpeedrunSettings(); + public ISDInstanceLocale Locale => new SpeedrunDayLocale(); + + public override void Setup() { + generics = Provider.GetRequiredService(); + + foreach (var player in Utilities.GetPlayers().Where(p => !p.PawnIsAlive)) + player.Respawn(); + + var speedrunner = PlayerUtil.GetRandomFromTeam(rng.Next(2) == 0 ? + CsTeam.Terrorist : + CsTeam.CounterTerrorist); + + if (speedrunner == null) { + speedrunner = PlayerUtil.GetAlive().FirstOrDefault(); + if (speedrunner == null) { + generics.Error("Could not find a valid speedrunner").ToAllChat(); + RoundUtil.SetTimeRemaining(1); + return; + } + } + + Timers[0.1f] += () => { + // Needed since players who respawned are given knife later + foreach (var player in PlayerUtil.GetAlive()) player.RemoveWeapons(); + }; + Timers[FIRST_ROUND_FREEZE - 4] += () => { + msg.RunnerAssigned(speedrunner).ToAllChat(); + speedrunner.SetColor(Color.DodgerBlue); + msg.YouAreRunner(FIRST_SPEEDRUNNER_TIME).ToChat(speedrunner); + }; + Timers[FIRST_ROUND_FREEZE] += () => { + start = speedrunner.PlayerPawn.Value!.AbsOrigin!.Clone(); + speedrunner.UnFreeze(); + bestTrail = new ActivePulsatingBeamPlayerTrail(Plugin, speedrunner, 0f, + MAX_POINTS, 0.15f); + }; + + Timers[FIRST_SPEEDRUNNER_TIME + FIRST_ROUND_FREEZE - 30] += () + => msg.RuntimeLeft(30).ToChat(speedrunner); + Timers[FIRST_SPEEDRUNNER_TIME + FIRST_ROUND_FREEZE - 10] += () + => msg.RuntimeLeft(10).ToChat(speedrunner); + Timers[FIRST_SPEEDRUNNER_TIME + FIRST_ROUND_FREEZE] += () => { + target = speedrunner.PlayerPawn.Value?.AbsOrigin!.Clone(); + targetCircle = new BeamCircle(Plugin, target!, 10, 16); + targetCircle.SetColor(Color.Green); + targetCircle.Draw(); + + if (bestTrail is null) { + generics.Error("bestTrail is null").ToAllChat(); + return; + } + + if (bestTrail is ActivePlayerTrail active) + active.StopTracking(); + + var timeSpent = bestTrail.GetEndSegment()!.GetSpawnTime() + - bestTrail.GetStartSegment()!.GetSpawnTime(); + + bestTime = timeSpent; + + var minTime = FIRST_SPEEDRUNNER_TIME * 0.5; + + startRound((int)Math.Ceiling(Math.Max(timeSpent * 1.1, minTime))); + + finishCheckTimer = Plugin.AddTimer(0.03f, checkFinishers, + TimerFlags.STOP_ON_MAPCHANGE | TimerFlags.REPEAT); + }; + + base.Setup(); + + foreach (var player in PlayerUtil.GetAlive()) { + player.SetColor(Color.FromArgb(100, 255, 255, 255)); + player.RemoveWeapons(); + } + + Execute(); + } + + public override void Execute() { + if (Settings.RestrictWeapons) + Plugin.RegisterListener(OnTick); + } + + private void startRound(int seconds) { + roundStartTime = null; + var alive = PlayerUtil.GetAlive().ToArray(); + playersAliveAtStart = PlayerUtil.GetAlive().Count(); + msg.BeginRound(++round, getEliminations(playersAliveAtStart), seconds) + .ToAllChat(); + + RoundUtil.SetTimeRemaining(seconds + FREEZE_TIME); + + foreach (var player in alive) { + var pawn = player.PlayerPawn.Value; + if (pawn == null) continue; + pawn.Teleport(start); + player.Freeze(); + player.RemoveWeapons(); + } + + resetTrails(); + finishTimestamps.Clear(); + + Plugin.AddTimer(FREEZE_TIME, () => { + foreach (var player in PlayerUtil.GetAlive()) player.UnFreeze(); + roundStartTime = Server.CurrentTime; + }, TimerFlags.STOP_ON_MAPCHANGE); + + roundEndTimer = Plugin.AddTimer(seconds + FREEZE_TIME, endRound, + TimerFlags.STOP_ON_MAPCHANGE); + } + + private void checkFinishers() { + if (target == null || roundStartTime == null) return; + if (finishCheckTimer == null) return; + targetCircle?.SetRadius(getRequiredDistance() / 2); + targetCircle?.Update(); + var required = MathF.Pow(getRequiredDistance(), 2); + foreach (var player in PlayerUtil.GetAlive()) { + if (finishTimestamps.ContainsKey(player.Slot)) continue; + var pos = player.Pawn.Value?.AbsOrigin; + if (pos == null) continue; + var hdist = pos.HorizontalDistanceSquared(target); + if (hdist >= required) continue; + var dist = pos.DistanceSquared(target); + if (dist >= required * 1.25f) continue; + onFinish(player); + } + } + + private void onFinish(CCSPlayerController player) { + if (roundStartTime == null) { + generics.Error("roundStartTime is null").ToAllChat(); + return; + } + + var time = Server.CurrentTime - roundStartTime!.Value; + if (bestTime == null || time < bestTime) { + bestTime = time; + bestTimePlayerSlot = player.Slot; + msg.BestTime(player, time).ToAllChat(); + player.SetColor(Color.FromArgb(255, Color.Gold)); + } else { + msg.PlayerTime(player, finishTimestamps.Count + 1, -time).ToAllChat(); + } + + finishTimestamps[player.Slot] = -Server.CurrentTime; + var eliminations = getEliminations(PlayerUtil.GetAlive().Count()); + activeTrails[player.Slot].StopTracking(); + + var taking = playersAliveAtStart - eliminations; + + if (finishTimestamps.Count >= taking) endRound(); + + if (!player.IsValid) { + generics.Error("completer is not valid").ToAllChat(); + return; + } + + if (bestTimePlayerSlot != null && bestTimePlayerSlot == player.Slot) return; + + var alpha = Math.Max(255 - finishTimestamps.Count * 20, 0); + player.SetColor(Color.FromArgb(alpha, Color.White)); + } + + private void resetTrails() { + if (activeTrails.Count != 0 && finishTimestamps.Count != 0) { + var completers = finishTimestamps.Where(x => x.Value < 0).ToArray(); + if (completers.Length > 0) { + // Of the players who finished, find the one who finished the fastest + // since these times are negative timestamps, we want the "largest" + // value (i.e. least negative) + var best = completers.MaxBy(x => x.Value); + var bestPlayer = best.Key; + var time = best.Value; + if (bestTime == null || time <= bestTime) { + bestTrail?.Kill(); + activeTrails[bestPlayer].StopTracking(); + // bestTrail = BeamTrail.FromTrail(Plugin, activeTrails[bestPlayer]); + bestTrail = PulsatingBeamTrail.FromTrail(Plugin, + activeTrails[bestPlayer]); + } + } + } + + foreach (var trail in activeTrails.Values) trail.Kill(); + + activeTrails.Clear(); + + foreach (var player in PlayerUtil.GetAlive()) + activeTrails[player.Slot] = + new ActiveInvisiblePlayerTrail(Plugin, player, 0f, MAX_POINTS); + } + + // https://www.desmos.com/calculator/e1qwgpmtmz + private float getRequiredDistance() { + if (roundStartTime == null) return 0; + var elapsedSeconds = (float)(Server.CurrentTime - roundStartTime); + + return 10 + elapsedSeconds + MathF.Pow(elapsedSeconds, 3.3f) / 2500; + } + + private void endRound() { + roundEndTimer?.Kill(); + if (target == null) { + generics.Error("Target is null").ToAllChat(); + new EventRoundEnd(true).FireEvent(false); + return; + } + + var aliveCount = PlayerUtil.GetAlive().Count(); + var playersDiedMidRound = playersAliveAtStart - aliveCount; + var toEliminate = getEliminations(aliveCount) - playersDiedMidRound; + + var ctMade = PlayerUtil.FromTeam(CsTeam.CounterTerrorist).Count() < 4; + var tMade = PlayerUtil.FromTeam(CsTeam.Terrorist).Count() < 4; + + foreach (var player in PlayerUtil.GetAlive()) { + if (player.Team == CsTeam.CounterTerrorist) ctMade = true; + if (player.Team == CsTeam.Terrorist) tMade = true; + if (finishTimestamps.ContainsKey(player.Slot)) continue; + + var dist = player.PlayerPawn.Value?.AbsOrigin?.Distance(target); + if (dist == null) continue; + finishTimestamps[player.Slot] = dist.Value; + } + + if (aliveCount > 1) + if (ctMade != tMade && round == 1) { + var random = PlayerUtil.GetRandomFromTeam(tMade ? + CsTeam.CounterTerrorist : + CsTeam.Terrorist); + + if (random != null && activeTrails.TryGetValue(random.Slot, + out var randomTrail)) { + msg.ImpossibleLocation( + ctMade ? CsTeam.Terrorist : CsTeam.CounterTerrorist, random); + + bestTrail?.Kill(); + randomTrail.StopTracking(); + bestTrail = PulsatingBeamTrail.FromTrail(Plugin, randomTrail); + target = bestTrail!.GetEndSegment()!.GetEnd(); + } + + toEliminate = 2; + round--; + } + + announceTimes(); + var slowTimes = SlowestTimes(finishTimestamps); + var keyValuePairs = slowTimes.ToList(); + + if (aliveCount <= 2) { + // Announce winners, end the round, etc. + // Maybe tp the loser to the winner and let the winner kill them + + if (keyValuePairs.Count == 0) { + generics.Error("No slowest times found").ToAllChat(); + return; + } + + var winner = Utilities.GetPlayerFromSlot(keyValuePairs.Last().Key); + + if (winner == null || !winner.IsValid) { + generics.Error("Winner is null").ToAllChat(); + return; + } + + targetCircle?.Remove(); + targetCircle = null; + + var loser = PlayerUtil.GetAlive() + .FirstOrDefault(p => p.IsValid && p.Slot != winner.Slot); + + msg.PlayerWon(winner).ToAllChat(); + if (loser == null || !loser.IsValid) { + RoundUtil.SetTimeRemaining(10); + Server.ExecuteCommand("mp_ignore_round_win_conditions 0"); + return; + } + + + loser.SetColor(Color.FromArgb(254, Color.White)); + loser.Teleport(winner); + EnableDamage(loser); + + winner.GiveNamedItem("weapon_knife"); + winner.GiveNamedItem("weapon_negev"); + + Plugin.RemoveListener(OnTick); + + RoundUtil.SetTimeRemaining(30); + Server.ExecuteCommand("mp_ignore_round_win_conditions 0"); + return; + } + + // var fastTime = MathF.Abs(fastTimestamp) - roundStartTime!; + var roundTimeWas = Math.Ceiling(Server.CurrentTime - roundStartTime!.Value); + var nextRoundTime = (int)Math.Ceiling((bestTime ?? 20) + 10 - round * 2); + + if (toEliminate <= 0) { + msg.NoneEliminated.ToAllChat(); + Plugin.AddTimer(3f, () => { startRound(nextRoundTime); }, + TimerFlags.STOP_ON_MAPCHANGE); + return; + } + + nextRoundTime = (int)Math.Clamp(nextRoundTime, 5, roundTimeWas); + var slowestEnumerator = SlowestTimes(finishTimestamps).GetEnumerator(); + + if (ctMade != tMade && round == 0) { + bool killedCt = false, killedT = false; + while (slowestEnumerator.MoveNext()) { + var (slot, _) = slowestEnumerator.Current; + var player = Utilities.GetPlayerFromSlot(slot); + if (player == null || !player.IsValid) continue; + switch (player.Team) { + case CsTeam.CounterTerrorist when !killedCt: + killedCt = true; + eliminatePlayer(player); + toEliminate--; + break; + case CsTeam.Terrorist when !killedT: + killedT = true; + eliminatePlayer(player); + toEliminate--; + break; + } + + if (killedCt && killedT) break; + } + } + + for (var i = 0; i < toEliminate; i++) { + if (!slowestEnumerator.MoveNext()) break; + var (slot, _) = slowestEnumerator.Current; + var player = Utilities.GetPlayerFromSlot(slot); + if (player == null || !player.IsValid) continue; + EnableDamage(player); + player.CommitSuicide(false, true); + msg.PlayerEliminated(player).ToAllChat(); + } + + slowestEnumerator.Dispose(); + + Plugin.AddTimer(3f, () => { startRound(nextRoundTime); }, + TimerFlags.STOP_ON_MAPCHANGE); + } + + private void eliminatePlayer(CCSPlayerController player) { + EnableDamage(player); + player.CommitSuicide(false, true); + msg.PlayerEliminated(player).ToAllChat(); + } + + private int getEliminations(int players) { + return players switch { + <= 4 => 1, + <= 8 => 2, + <= 12 => 3, + <= 20 => 4, + <= 30 => 5, + <= 40 => 8, + <= 64 => 12, + _ => 4 + }; + } + + private void announceTimes() { + var times = SlowestTimes(finishTimestamps).ToArray(); + for (var i = 0; i < times.Length; i++) { + var (slot, time) = times[i]; + var player = Utilities.GetPlayerFromSlot(slot); + if (player == null) continue; + if (time > 0) + msg.PlayerTime(player, times.Length - i, time).ToChat(player); + } + } + + public static IEnumerable> + SlowestTimes(IDictionary timeDistances) { + return timeDistances.OrderByDescending(entry => entry.Value >= 0) + .ThenByDescending(entry => entry.Value >= 0 ? entry.Value : float.MinValue) + .ThenBy(entry => entry.Value < 0 ? entry.Value : float.MaxValue); + } + + override protected HookResult + OnEnd(EventRoundEnd @event, GameEventInfo info) { + var result = base.OnEnd(@event, info); + + finishCheckTimer?.Kill(); + finishCheckTimer = null; + bestTrail?.Kill(); + + foreach (var trail in activeTrails.Values) trail.Kill(); + + activeTrails.Clear(); + + return result; + } + + public class SpeedrunSettings : SpecialDaySettings { + public SpeedrunSettings() { + CtTeleport = TeleportType.RANDOM_STACKED; + TTeleport = TeleportType.RANDOM_STACKED; + // RestrictWeapons = true; + StripToKnife = true; + ConVarValues["mp_ignore_round_win_conditions"] = true; + WithFriendlyFire(); + } + + public override Func RoundTime + => () => FIRST_SPEEDRUNNER_TIME + FIRST_ROUND_FREEZE; + + public override ISet AllowedWeapons(CCSPlayerController player) { + // Return empty set to allow no weapons + return new HashSet(); + } + + public override float FreezeTime(CCSPlayerController player) { + return FIRST_ROUND_FREEZE; + } + } +} \ No newline at end of file diff --git a/mod/Jailbreak.SpecialDay/SpecialDays/TeleportDay.cs b/mod/Jailbreak.SpecialDay/SpecialDays/TeleportDay.cs new file mode 100644 index 00000000..393c13ca --- /dev/null +++ b/mod/Jailbreak.SpecialDay/SpecialDays/TeleportDay.cs @@ -0,0 +1,63 @@ +using CounterStrikeSharp.API.Core; +using Jailbreak.English.SpecialDay; +using Jailbreak.Formatting.Views; +using Jailbreak.Public.Extensions; +using Jailbreak.Public.Mod.SpecialDay; +using Jailbreak.Public.Mod.SpecialDay.Enums; + +namespace Jailbreak.SpecialDay.SpecialDays; + +public class TeleportDay(BasePlugin plugin, IServiceProvider provider) + : FFADay(plugin, provider) { + public override SDType Type => SDType.FFA; + public override SpecialDaySettings Settings => new TeleportSettings(); + + public override ISDInstanceLocale Locale + => new SoloDayLocale("Teleport", + "Free for all! If you damage someone, you will swap places with them!"); + + public override void Setup() { + base.Setup(); + Plugin.RegisterEventHandler(onDamage); + } + + private HookResult onDamage(EventPlayerHurt @event, GameEventInfo info) { + var player = @event.Userid; + if (player == null || !player.IsValid) return HookResult.Continue; + var attacker = @event.Attacker; + if (attacker == null || !attacker.IsValid) return HookResult.Continue; + + var playerLoc = player.Pawn.Value?.AbsOrigin; + var attackerLoc = attacker.Pawn.Value?.AbsOrigin; + + if (playerLoc == null || attackerLoc == null) return HookResult.Continue; + + var tmp = playerLoc.Clone(); + + player.Pawn.Value?.Teleport(attackerLoc); + attacker.Pawn.Value?.Teleport(tmp); + return HookResult.Continue; + } + + override protected HookResult + OnEnd(EventRoundEnd @event, GameEventInfo info) { + Plugin.DeregisterEventHandler(onDamage); + return base.OnEnd(@event, info); + } + + public class TeleportSettings : SpecialDaySettings { + private readonly Random rng; + + public TeleportSettings() { + CtTeleport = TeleportType.ARMORY; + TTeleport = TeleportType.ARMORY; + StripToKnife = false; + rng = new Random(); + WithFriendlyFire(); + } + + public override float FreezeTime(CCSPlayerController player) { + return rng.NextSingle() * 5 + 2; + } + } +} \ No newline at end of file diff --git a/mod/Jailbreak.SpecialDay/SpecialDays/WardayDay.cs b/mod/Jailbreak.SpecialDay/SpecialDays/WardayDay.cs index 436ef35d..ad9d2ecb 100644 --- a/mod/Jailbreak.SpecialDay/SpecialDays/WardayDay.cs +++ b/mod/Jailbreak.SpecialDay/SpecialDays/WardayDay.cs @@ -11,25 +11,30 @@ namespace Jailbreak.SpecialDay.SpecialDays; public class WardayDay(BasePlugin plugin, IServiceProvider provider) - : ArmoryRestrictedDay(plugin, provider), ISpecialDayMessageProvider { + : AbstractArmoryRestrictedDay(plugin, provider), ISpecialDayMessageProvider { public override SDType Type => SDType.WARDAY; - private WardayInstanceMessages msg => (WardayInstanceMessages)Messages; + private WardayInstanceLocale msg => (WardayInstanceLocale)Locale; public override SpecialDaySettings Settings => new WardaySettings(); - public ISpecialDayInstanceMessages Messages => new WardayInstanceMessages(); + public ISDInstanceLocale Locale => new WardayInstanceLocale(); public override void Setup() { - Timers[15] += () => Messages.BeginsIn(15).ToAllChat(); - Timers[30] += () => Messages.BeginsIn(5).ToAllChat(); + Timers[15] += () => Locale.BeginsIn(15).ToAllChat(); + Timers[30] += () => Locale.BeginsIn(5).ToAllChat(); Timers[35] += Execute; Timers[120] += () => msg.ExpandIn(30).ToAllChat(); Timers[150] += () => { msg.ExpandNow.ToAllChat(); foreach (var ct in PlayerUtil.FromTeam(CsTeam.CounterTerrorist)) { - ct.SetHealth(100); - ct.SetArmor(100); + ct.SetHealth(200); + ct.SetArmor(300); ct.SetSpeed(1.5f); } + + foreach (var t in PlayerUtil.FromTeam(CsTeam.Terrorist)) { + t.SetHealth(Math.Min(25, t.Health)); + t.SetArmor(0); + } }; base.Setup(); diff --git a/mod/Jailbreak.Trail/ActiveBeamPlayerTrail.cs b/mod/Jailbreak.Trail/ActiveBeamPlayerTrail.cs new file mode 100644 index 00000000..0f02aa9b --- /dev/null +++ b/mod/Jailbreak.Trail/ActiveBeamPlayerTrail.cs @@ -0,0 +1,16 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.Public.Mod.Draw; + +namespace Jailbreak.Trail; + +public class ActiveBeamPlayerTrail(BasePlugin plugin, + CCSPlayerController player, float lifetime = 20, int maxPoints = 100, + float updateRate = 0.5f) + : ActivePlayerTrail(plugin, player, lifetime, maxPoints, + updateRate) { + public override BeamTrailSegment CreateSegment(Vector start, Vector end) { + var beam = new BeamLine(Plugin, start, end); + return new BeamTrailSegment(beam); + } +} \ No newline at end of file diff --git a/mod/Jailbreak.Trail/ActiveInvisiblePlayerTrail.cs b/mod/Jailbreak.Trail/ActiveInvisiblePlayerTrail.cs new file mode 100644 index 00000000..178eeee5 --- /dev/null +++ b/mod/Jailbreak.Trail/ActiveInvisiblePlayerTrail.cs @@ -0,0 +1,14 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; + +namespace Jailbreak.Trail; + +public class ActiveInvisiblePlayerTrail(BasePlugin plugin, + CCSPlayerController player, float lifetime = 20, int maxPoints = 100, + float updateRate = 0.5f) + : ActivePlayerTrail(plugin, player, lifetime, maxPoints, + updateRate) { + public override VectorTrailSegment CreateSegment(Vector start, Vector end) { + return new VectorTrailSegment(start, end); + } +} \ No newline at end of file diff --git a/mod/Jailbreak.Trail/ActivePlayerTrail.cs b/mod/Jailbreak.Trail/ActivePlayerTrail.cs new file mode 100644 index 00000000..6bc14c57 --- /dev/null +++ b/mod/Jailbreak.Trail/ActivePlayerTrail.cs @@ -0,0 +1,48 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Timers; +using Jailbreak.Public.Extensions; +using Jailbreak.Public.Mod.Trail; +using Timer = CounterStrikeSharp.API.Modules.Timers.Timer; + +namespace Jailbreak.Trail; + +public abstract class ActivePlayerTrail : AbstractTrail + where T : ITrailSegment { + protected readonly BasePlugin Plugin; + protected readonly Timer Timer; + + public ActivePlayerTrail(BasePlugin plugin, CCSPlayerController player, + float lifetime = 20, int maxPoints = 100, float updateRate = 0.5f) : base( + lifetime, maxPoints) { + Plugin = plugin; + Player = player; + Timer = plugin.AddTimer(updateRate, Tick, + TimerFlags.REPEAT | TimerFlags.STOP_ON_MAPCHANGE); + } + + public CCSPlayerController Player { get; protected set; } + + virtual protected void Tick() { + if (!Player.IsValid) Kill(); + var pos = Player.PlayerPawn.Value?.AbsOrigin; + if (pos == null) return; + pos = pos.Clone(); + var end = GetEndSegment(); + var dist = end?.GetStart().DistanceSquared(pos) ?? float.MaxValue; + if (dist < 1000) { + // Still want to remove old segments + Cleanup(); + return; + } + + AddTrailPoint(pos); + } + + public virtual void StopTracking() { Timer.Kill(); } + + public override void Kill() { + foreach (var segment in Segments) segment.Remove(); + + StopTracking(); + } +} \ No newline at end of file diff --git a/mod/Jailbreak.Trail/ActivePulsatingBeamPlayerTrail.cs b/mod/Jailbreak.Trail/ActivePulsatingBeamPlayerTrail.cs new file mode 100644 index 00000000..bffe08d3 --- /dev/null +++ b/mod/Jailbreak.Trail/ActivePulsatingBeamPlayerTrail.cs @@ -0,0 +1,51 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; + +namespace Jailbreak.Trail; + +public class ActivePulsatingBeamPlayerTrail(BasePlugin plugin, + CCSPlayerController player, float lifetime = 20, int maxPoints = 100, + float updateRate = 0.5f) + : ActivePlayerTrail(plugin, player, lifetime, maxPoints, + updateRate) { + private readonly PulsatingBeamTrail trail = new(plugin, lifetime, maxPoints, + updateRate); + + public override void Kill() { + base.Kill(); + trail.Kill(); + } + + public override void AddTrailPoint(Vector vector) { + trail.AddTrailPoint(vector); + } + + public override IList GetTrail(float since, int max = 0) { + return trail.GetTrail(since, max); + } + + public override IList GetTrailPoints(float since, int max = 0) { + return trail.GetTrailPoints(since, max); + } + + public override BeamTrailSegment CreateSegment(Vector start, Vector end) { + return trail.CreateSegment(start, end); + } + + public override BeamTrailSegment? GetStartSegment() { + return trail.GetStartSegment(); + } + + public override BeamTrailSegment? GetEndSegment() { + return trail.GetEndSegment(); + } + + public override IList GetTrailPoints(int max) { + return trail.GetTrailPoints(max); + } + + public override BeamTrailSegment? GetNearestSegment(Vector position, + float since, int max = 0) { + return trail.GetNearestSegment(position, since, max); + } +} \ No newline at end of file diff --git a/mod/Jailbreak.Trail/BeamTrail.cs b/mod/Jailbreak.Trail/BeamTrail.cs new file mode 100644 index 00000000..e0c6c087 --- /dev/null +++ b/mod/Jailbreak.Trail/BeamTrail.cs @@ -0,0 +1,24 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.Public.Mod.Draw; +using Jailbreak.Public.Mod.Trail; + +namespace Jailbreak.Trail; + +public class BeamTrail(BasePlugin plugin, float lifetime = 20, + int maxPoints = 100) : AbstractTrail(lifetime, maxPoints) { + public static BeamTrail FromTrail(BasePlugin plugin, + AbstractTrail trail) where T : ITrailSegment { + var beamTrail = new BeamTrail(plugin, trail.Lifetime, trail.MaxPoints); + foreach (var segment in trail) + beamTrail.Segments.Add( + beamTrail.CreateSegment(segment.GetStart(), segment.GetEnd())); + + return beamTrail; + } + + public override BeamTrailSegment CreateSegment(Vector start, Vector end) { + var beam = new BeamLine(plugin, start, end); + return new BeamTrailSegment(beam); + } +} \ No newline at end of file diff --git a/mod/Jailbreak.Trail/BeamTrailSegment.cs b/mod/Jailbreak.Trail/BeamTrailSegment.cs new file mode 100644 index 00000000..2cb5e8d5 --- /dev/null +++ b/mod/Jailbreak.Trail/BeamTrailSegment.cs @@ -0,0 +1,15 @@ +using Jailbreak.Public.Mod.Draw; + +namespace Jailbreak.Trail; + +public class BeamTrailSegment : VectorTrailSegment { + private readonly BeamLine line; + + public BeamTrailSegment(BeamLine line) : base(line.Position, line.End) { + this.line = line; + line.Draw(); + } + + public override void Remove() { line.Remove(); } + public BeamLine GetLine() { return line; } +} \ No newline at end of file diff --git a/mod/Jailbreak.Trail/Jailbreak.Trail.csproj b/mod/Jailbreak.Trail/Jailbreak.Trail.csproj new file mode 100644 index 00000000..1b2b6d97 --- /dev/null +++ b/mod/Jailbreak.Trail/Jailbreak.Trail.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/mod/Jailbreak.Trail/PulsatingBeamTrail.cs b/mod/Jailbreak.Trail/PulsatingBeamTrail.cs new file mode 100644 index 00000000..5c2eeced --- /dev/null +++ b/mod/Jailbreak.Trail/PulsatingBeamTrail.cs @@ -0,0 +1,61 @@ +using System.Drawing; +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Timers; +using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.Public.Mod.Draw; +using Jailbreak.Public.Mod.Trail; +using Timer = CounterStrikeSharp.API.Modules.Timers.Timer; + +namespace Jailbreak.Trail; + +public class PulsatingBeamTrail : AbstractTrail { + private readonly BasePlugin plugin; + private readonly Timer timer; + + public PulsatingBeamTrail(BasePlugin plugin, float lifetime = 20, + int maxPoints = 100, float updateRate = 0.25f) : base(lifetime, maxPoints) { + this.plugin = plugin; + + timer = plugin.AddTimer(updateRate, Update, + TimerFlags.REPEAT | TimerFlags.STOP_ON_MAPCHANGE); + } + + protected void Update() { + var i = 0; + foreach (var segment in Segments.Reverse()) { + var line = segment.GetLine(); + var width = MathF.Sin(-Server.CurrentTime * 3f + i * 1.5f) * 5f - 2.5f; + if (width < 0) + line.SetColor(Color.FromArgb(0, 0, 0, 0)); + else + line.SetColor(Color.FromArgb( + (int)Math.Clamp(width / 2.5f * 255, 0, 255), 255, 255, 255)); + line.SetWidth(width); + line.Update(); + i += 1; + } + } + + public override void Kill() { + base.Kill(); + timer.Kill(); + } + + public static PulsatingBeamTrail FromTrail(BasePlugin plugin, + AbstractTrail trail, float updateRate = 0.25f, + Func? transform = null) where T : ITrailSegment { + var beamTrail = new PulsatingBeamTrail(plugin, trail.Lifetime, + trail.MaxPoints, updateRate); + foreach (var segment in trail) + beamTrail.Segments.Add( + beamTrail.CreateSegment(segment.GetStart(), segment.GetEnd())); + + return beamTrail; + } + + public override BeamTrailSegment CreateSegment(Vector start, Vector end) { + var beam = new BeamLine(plugin, start, end); + return new BeamTrailSegment(beam); + } +} \ No newline at end of file diff --git a/mod/Jailbreak.Trail/TrailManager.cs b/mod/Jailbreak.Trail/TrailManager.cs new file mode 100644 index 00000000..7b2a1cba --- /dev/null +++ b/mod/Jailbreak.Trail/TrailManager.cs @@ -0,0 +1,22 @@ +using CounterStrikeSharp.API.Core; +using Jailbreak.Public.Mod.Trail; + +namespace Jailbreak.Trail; + +public class TrailManager : ITrailManager { + public bool AddPlayerTrail(CCSPlayerController player) { + throw new NotImplementedException(); + } + + public bool HasPlayerTrail(CCSPlayerController player) { + throw new NotImplementedException(); + } + + public bool RemovePlayerTrail(CCSPlayerController player) { + throw new NotImplementedException(); + } + + public BeamTrailSegment GetPlayerTrail(CCSPlayerController player) { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/mod/Jailbreak.Trail/VectorTrailSegment.cs b/mod/Jailbreak.Trail/VectorTrailSegment.cs new file mode 100644 index 00000000..e1ed64be --- /dev/null +++ b/mod/Jailbreak.Trail/VectorTrailSegment.cs @@ -0,0 +1,16 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.Public.Mod.Trail; + +namespace Jailbreak.Trail; + +public class VectorTrailSegment(Vector start, Vector end) : ITrailSegment { + public readonly float spawnTime = Server.CurrentTime; + + public float GetSpawnTime() { return spawnTime; } + + public Vector GetStart() { return start; } + + public Vector GetEnd() { return end; } + public virtual void Remove() { } +} \ No newline at end of file diff --git a/mod/Jailbreak.Warden/Commands/OpenCommandsBehavior.cs b/mod/Jailbreak.Warden/Commands/OpenCommandsBehavior.cs new file mode 100644 index 00000000..c7867fe8 --- /dev/null +++ b/mod/Jailbreak.Warden/Commands/OpenCommandsBehavior.cs @@ -0,0 +1,59 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Core.Attributes.Registration; +using CounterStrikeSharp.API.Modules.Admin; +using CounterStrikeSharp.API.Modules.Commands; +using CounterStrikeSharp.API.Modules.Cvars; +using CounterStrikeSharp.API.Modules.Cvars.Validators; +using Jailbreak.Formatting.Extensions; +using Jailbreak.Formatting.Views.Warden; +using Jailbreak.Public.Behaviors; +using Jailbreak.Public.Mod.Warden; +using Jailbreak.Public.Mod.Zones; +using Jailbreak.Public.Utils; + +namespace Jailbreak.Warden.Commands; + +public class OpenCommandsBehavior(IWardenService warden, IWardenLocale msg, + IWardenCmdOpenLocale wardenCmdOpenMsg, IZoneManager zoneManager) + : IPluginBehavior { + public static readonly FakeConVar CvOpenCommandCooldown = new( + "css_jb_warden_open_cooldown", + "Minimum seconds warden must wait before being able to open the cells.", 30, + customValidators: new RangeValidator(0, 300)); + + private bool openedCells; + + [GameEventHandler] + public HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) { + openedCells = false; + return HookResult.Continue; + } + + [ConsoleCommand("css_open", "Opens the cell doors")] + [ConsoleCommand("css_o", "Opens the cell doors")] + public void Command_Open(CCSPlayerController? executor, CommandInfo info) { + if (executor != null + && !AdminManager.PlayerHasPermissions(executor, "@css/cheat")) { + if (!warden.IsWarden(executor)) { + msg.NotWarden.ToChat(executor); + return; + } + + if (RoundUtil.GetTimeElapsed() < CvOpenCommandCooldown.Value) { + wardenCmdOpenMsg.CannotOpenYet(CvOpenCommandCooldown.Value) + .ToChat(executor); + return; + } + + if (openedCells) { + wardenCmdOpenMsg.AlreadyOpened.ToChat(executor); + return; + } + } + + openedCells = true; + var result = MapUtil.OpenCells(zoneManager); + (result ? wardenCmdOpenMsg.CellsOpened : wardenCmdOpenMsg.OpeningFailed) + .ToAllChat(); + } +} \ No newline at end of file diff --git a/mod/Jailbreak.Warden/Commands/PeaceCommandsBehavior.cs b/mod/Jailbreak.Warden/Commands/PeaceCommandsBehavior.cs index 93189832..673b30a3 100644 --- a/mod/Jailbreak.Warden/Commands/PeaceCommandsBehavior.cs +++ b/mod/Jailbreak.Warden/Commands/PeaceCommandsBehavior.cs @@ -4,6 +4,7 @@ using CounterStrikeSharp.API.Modules.Commands; using Jailbreak.Formatting.Extensions; using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.Warden; using Jailbreak.Public.Behaviors; using Jailbreak.Public.Mod.Mute; using Jailbreak.Public.Mod.Warden; @@ -11,13 +12,13 @@ namespace Jailbreak.Warden.Commands; public class PeaceCommandsBehavior(IWardenService warden, IMuteService mute, - IPeaceMessages messages, IWardenNotifications notifications, - IGenericCommandNotifications generics) : IPluginBehavior { + IWardenPeaceLocale peaceLocale, IWardenLocale wardenLocale, + IGenericCmdLocale generics) : IPluginBehavior { [ConsoleCommand("css_peace", "Invokes a peace period where only the warden can talk")] public void Command_Peace(CCSPlayerController? executor, CommandInfo info) { if (mute.IsPeaceEnabled()) { - if (executor != null) messages.PeaceActive.ToPlayerChat(executor); + if (executor != null) peaceLocale.PeaceActive.ToChat(executor); return; } @@ -32,13 +33,13 @@ public void Command_Peace(CCSPlayerController? executor, CommandInfo info) { if (!warden.IsWarden(executor) && !AdminManager.PlayerHasPermissions(executor, "@css/chat")) { - notifications.NotWarden.ToPlayerChat(executor); + wardenLocale.NotWarden.ToChat(executor); return; } if (DateTime.Now - mute.GetLastPeace() < TimeSpan.FromSeconds(60)) { generics.CommandOnCooldown(mute.GetLastPeace().AddSeconds(60)) - .ToPlayerChat(executor); + .ToChat(executor); return; } diff --git a/mod/Jailbreak.Warden/Commands/RollCommandBehavior.cs b/mod/Jailbreak.Warden/Commands/RollCommandBehavior.cs index 293b5f6e..740f7980 100644 --- a/mod/Jailbreak.Warden/Commands/RollCommandBehavior.cs +++ b/mod/Jailbreak.Warden/Commands/RollCommandBehavior.cs @@ -3,15 +3,15 @@ using CounterStrikeSharp.API.Modules.Commands; using Jailbreak.Formatting.Extensions; using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.Warden; using Jailbreak.Public.Behaviors; using Jailbreak.Public.Mod.Warden; namespace Jailbreak.Warden.Commands; public class RollCommandBehavior(IWardenService warden, - IRollCommandNotications notifications, - IWardenNotifications wardenNotifications, - IGenericCommandNotifications generics) : IPluginBehavior { + IWardenCmdRollLocale notifications, IWardenLocale wardenLocale, + IGenericCmdLocale generics) : IPluginBehavior { private readonly Random rng = new(); [ConsoleCommand("css_roll", @@ -21,7 +21,7 @@ public void Command_Toggle(CCSPlayerController? player, CommandInfo command) { if (player == null) return; if (!warden.IsWarden(player)) { - wardenNotifications.NotWarden.ToPlayerChat(player); + wardenLocale.NotWarden.ToChat(player); return; } diff --git a/mod/Jailbreak.Warden/Commands/SpecialTreatmentCommandsBehavior.cs b/mod/Jailbreak.Warden/Commands/SpecialTreatmentCommandsBehavior.cs index 79a6791a..fd51ec8c 100644 --- a/mod/Jailbreak.Warden/Commands/SpecialTreatmentCommandsBehavior.cs +++ b/mod/Jailbreak.Warden/Commands/SpecialTreatmentCommandsBehavior.cs @@ -4,6 +4,7 @@ using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Formatting.Extensions; using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.Warden; using Jailbreak.Public; using Jailbreak.Public.Behaviors; using Jailbreak.Public.Mod.Warden; @@ -12,9 +13,8 @@ namespace Jailbreak.Warden.Commands; public class SpecialTreatmentCommandsBehavior(IWardenService warden, - ISpecialTreatmentService specialTreatment, - IGenericCommandNotifications generic, IWardenNotifications wardenNotifs) - : IPluginBehavior { + ISpecialTreatmentService specialTreatment, IGenericCmdLocale generic, + IWardenLocale wardenNotifs) : IPluginBehavior { [ConsoleCommand("css_treat", "Grant or revoke special treatment from a player")] [ConsoleCommand("css_st", "Grant or revoke special treatment from a player")] @@ -23,7 +23,7 @@ public void Command_Toggle(CCSPlayerController? player, CommandInfo command) { if (player == null) return; if (!warden.IsWarden(player)) { - wardenNotifs.NotWarden.ToPlayerChat(player).ToPlayerConsole(player); + wardenNotifs.NotWarden.ToChat(player).ToConsole(player); return; } @@ -38,15 +38,15 @@ public void Command_Toggle(CCSPlayerController? player, CommandInfo command) { if (eligible.Count == 0) { generic.PlayerNotFound(command.GetArg(1)) - .ToPlayerChat(player) - .ToPlayerConsole(player); + .ToChat(player) + .ToConsole(player); return; } if (eligible.Count != 1) { generic.PlayerFoundMultiple(command.GetArg(1)) - .ToPlayerChat(player) - .ToPlayerConsole(player); + .ToChat(player) + .ToConsole(player); return; } diff --git a/mod/Jailbreak.Warden/Commands/WardenCommandsBehavior.cs b/mod/Jailbreak.Warden/Commands/WardenCommandsBehavior.cs index 123bbd3f..b0abd2de 100644 --- a/mod/Jailbreak.Warden/Commands/WardenCommandsBehavior.cs +++ b/mod/Jailbreak.Warden/Commands/WardenCommandsBehavior.cs @@ -6,15 +6,16 @@ using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Formatting.Extensions; using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.Warden; using Jailbreak.Public.Behaviors; using Jailbreak.Public.Mod.Warden; +using Jailbreak.Public.Utils; namespace Jailbreak.Warden.Commands; -public class WardenCommandsBehavior(IWardenNotifications notifications, +public class WardenCommandsBehavior(IWardenLocale locale, IWardenSelectionService queue, IWardenService warden, - IGenericCommandNotifications generics, WardenConfig config) - : IPluginBehavior { + IGenericCmdLocale generics, WardenConfig config) : IPluginBehavior { private readonly Dictionary lastPassCommand = new(); @@ -33,14 +34,14 @@ public void Command_Pass(CCSPlayerController? player, CommandInfo command) { if (!warden.IsWarden(player)) return; // Handle warden pass - notifications.PassWarden(player).ToAllChat().ToAllCenter(); + locale.PassWarden(player).ToAllChat().ToAllCenter(); // GetPlayers() returns valid players, no need to error check here. foreach (var clients in Utilities.GetPlayers()) clients.ExecuteClientCommand( $"play sounds/{config.WardenPassedSoundName}"); - notifications.BecomeNextWarden.ToAllChat(); + locale.BecomeNextWarden.ToAllChat(); if (!warden.TryRemoveWarden(true)) Server.PrintToChatAll("[BUG] Couldn't remove warden :^("); @@ -54,26 +55,26 @@ public void Command_Fire(CCSPlayerController? player, CommandInfo command) { if (player == null) return; if (!warden.HasWarden || warden.Warden == null) { - notifications.CurrentWarden(null).ToPlayerChat(player); + locale.CurrentWarden(null).ToChat(player); return; } if (!AdminManager.PlayerHasPermissions(player, "@css/ban")) { - generics.NoPermissionMessage("@css/ban").ToPlayerChat(player); + generics.NoPermissionMessage("@css/ban").ToChat(player); return; } foreach (var client in Utilities.GetPlayers()) { if (AdminManager.PlayerHasPermissions(client, "@css/chat")) - notifications.FireWarden(warden.Warden, player).ToPlayerChat(client); + locale.FireWarden(warden.Warden, player).ToChat(client); else - notifications.FireWarden(warden.Warden).ToPlayerChat(client); + locale.FireWarden(warden.Warden).ToChat(client); client.ExecuteClientCommand( $"play sounds/{config.WardenPassedSoundName}"); } - notifications.BecomeNextWarden.ToAllChat(); + locale.BecomeNextWarden.ToAllChat(); lastPassCommand[warden.Warden] = DateTime.Now; @@ -96,22 +97,26 @@ public void Command_Warden(CCSPlayerController? player, CommandInfo command) { if (lastPassCommand.TryGetValue(player, out var last)) { var cooldown = last.AddSeconds(15); if (DateTime.Now < cooldown) { - generics.CommandOnCooldown(cooldown).ToPlayerChat(player); + generics.CommandOnCooldown(cooldown).ToChat(player); return; } } + if (RoundUtil.IsWarmup()) { + locale.CannotWardenDuringWarmup.ToChat(player); + return; + } + // Queue is open ? if (queue.Active) { if (!queue.InQueue(player)) { - if (queue.TryEnter(player)) - notifications.JoinRaffle.ToPlayerChat(player); + if (queue.TryEnter(player)) locale.JoinRaffle.ToChat(player); return; } if (queue.InQueue(player)) if (queue.TryExit(player)) - notifications.LeaveRaffle.ToPlayerChat(player); + locale.LeaveRaffle.ToChat(player); return; } @@ -121,7 +126,7 @@ public void Command_Warden(CCSPlayerController? player, CommandInfo command) { if (warden.TrySetWarden(player)) return; - notifications.CurrentWarden(warden.Warden).ToPlayerChat(player); + locale.CurrentWarden(warden.Warden).ToChat(player); } /// diff --git a/mod/Jailbreak.Warden/Global/WardenBehavior.cs b/mod/Jailbreak.Warden/Global/WardenBehavior.cs index 2883ad2a..845bd4d7 100644 --- a/mod/Jailbreak.Warden/Global/WardenBehavior.cs +++ b/mod/Jailbreak.Warden/Global/WardenBehavior.cs @@ -6,8 +6,8 @@ using CounterStrikeSharp.API.Modules.Cvars.Validators; using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Formatting.Extensions; -using Jailbreak.Formatting.Views; using Jailbreak.Formatting.Views.Logging; +using Jailbreak.Formatting.Views.Warden; using Jailbreak.Public; using Jailbreak.Public.Behaviors; using Jailbreak.Public.Extensions; @@ -31,7 +31,7 @@ internal struct PreWardenStats(int armorValue, int health, int maxHealth, } public class WardenBehavior(ILogger logger, - IWardenNotifications notifications, IRichLogService logs, + IWardenLocale locale, IRichLogService logs, ISpecialTreatmentService specialTreatment, IRebelService rebels, WardenConfig config, IMuteService mute) : IPluginBehavior, IWardenService { private readonly ISet bluePrisoners = @@ -100,7 +100,7 @@ public bool TrySetWarden(CCSPlayerController controller) { "m_clrRender"); } - notifications.NewWarden(Warden).ToAllChat().ToAllCenter(); + locale.NewWarden(Warden).ToAllChat().ToAllCenter(); Warden.Clan = "[WARDEN]"; Utilities.SetStateChanged(Warden, "CCSPlayerController", "m_szClan"); @@ -249,7 +249,7 @@ private void processWardenDeath() { logger.LogWarning("[Warden] BUG: Problem removing current warden :^("); // Warden died! - notifications.WardenDied.ToAllChat().ToAllCenter(); + locale.WardenDied.ToAllChat().ToAllCenter(); foreach (var player in Utilities.GetPlayers()) { if (!player.IsReal()) continue; @@ -257,7 +257,7 @@ private void processWardenDeath() { $"play sounds/{config.WardenKilledSoundName}"); } - notifications.BecomeNextWarden.ToAllChat(); + locale.BecomeNextWarden.ToAllChat(); unblueTimer ?.Kill(); // If the warden dies withing 3 seconds of becoming warden, we need to cancel the unblue timer @@ -387,13 +387,13 @@ public HookResult OnPlayerDisconnect(EventPlayerDisconnect ev, logger.LogWarning("[Warden] BUG: Problem removing current warden :^("); - notifications.WardenLeft.ToAllChat().ToAllCenter(); + locale.WardenLeft.ToAllChat().ToAllCenter(); foreach (var player in Utilities.GetPlayers()) player.ExecuteClientCommand( $"play sounds/{config.WardenPassedSoundName}"); - notifications.BecomeNextWarden.ToAllChat(); + locale.BecomeNextWarden.ToAllChat(); return HookResult.Continue; } } \ No newline at end of file diff --git a/mod/Jailbreak.Warden/Selection/WardenSelectionBehavior.cs b/mod/Jailbreak.Warden/Selection/WardenSelectionBehavior.cs index 4046965a..7c778f87 100644 --- a/mod/Jailbreak.Warden/Selection/WardenSelectionBehavior.cs +++ b/mod/Jailbreak.Warden/Selection/WardenSelectionBehavior.cs @@ -4,7 +4,7 @@ using CounterStrikeSharp.API.Core.Attributes.Registration; using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Formatting.Extensions; -using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.Warden; using Jailbreak.Public.Behaviors; using Jailbreak.Public.Extensions; using Jailbreak.Public.Generic; @@ -22,9 +22,9 @@ public class private readonly IPlayerState favor; - private readonly ILogger logger; + private readonly IWardenLocale locale; - private readonly IWardenNotifications notifications; + private readonly ILogger logger; /// /// A state dict that handles the player's current queue @@ -41,14 +41,14 @@ public class private bool queueInactive; public WardenSelectionBehavior(IPlayerStateFactory factory, - IWardenService warden, IWardenNotifications notifications, + IWardenService warden, IWardenLocale locale, ILogger logger, ICoroutines coroutines) { - this.warden = warden; - this.notifications = notifications; - this.logger = logger; - this.coroutines = coroutines; - queue = factory.Round(); - favor = factory.Global(); + this.warden = warden; + this.locale = locale; + this.logger = logger; + this.coroutines = coroutines; + queue = factory.Round(); + favor = factory.Global(); queueInactive = true; } @@ -82,7 +82,7 @@ public HookResult OnRoundStart(EventRoundStart ev, GameEventInfo info) { // Enable the warden queue queueInactive = false; - notifications.PickingShortly.ToAllChat(); + locale.PickingShortly.ToAllChat(); // Start a timer to pick the warden in 7 seconds ScheduleChooseWarden(); @@ -109,7 +109,7 @@ protected void OnChooseWarden() { eligible); if (eligible.Count == 0) { - notifications.NoWardens.ToAllChat(); + locale.NoWardens.ToAllChat(); queueInactive = true; return; diff --git a/mod/Jailbreak.Warden/SpecialTreatment/SpecialTreatmentBehavior.cs b/mod/Jailbreak.Warden/SpecialTreatment/SpecialTreatmentBehavior.cs index 04719a47..9cf2e9a8 100644 --- a/mod/Jailbreak.Warden/SpecialTreatment/SpecialTreatmentBehavior.cs +++ b/mod/Jailbreak.Warden/SpecialTreatment/SpecialTreatmentBehavior.cs @@ -2,7 +2,7 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using Jailbreak.Formatting.Extensions; -using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.Warden; using Jailbreak.Public.Behaviors; using Jailbreak.Public.Generic; using Jailbreak.Public.Mod.Rebel; @@ -11,7 +11,7 @@ namespace Jailbreak.Warden.SpecialTreatment; public class SpecialTreatmentBehavior(IPlayerStateFactory factory, - IRebelService rebel, ISpecialTreatmentNotifications notifications) + IRebelService rebel, IWardenSTLocale notifications) : IPluginBehavior, ISpecialTreatmentService { private readonly IPlayerState sts = factory.Round(); @@ -29,7 +29,7 @@ public void Grant(CCSPlayerController player) { if (rebel.IsRebel(player)) rebel.UnmarkRebel(player); setSpecialColor(player, true); - notifications.Granted.ToPlayerChat(player).ToPlayerCenter(player); + notifications.Granted.ToChat(player).ToCenter(player); notifications.GrantedTo(player).ToAllChat(); } @@ -42,7 +42,7 @@ public void Revoke(CCSPlayerController player) { setSpecialColor(player, false); - notifications.Revoked.ToPlayerChat(player).ToPlayerCenter(player); + notifications.Revoked.ToChat(player).ToCenter(player); notifications.RevokedFrom(player).ToAllChat(); } diff --git a/mod/Jailbreak.Warden/WardenServiceExtension.cs b/mod/Jailbreak.Warden/WardenServiceExtension.cs index ea31e10c..ebcc0397 100644 --- a/mod/Jailbreak.Warden/WardenServiceExtension.cs +++ b/mod/Jailbreak.Warden/WardenServiceExtension.cs @@ -24,6 +24,7 @@ public static void AddJailbreakWarden( serviceCollection.AddPluginBehavior(); serviceCollection.AddPluginBehavior(); serviceCollection.AddPluginBehavior(); + serviceCollection.AddPluginBehavior(); serviceCollection.AddPluginBehavior(); serviceCollection.AddPluginBehavior(); diff --git a/mod/Jailbreak.Zones/DistanceZone.cs b/mod/Jailbreak.Zones/DistanceZone.cs index 300613fa..65ff963d 100644 --- a/mod/Jailbreak.Zones/DistanceZone.cs +++ b/mod/Jailbreak.Zones/DistanceZone.cs @@ -1,5 +1,4 @@ -using System.Numerics; -using Jailbreak.Public.Extensions; +using Jailbreak.Public.Extensions; using Jailbreak.Public.Mod.Zones; using Vector = CounterStrikeSharp.API.Modules.Utils.Vector; @@ -19,12 +18,12 @@ public class DistanceZone(IList origins, float tolerance) : IZone { public bool IsInsideZone(Vector position) { if (ConvexHullUtil.IsInsideZone(position, cachedHull)) return true; - var minDistance = GetMinDistance(position); + var minDistance = GetMinDistanceSquared(position); return minDistance < tolerance; } - public float GetMinDistance(Vector position) { - return ConvexHullUtil.GetMinDistance(position, cachedHull); + public float GetMinDistanceSquared(Vector position) { + return ConvexHullUtil.GetMinDistanceSquared(position, cachedHull); } public Vector GetCenterPoint() { @@ -43,6 +42,10 @@ public void AddPoint(Vector point) { cachedHull = ConvexHullUtil.ComputeConvexHull(origins).ToList(); } + public float GetMinDistance(Vector position) { + return (float)Math.Sqrt(GetMinDistanceSquared(position)); + } + public bool IsInsideRegion(Vector position) { return origins.Any(origin => origin.DistanceSquared(position) < tolerance); } diff --git a/mod/Jailbreak.Zones/Jailbreak.Zones.csproj b/mod/Jailbreak.Zones/Jailbreak.Zones.csproj index 97a3ebb3..bcf1bdfa 100644 --- a/mod/Jailbreak.Zones/Jailbreak.Zones.csproj +++ b/mod/Jailbreak.Zones/Jailbreak.Zones.csproj @@ -12,7 +12,7 @@ - + diff --git a/mod/Jailbreak.Zones/MultiZoneWrapper.cs b/mod/Jailbreak.Zones/MultiZoneWrapper.cs index 5bd7b8c4..5b2430b4 100644 --- a/mod/Jailbreak.Zones/MultiZoneWrapper.cs +++ b/mod/Jailbreak.Zones/MultiZoneWrapper.cs @@ -4,6 +4,7 @@ using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Public.Extensions; using Jailbreak.Public.Mod.Zones; +using JetBrains.Annotations; namespace Jailbreak.Zones; @@ -17,7 +18,10 @@ public class MultiZoneWrapper(IEnumerable? zones = null) : IZone, IEnumerable { private readonly IEnumerable zones = zones ?? []; + [MustDisposeResource] public IEnumerator GetEnumerator() { return zones.GetEnumerator(); } + + [MustDisposeResource] IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public int Id { get; set; } @@ -26,8 +30,8 @@ public bool IsInsideZone(Vector position) { return zones.Any(zone => zone.IsInsideZone(position)); } - public float GetMinDistance(Vector position) { - return zones.Min(zone => zone.GetMinDistance(position)); + public float GetMinDistanceSquared(Vector position) { + return zones.Min(zone => zone.GetMinDistanceSquared(position)); } public Vector GetCenterPoint() { @@ -53,7 +57,8 @@ public void AddPoint(Vector point) { return; } - var minZone = zones.OrderBy(zone => zone.GetMinDistance(point)).First(); + var minZone = zones.OrderBy(zone => zone.GetMinDistanceSquared(point)) + .First(); minZone.AddPoint(point); } diff --git a/mod/Jailbreak.Zones/RandomZoneGenerator.cs b/mod/Jailbreak.Zones/RandomZoneGenerator.cs new file mode 100644 index 00000000..10a5c538 --- /dev/null +++ b/mod/Jailbreak.Zones/RandomZoneGenerator.cs @@ -0,0 +1,183 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Timers; +using Jailbreak.Public.Behaviors; +using Jailbreak.Public.Extensions; +using Jailbreak.Public.Mod.Zones; +using Jailbreak.Public.Utils; +using Timer = CounterStrikeSharp.API.Modules.Timers.Timer; + +namespace Jailbreak.Zones; + +public class RandomZoneGenerator(IZoneManager zoneManager, IZoneFactory factory) + : IPluginBehavior { + private IZone? cells; + private string? currentMap; + private IList? manualSpawnPoints; + private BasePlugin plugin = null!; + private IList? restrictedAreas; + private Timer? timer; + + public void Start(BasePlugin basePlugin, bool hotreload) { + plugin = basePlugin; + + startTimer(); + } + + void IDisposable.Dispose() { timer?.Kill(); } + + private void startTimer() { + timer?.Kill(); + timer = plugin.AddTimer(63f, tick, TimerFlags.REPEAT); + } + + private void reload() { + cells = getCells(); + restrictedAreas = getRestrictedAreas(); + manualSpawnPoints = getManualSpawnPoints(); + + if (manualSpawnPoints?.Count > Server.MaxPlayers) return; + + startTimer(); + } + + private void tick() { + if (currentMap != Server.MapName) reload(); + currentMap = Server.MapName; + foreach (var player in PlayerUtil.GetAlive()) tick(player); + } + + private void tick(CCSPlayerController player) { + var pawn = player.PlayerPawn.Value; + if (pawn == null) return; + if (!pawn.OnGroundLastTick) return; + var pos = pawn.AbsOrigin; + if (pos == null) return; + pos = pos.Clone(); + float dist; + if (cells != null) { + if (cells.IsInsideZone(pos)) return; + dist = cells.GetMinDistanceSquared(pos); + if (dist <= DistanceZone.WIDTH_CELL) return; + } + + if (restrictedAreas is { Count: > 0 }) { + dist = restrictedAreas.Min(a => a.GetMinDistanceSquared(pos)); + if (dist <= DistanceZone.WIDTH_MEDIUM_ROOM) return; + } + + if (manualSpawnPoints != null + && manualSpawnPoints.Count >= Server.MaxPlayers) + return; + + var autoSpawnPoints = getAutoSpawnPoints(); + + var allSpawnPoints = (manualSpawnPoints ?? []).Concat(autoSpawnPoints) + .ToList(); + + if (allSpawnPoints.Count > 0) { + dist = allSpawnPoints.Min(a => a.GetMinDistanceSquared(pos)); + if (dist <= DistanceZone.WIDTH_MEDIUM_ROOM) return; + } + + if (allSpawnPoints.Count > Server.MaxPlayers) { + var nearestPoint = autoSpawnPoints + .OrderBy(z => z.GetMinDistanceSquared(pos)) + .First(); + var currentScore = zoneScore(manualSpawnPoints); + var newPoints = new List(allSpawnPoints); + var zone = factory.CreateZone([pos]); + newPoints.Remove(nearestPoint); + newPoints.Add(zone); + + var newScore = zoneScore(newPoints); + + if (newScore <= currentScore) return; + + var map = Server.MapName; + zone.Id = nearestPoint.Id; + Server.NextFrameAsync(async () => { + await zoneManager.DeleteZone(zone.Id, map); + await zoneManager.PushZoneWithID(zone, ZoneType.SPAWN_AUTO, map); + }); + + return; + } + + var spawn = factory.CreateZone([pos]); + autoSpawnPoints.Add(spawn); + Server.NextFrameAsync(async () => { + await zoneManager.PushZone(spawn, ZoneType.SPAWN_AUTO, currentMap!); + }); + } + + private float zoneScore(IList? zones) { + // We want our spawnpoints to be as far away from each other as possible, + // and as equally distributed as possible. + + if (zones == null) return 0; + + var total = 0f; + foreach (var zone in zones) { + var min = zones.Min(z => z.GetMinDistanceSquared(zone.GetCenterPoint())); + total += min; + } + + return total; + } + + private IList getManualSpawnPoints() { + var zones = zoneManager.GetZones(ZoneType.SPAWN).GetAwaiter().GetResult(); + return zones; + } + + private IList getAutoSpawnPoints() { + var zones = zoneManager.GetZones(ZoneType.SPAWN_AUTO) + .GetAwaiter() + .GetResult(); + return zones; + } + + private IZone getCells() { + var result = zoneManager.GetZones(ZoneType.CELL).GetAwaiter().GetResult(); + if (result.Count > 0) return new MultiZoneWrapper(result); + + var bounds = new DistanceZone( + Utilities + .FindAllEntitiesByDesignerName("info_player_terrorist") + .Where(s => s.AbsOrigin != null) + .Select(s => s.AbsOrigin!) + .ToList(), DistanceZone.WIDTH_CELL); + return bounds; + } + + private IZone getArmory() { + var armory = zoneManager.GetZones(ZoneType.ARMORY).GetAwaiter().GetResult(); + if (armory.Count > 0) return new MultiZoneWrapper(armory); + + var bounds = new DistanceZone( + Utilities + .FindAllEntitiesByDesignerName< + SpawnPoint>("info_player_counterterrorist") + .Where(s => s.AbsOrigin != null) + .Select(s => s.AbsOrigin!) + .ToList(), DistanceZone.WIDTH_CELL); + return bounds; + } + + private IList getRestrictedAreas() { + List result = []; + var tZones = zoneManager.GetZones(ZoneType.ZONE_LIMIT_T) + .GetAwaiter() + .GetResult(); + + var ctZones = zoneManager.GetZones(ZoneType.ZONE_LIMIT_CT) + .GetAwaiter() + .GetResult(); + + result.AddRange(tZones); + result.AddRange(ctZones); + result.Add(getArmory()); + return result; + } +} \ No newline at end of file diff --git a/mod/Jailbreak.Zones/SqlZoneManager.cs b/mod/Jailbreak.Zones/SqlZoneManager.cs index e833dd4a..912d238e 100644 --- a/mod/Jailbreak.Zones/SqlZoneManager.cs +++ b/mod/Jailbreak.Zones/SqlZoneManager.cs @@ -8,95 +8,109 @@ namespace Jailbreak.Zones; public class SqlZoneManager(IZoneFactory factory) : IZoneManager { - public static FakeConVar CvSqlTable = new("css_jb_zonetable", + public static readonly FakeConVar CvSqlTable = new("css_jb_zonetable", "The table name for the zones", "cs2_jb_zones"); - public static FakeConVar CvSqlConnectionString = + public static readonly FakeConVar CvSqlConnectionString = new("css_jb_sqlconnection", "The connection string for the database", "", ConVarFlags.FCVAR_PROTECTED); - private readonly IDictionary> zones = - new Dictionary>(); + private readonly Dictionary> zones = new(); private BasePlugin plugin = null!; public void Start(BasePlugin basePlugin) { plugin = basePlugin; - Server.NextFrameAsync(async () => { await createTable(); }); + + CvSqlConnectionString.ValueChanged += async (_, _) => { + await LoadZones(Server.MapName); + }; basePlugin.RegisterListener(OnMapStart); basePlugin.RegisterListener(OnMapEnd); } - public void Dispose() { + void IDisposable.Dispose() { plugin.RemoveListener(OnMapStart); + plugin.RemoveListener(OnMapEnd); } public async Task DeleteZone(int zoneId, string map) { foreach (var list in zones.Values) { var zone = list.FirstOrDefault(z => z.Id == zoneId); - if (zone != null) { - list.Remove(zone); - break; - } + if (zone == null) continue; + list.Remove(zone); + break; } - var conn = createConnection(); - if (conn == null) return; - await conn.OpenAsync(); - var cmd = conn.CreateCommand(); - cmd.CommandText = $""" - DELETE FROM {CvSqlTable.Value.Trim('"')} - WHERE zoneid = @zoneid - AND map = @map - """; - - cmd.Parameters.AddWithValue("@zoneid", zoneId); - cmd.Parameters.AddWithValue("@map", map); - await cmd.ExecuteNonQueryAsync(); + foreach (var list in zones.Values) { + var zone = list.FirstOrDefault(z => z.Id == zoneId); + if (zone == null) continue; + list.Remove(zone); + break; + } - await conn.CloseAsync(); + await using var conn = createConnection(); + if (conn == null) return; + try { + await conn.OpenAsync(); + await using var cmd = conn.CreateCommand(); + cmd.CommandText = $""" + DELETE FROM {CvSqlTable.Value.Trim('"')} + WHERE zoneid = @zoneid + AND map = @map + """; + + cmd.Parameters.AddWithValue("@zoneid", zoneId); + cmd.Parameters.AddWithValue("@map", map); + await cmd.ExecuteNonQueryAsync(); + } catch (MySqlException e) { Console.WriteLine(e); } finally { + await conn.CloseAsync(); + } } public async Task PushZoneWithID(IZone zone, ZoneType type, string map) { - Server.NextFrame(() => { Server.PrintToConsole("Pushing zone to SQL"); }); if (!zones.TryGetValue(type, out var list)) { list = new List(); zones[type] = list; } + // remove other zones with the same id + foreach (var zoneList in zones.Values) { + var z = zoneList.FirstOrDefault(z => z.Id == zone.Id); + if (z == null) continue; + zoneList.Remove(z); + break; + } + list.Add(zone); - var conn = createConnection(); + await using var conn = createConnection(); if (conn == null) return; - Server.NextFrame(() => { - Server.PrintToConsole("Successfully connect to sql"); - }); - await conn.OpenAsync(); - - var insertPointCommand = $""" - INSERT INTO {CvSqlTable.Value.Trim('"')} (map, type, zoneid, pointid, X, Y, Z) - VALUES (@map, @type, @zoneid, @pointid, @X, @Y, @Z) - """; - Server.NextFrame(() => { Server.PrintToConsole("Creating command"); }); - var pointId = 0; - - foreach (var point in zone.GetAllPoints()) { - var cmd = conn.CreateCommand(); - cmd.CommandText = insertPointCommand; - - cmd.Parameters.AddWithValue("@map", map); - cmd.Parameters.AddWithValue("@type", type.ToString()); - - cmd.Parameters.AddWithValue("@zoneid", zone.Id); - cmd.Parameters.AddWithValue("@X", point.X); - cmd.Parameters.AddWithValue("@Y", point.Y); - cmd.Parameters.AddWithValue("@Z", point.Z); - cmd.Parameters.AddWithValue("@pointid", pointId++); - await cmd.ExecuteNonQueryAsync(); + try { + await conn.OpenAsync(); + + var insertPointCommand = $""" + INSERT INTO {CvSqlTable.Value.Trim('"')} (map, type, zoneid, pointid, X, Y, Z) + VALUES (@map, @type, @zoneid, @pointid, @X, @Y, @Z) + """; + var pointId = 0; + + foreach (var point in zone.GetAllPoints()) { + await using var cmd = new MySqlCommand(insertPointCommand, conn); + cmd.Parameters.AddWithValue("@map", map); + cmd.Parameters.AddWithValue("@type", type.ToString()); + + cmd.Parameters.AddWithValue("@zoneid", zone.Id); + cmd.Parameters.AddWithValue("@X", point.X); + cmd.Parameters.AddWithValue("@Y", point.Y); + cmd.Parameters.AddWithValue("@Z", point.Z); + cmd.Parameters.AddWithValue("@pointid", pointId++); + await cmd.ExecuteNonQueryAsync(); + } + } catch (MySqlException e) { Console.WriteLine(e); } finally { + await conn.CloseAsync(); } - - await conn.CloseAsync(); } public Task> GetZones(string map, ZoneType type) { @@ -111,20 +125,20 @@ public async Task PushZone(IZone zone, ZoneType type, string map) { zones[type] = list; } - list.Add(zone); + var allZones = zones.Values.SelectMany(s => s).ToList(); - var nextId = zones.SelectMany(s => s.Value).Max(z => z.Id) + 1; + var nextId = allZones.Count == 0 ? 0 : allZones.Max(z => z.Id) + 1; zone.Id = nextId; - PushZoneWithID(zone, type, map); + await PushZoneWithID(zone, type, map); } public async Task UpdateZone(IZone zone, ZoneType type, int id) { - DeleteZone(id, Server.MapName); + await DeleteZone(id, Server.MapName); zone.Id = id; - PushZoneWithID(zone, type, Server.MapName); + await PushZoneWithID(zone, type, Server.MapName); } - public Task>> GetAllZones() { + public Task>> GetAllZones() { return Task.FromResult(zones); } @@ -140,72 +154,76 @@ public async Task LoadZones(string map) { private void OnMapEnd() { zones.Clear(); } private async Task createTable() { - Server.PrintToConsole("Creating table for zones with auth: " - + CvSqlConnectionString.Value); - var conn = createConnection(); + await using var conn = createConnection(); if (conn == null) return; - await conn.OpenAsync(); - - var cmdText = $""" - CREATE TABLE IF NOT EXISTS {CvSqlTable.Value.Trim('"')}( - zoneid INT NOT NULL, - pointid INT NOT NULL, - map VARCHAR(64) NOT NULL, - type VARCHAR(32) NOT NULL, - X FLOAT NOT NULL, - Y FLOAT NOT NULL, - Z FLOAT NOT NULL, - PRIMARY KEY(map, zoneid, pointid)) - """; - - var cmd = new MySqlCommand(cmdText, conn); - await cmd.ExecuteNonQueryAsync(); - await conn.CloseAsync(); + try { + await conn.OpenAsync(); + + var cmdText = $""" + CREATE TABLE IF NOT EXISTS {CvSqlTable.Value.Trim('"')}( + zoneid INT NOT NULL, + pointid INT NOT NULL, + map VARCHAR(64) NOT NULL, + type VARCHAR(32) NOT NULL, + X FLOAT NOT NULL, + Y FLOAT NOT NULL, + Z FLOAT NOT NULL, + PRIMARY KEY(map, zoneid, pointid)) + """; + + await using var cmd = new MySqlCommand(cmdText, conn); + await cmd.ExecuteNonQueryAsync(); + } catch (MySqlException e) { Console.WriteLine(e); } finally { + await conn.CloseAsync(); + } } private void OnMapStart(string mapname) { - Server.NextFrameAsync(() => LoadZones(mapname)); + Server.NextFrameAsync(async () => { + await createTable(); + await LoadZones(mapname); + }); } - private MySqlCommand queryAllZones(string map, ZoneType type) { + private MySqlCommand queryAllZones(MySqlConnection conn, string map, + ZoneType type) { var cmdText = $""" SELECT * FROM {CvSqlTable.Value.Trim('"')} WHERE map = '{map}' AND type = '{type}' ORDER BY zoneid, pointid DESC """; - return new MySqlCommand(cmdText); + return new MySqlCommand(cmdText, conn); } public async Task LoadZones(string map, ZoneType type) { - var conn = createConnection(); + // var conn = createConnection(); + await using var conn = createConnection(); if (conn == null) return; - var cmd = queryAllZones(map, type); - - await conn.OpenAsync(); - cmd.Connection = conn; - - var reader = await cmd.ExecuteReaderAsync(); - - var currentZone = -1; - var pointId = -1; - var zoneCreator = new BasicZoneCreator(); - zoneCreator.BeginCreation(); - Server.NextFrame(() => { - while (reader.Read()) { - var point = new Vector(reader.GetFloat("X"), reader.GetFloat("Y"), - reader.GetFloat("Z")); - zoneCreator.AddPoint(point); + try { + await conn.OpenAsync(); + await using var cmd = queryAllZones(conn, map, type); + await using var reader = await cmd.ExecuteReaderAsync(); + var currentZone = -1; + var pointId = -1; + var zoneCreator = new BasicZoneCreator(); + zoneCreator.BeginCreation(); + while (await reader.ReadAsync()) { + var x = reader.GetFloat("X"); + var y = reader.GetFloat("Y"); + var z = reader.GetFloat("Z"); var zoneId = reader.GetInt32("zoneid"); - pointId = reader.GetInt32("pointid"); - - // We just started reading zones - if (currentZone == -1) currentZone = zoneId; - - if (pointId == 0 || zoneId != currentZone) { + var pid = reader.GetInt32("pointid"); + Server.NextFrame(() => { + var point = new Vector(x, y, z); + zoneCreator.AddPoint(point); + // We just started reading zones + if (currentZone == -1) currentZone = zoneId; + + if (pid != 0 && zoneId == currentZone) return; if (zoneId != currentZone) - printNotClosedWarning(map, zoneId, pointId, currentZone); + printNotClosedWarning(map, zoneId, pid, currentZone); // Assume the zone is closed and allow the new zone to be created var zone = zoneCreator.Build(factory); if (!zones.ContainsKey(type)) zones[type] = new List(); @@ -214,13 +232,14 @@ public async Task LoadZones(string map, ZoneType type) { pointId = -1; currentZone = -1; zoneCreator.BeginCreation(); - } + }); } - reader.Close(); - if (pointId > 0) printNotClosedWarning(map, -1, pointId, currentZone); - }); + } catch (MySqlException e) { Console.WriteLine(e); } finally { + await conn.CloseAsync(); + } + await Task.Delay((int)Math.Ceiling(Server.TickInterval / 1000) * 5); } diff --git a/mod/Jailbreak.Zones/ZoneMovementRestrictor.cs b/mod/Jailbreak.Zones/ZoneMovementRestrictor.cs index 5a5536af..b97c203d 100644 --- a/mod/Jailbreak.Zones/ZoneMovementRestrictor.cs +++ b/mod/Jailbreak.Zones/ZoneMovementRestrictor.cs @@ -14,7 +14,7 @@ public ZoneMovementRestrictor(BasePlugin plugin, CCSPlayerController player, } public override float DistanceFrom(Vector vec) { - return zone.GetMinDistance(vec); + return zone.GetMinDistanceSquared(vec); } public override Vector GetCenter() { return zone.GetCenterPoint(); } diff --git a/mod/Jailbreak.Zones/ZoneServiceExtension.cs b/mod/Jailbreak.Zones/ZoneServiceExtension.cs index a90dc7e2..1a87f52a 100644 --- a/mod/Jailbreak.Zones/ZoneServiceExtension.cs +++ b/mod/Jailbreak.Zones/ZoneServiceExtension.cs @@ -8,5 +8,6 @@ public static class ZoneServiceExtension { public static void AddJailbreakZones(this IServiceCollection service) { service.AddPluginBehavior(); service.AddPluginBehavior(); + service.AddPluginBehavior(); } } \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Core/FormatObject.cs b/public/Jailbreak.Formatting/Core/FormatObject.cs index 7cd68eb6..e3f75b79 100644 --- a/public/Jailbreak.Formatting/Core/FormatObject.cs +++ b/public/Jailbreak.Formatting/Core/FormatObject.cs @@ -1,4 +1,5 @@ using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Formatting.Objects; using Jailbreak.Public.Extensions; @@ -36,6 +37,14 @@ public static implicit operator FormatObject(int value) { return new IntegerFormatObject(value); } + public static implicit operator FormatObject(float value) { + return new FloatFormatObject(value); + } + + public static implicit operator FormatObject(CsTeam value) { + return new TeamFormatObject(value); + } + public static FormatObject FromObject(object value) { return new StringFormatObject(value.ToString() ?? "null"); } diff --git a/public/Jailbreak.Formatting/Extensions/ViewExtensions.cs b/public/Jailbreak.Formatting/Extensions/ViewExtensions.cs index 0bd87a8c..067798b9 100644 --- a/public/Jailbreak.Formatting/Extensions/ViewExtensions.cs +++ b/public/Jailbreak.Formatting/Extensions/ViewExtensions.cs @@ -6,6 +6,8 @@ using Jailbreak.Public.Extensions; using Jailbreak.Public.Utils; +#pragma warning disable CS0618 // Type or member is obsolete + namespace Jailbreak.Formatting.Extensions; public static class ViewExtensions { @@ -26,25 +28,23 @@ public static IView ToServerConsole(this IView view) { } public static IView ToAllConsole(this IView view) { - Utilities.GetPlayers().ForEach(player => view.ToPlayerConsole(player)); - + view.ToConsole(Utilities.GetPlayers().ToArray()); return view; } public static IView ToAllChat(this IView view) { - Utilities.GetPlayers().ForEach(player => view.ToPlayerChat(player)); - + view.ToChat(Utilities.GetPlayers().ToArray()); return view; } public static IView ToAllCenter(this IView view) { - Utilities.GetPlayers().ForEach(player => view.ToPlayerCenter(player)); - + view.ToCenter(Utilities.GetPlayers().ToArray()); return view; } #region Individual + [Obsolete("Use ToConsole instead")] public static IView ToPlayerConsole(this IView view, CCSPlayerController player) { if (!player.IsReal() || player.IsBot) return view; @@ -56,6 +56,7 @@ public static IView ToPlayerConsole(this IView view, return view; } + [Obsolete("Use ToChat instead")] public static IView ToPlayerChat(this IView view, CCSPlayerController player) { if (!player.IsReal() || player.IsBot) return view; @@ -67,6 +68,7 @@ public static IView ToPlayerChat(this IView view, return view; } + [Obsolete("Use ToCenter instead")] public static IView ToPlayerCenter(this IView view, CCSPlayerController player) { if (!player.IsReal() || player.IsBot) return view; @@ -79,6 +81,7 @@ public static IView ToPlayerCenter(this IView view, return view; } + [Obsolete("Use ToCenterHtml instead")] public static IView ToPlayerCenterHtml(this IView view, CCSPlayerController player) { if (!player.IsReal() || player.IsBot) return view; @@ -93,11 +96,11 @@ public static IView ToPlayerCenterHtml(this IView view, #endregion - #region team + #region Team public static IView ToTeamChat(this IView view, CsTeam team) { foreach (var player in PlayerUtil.FromTeam(team, false)) - view.ToPlayerChat(player); + view.ToChat(player); return view; } @@ -117,4 +120,29 @@ public static IView ToTeamCenterHtml(this IView view, CsTeam team) { } #endregion + + #region params + + public static IView ToConsole(this IView view, + params CCSPlayerController[] players) { + foreach (var player in players) view.ToPlayerConsole(player); + + return view; + } + + public static IView ToChat(this IView view, + params CCSPlayerController[] players) { + foreach (var player in players) view.ToPlayerChat(player); + + return view; + } + + public static IView ToCenter(this IView view, + params CCSPlayerController[] players) { + foreach (var player in players) view.ToPlayerCenter(player); + + return view; + } + + #endregion } \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Logistics/LanguageConfig.cs b/public/Jailbreak.Formatting/Logistics/LanguageConfig.cs index 2f75c5b0..304cdbfa 100644 --- a/public/Jailbreak.Formatting/Logistics/LanguageConfig.cs +++ b/public/Jailbreak.Formatting/Logistics/LanguageConfig.cs @@ -1,80 +1,23 @@ -using Jailbreak.Formatting.Views; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; namespace Jailbreak.Formatting.Logistics; -public class LanguageConfig where TDialect : IDialect { - private readonly IServiceCollection collection; - - public LanguageConfig(IServiceCollection collection) { - this.collection = collection; - } - - public void WithGenericCommand() - where TGenericCommand : class, ILanguage, - IGenericCommandNotifications { - collection.AddSingleton(); - } - - public void WithRatio() - where TRatio : class, ILanguage, IRatioNotifications { - collection.AddSingleton(); - } - - public void WithWarden() - where TWarden : class, ILanguage, IWardenNotifications { - collection.AddSingleton(); - } - - public void WithRebel() - where TRebel : class, ILanguage, IRebelNotifications { - collection.AddSingleton(); - } - - public void WithJihadC4() - where TJihadC4 : class, ILanguage, IJihadC4Notifications { - collection.AddSingleton(); - } - - public void WithSpecialDay() - where TSpecialDay : class, ILanguage, ISpecialDayMessages { - collection.AddSingleton(); - } - - public void WithLogging() - where TLogging : class, ILanguage, ILogMessages { - collection.AddSingleton(); - } - - public void WithRollCommand() - where TRollCommand : class, ILanguage, IRollCommandNotications { - collection.AddSingleton(); - } - - public void WithLastRequest() - where TLastRequest : class, ILanguage, ILastRequestMessages { - collection.AddSingleton(); - } - - public void WithSpecialTreatment() - where TSpecialTreatment : class, ILanguage, - ISpecialTreatmentNotifications { - collection - .AddSingleton(); - } - - public void WithMute() - where TMute : class, ILanguage, IPeaceMessages { - collection.AddSingleton(); - } - - public void WithRaceLR() - where TRaceLR : class, ILanguage, IRaceLRMessages { - collection.AddSingleton(); - } - - public void WithLastGuard() - where TLastGuard : class, ILanguage, ILastGuardNotifications { - collection.AddSingleton(); +public class LanguageConfig(IServiceCollection collection) + where TDialect : IDialect { + public void Configure(Dictionary serviceMap) { + foreach (var (service, implementation) in serviceMap) { + var method = typeof(IServiceCollection).GetMethod("AddSingleton") + ?.MakeGenericMethod(service, implementation); + method?.Invoke(collection, null); + } + } +} + +public static class ServiceCollectionExtensions { + public static void AddLanguage(this IServiceCollection services, + Action> configure) + where TDialect : class, IDialect { + var config = new LanguageConfig(services); + configure(config); } } \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Logistics/RegisterLanguageExtensions.cs b/public/Jailbreak.Formatting/Logistics/RegisterLanguageExtensions.cs deleted file mode 100644 index da5fae9c..00000000 --- a/public/Jailbreak.Formatting/Logistics/RegisterLanguageExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace Jailbreak.Formatting.Logistics; - -public static class RegisterLanguageExtensions { - public static void AddLanguage(this IServiceCollection collection, - Action> factory) where TDialect : IDialect { - var config = new LanguageConfig(collection); - - factory(config); - } -} \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Objects/FloatFormatObject.cs b/public/Jailbreak.Formatting/Objects/FloatFormatObject.cs new file mode 100644 index 00000000..52cbea20 --- /dev/null +++ b/public/Jailbreak.Formatting/Objects/FloatFormatObject.cs @@ -0,0 +1,14 @@ +using Jailbreak.Formatting.Core; + +namespace Jailbreak.Formatting.Objects; + +public class FloatFormatObject(float value, char chatColor = '\x09') + : FormatObject { + public float Value { get; } = value; + + public override string ToChat() { return $"{chatColor}{Value:F2}"; } + + public override string ToPanorama() { return Value.ToString("F2"); } + + public override string ToPlain() { return Value.ToString("F2"); } +} \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Objects/PlayerFormatObject.cs b/public/Jailbreak.Formatting/Objects/PlayerFormatObject.cs index 43c39a52..fc0c0322 100644 --- a/public/Jailbreak.Formatting/Objects/PlayerFormatObject.cs +++ b/public/Jailbreak.Formatting/Objects/PlayerFormatObject.cs @@ -7,8 +7,11 @@ namespace Jailbreak.Formatting.Objects; public class PlayerFormatObject(CCSPlayerController player) : FormatObject { private readonly string name = player.PlayerName; + private readonly CsTeam team = player.Team; - public override string ToChat() { return $"{ChatColors.Yellow}{name}"; } + public override string ToChat() { + return $"{TeamFormatObject.GetChatColor(team)}{name}"; + } public override string ToPanorama() { return name.Sanitize(); } diff --git a/public/Jailbreak.Formatting/Objects/TeamFormatObject.cs b/public/Jailbreak.Formatting/Objects/TeamFormatObject.cs new file mode 100644 index 00000000..2d6aa228 --- /dev/null +++ b/public/Jailbreak.Formatting/Objects/TeamFormatObject.cs @@ -0,0 +1,32 @@ +using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.Formatting.Core; + +namespace Jailbreak.Formatting.Objects; + +public class TeamFormatObject(CsTeam team) : FormatObject { + public override string ToChat() { return GetChatColor(team) + getName(); } + + public override string ToPanorama() { return getName(); } + + public override string ToPlain() { return getName(); } + + private string getName() { + return team switch { + CsTeam.CounterTerrorist => "Guards", + CsTeam.Terrorist => "Prisoners", + CsTeam.None => "No one", + CsTeam.Spectator => "Spectators", + _ => throw new ArgumentOutOfRangeException(nameof(team), team, null) + }; + } + + public static char GetChatColor(CsTeam team) { + return team switch { + CsTeam.Terrorist => ChatColors.Red, + CsTeam.CounterTerrorist => ChatColors.Blue, + CsTeam.None => ChatColors.Default, + CsTeam.Spectator => ChatColors.Grey, + _ => throw new ArgumentOutOfRangeException() + }; + } +} \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Views/IJihadC4Notifications.cs b/public/Jailbreak.Formatting/Views/IC4Locale.cs similarity index 82% rename from public/Jailbreak.Formatting/Views/IJihadC4Notifications.cs rename to public/Jailbreak.Formatting/Views/IC4Locale.cs index 1c126bd1..edcfd6f8 100644 --- a/public/Jailbreak.Formatting/Views/IJihadC4Notifications.cs +++ b/public/Jailbreak.Formatting/Views/IC4Locale.cs @@ -2,7 +2,7 @@ namespace Jailbreak.Formatting.Views; -public interface IJihadC4Notifications { +public interface IC4Locale { public IView JihadC4Pickup { get; } public IView JihadC4Received { get; } diff --git a/public/Jailbreak.Formatting/Views/IGenericCommandNotifications.cs b/public/Jailbreak.Formatting/Views/IGenericCmdLocale.cs similarity index 82% rename from public/Jailbreak.Formatting/Views/IGenericCommandNotifications.cs rename to public/Jailbreak.Formatting/Views/IGenericCmdLocale.cs index b3f2c58d..b5b66861 100644 --- a/public/Jailbreak.Formatting/Views/IGenericCommandNotifications.cs +++ b/public/Jailbreak.Formatting/Views/IGenericCmdLocale.cs @@ -2,10 +2,11 @@ namespace Jailbreak.Formatting.Views; -public interface IGenericCommandNotifications { +public interface IGenericCmdLocale { public IView PlayerNotFound(string query); public IView PlayerFoundMultiple(string query); public IView CommandOnCooldown(DateTime cooldownEndsAt); public IView InvalidParameter(string parameter, string expected); public IView NoPermissionMessage(string permission); + public IView Error(string message); } \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Views/ILastGuardNotifications.cs b/public/Jailbreak.Formatting/Views/ILGLocale.cs similarity index 82% rename from public/Jailbreak.Formatting/Views/ILastGuardNotifications.cs rename to public/Jailbreak.Formatting/Views/ILGLocale.cs index 6d777667..55e9a151 100644 --- a/public/Jailbreak.Formatting/Views/ILastGuardNotifications.cs +++ b/public/Jailbreak.Formatting/Views/ILGLocale.cs @@ -3,7 +3,7 @@ namespace Jailbreak.Formatting.Views; -public interface ILastGuardNotifications { +public interface ILGLocale { public IView LGStarted(CCSPlayerController lastGuard, int ctHealth, int tHealth); } \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Views/ILogMessages.cs b/public/Jailbreak.Formatting/Views/ILogLocale.cs similarity index 96% rename from public/Jailbreak.Formatting/Views/ILogMessages.cs rename to public/Jailbreak.Formatting/Views/ILogLocale.cs index e9c72fdd..f1483cd4 100644 --- a/public/Jailbreak.Formatting/Views/ILogMessages.cs +++ b/public/Jailbreak.Formatting/Views/ILogLocale.cs @@ -7,7 +7,7 @@ namespace Jailbreak.Formatting.Views; -public interface ILogMessages { +public interface ILogLocale { public IView BeginJailbreakLogs { get; } public IView EndJailbreakLogs { get; } diff --git a/public/Jailbreak.Formatting/Views/IRebelNotifications.cs b/public/Jailbreak.Formatting/Views/IRebelLocale.cs similarity index 74% rename from public/Jailbreak.Formatting/Views/IRebelNotifications.cs rename to public/Jailbreak.Formatting/Views/IRebelLocale.cs index 76098506..0607b6c3 100644 --- a/public/Jailbreak.Formatting/Views/IRebelNotifications.cs +++ b/public/Jailbreak.Formatting/Views/IRebelLocale.cs @@ -2,6 +2,6 @@ namespace Jailbreak.Formatting.Views; -public interface IRebelNotifications { +public interface IRebelLocale { public IView NoLongerRebel { get; } } \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Views/IRollCommandNotications.cs b/public/Jailbreak.Formatting/Views/IRollCommandNotications.cs deleted file mode 100644 index 0cd92aa0..00000000 --- a/public/Jailbreak.Formatting/Views/IRollCommandNotications.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Jailbreak.Formatting.Base; - -namespace Jailbreak.Formatting.Views; - -public interface IRollCommandNotications { - IView Roll(int roll); -} \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Views/ISDInstanceLocale.cs b/public/Jailbreak.Formatting/Views/ISDInstanceLocale.cs new file mode 100644 index 00000000..ef28b39f --- /dev/null +++ b/public/Jailbreak.Formatting/Views/ISDInstanceLocale.cs @@ -0,0 +1,14 @@ +using Jailbreak.Formatting.Base; + +namespace Jailbreak.Formatting.Views; + +public interface ISDInstanceLocale : ISDLocale { + public string Name { get; } + public string[] Description { get; } + + public IView SpecialDayStart { get; } + + public IView SpecialDayEnd { get; } + + public IView BeginsIn(int seconds); +} \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Views/ISDLocale.cs b/public/Jailbreak.Formatting/Views/ISDLocale.cs new file mode 100644 index 00000000..87cc6a40 --- /dev/null +++ b/public/Jailbreak.Formatting/Views/ISDLocale.cs @@ -0,0 +1,10 @@ +using Jailbreak.Formatting.Base; + +namespace Jailbreak.Formatting.Views; + +public interface ISDLocale { + public IView SpecialDayRunning(string name); + public IView InvalidSpecialDay(string name); + public IView SpecialDayCooldown(int rounds); + public IView TooLateForSpecialDay(int maxTime); +} \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Views/ISpecialDayInstanceMessages.cs b/public/Jailbreak.Formatting/Views/ISpecialDayInstanceMessages.cs deleted file mode 100644 index 3dd196e2..00000000 --- a/public/Jailbreak.Formatting/Views/ISpecialDayInstanceMessages.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Jailbreak.Formatting.Base; - -namespace Jailbreak.Formatting.Views; - -public interface ISpecialDayInstanceMessages { - public string Name { get; } - public string? Description { get; } - - public IView SpecialDayStart { get; } - - public IView SpecialDayEnd() { - return new SimpleView { ISpecialDayMessages.PREFIX, Name, "ended." }; - } - - public IView BeginsIn(int seconds); - // return seconds == 0 ? - // new SimpleView { ISpecialDayMessages.PREFIX, Name, "begins now!" } : - // new SimpleView { - // ISpecialDayMessages.PREFIX, - // Name, - // "begins in", - // seconds, - // "seconds." - // }; - // } -} \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Views/ISpecialDayMessages.cs b/public/Jailbreak.Formatting/Views/ISpecialDayMessages.cs deleted file mode 100644 index 74c3c82c..00000000 --- a/public/Jailbreak.Formatting/Views/ISpecialDayMessages.cs +++ /dev/null @@ -1,20 +0,0 @@ -using CounterStrikeSharp.API.Modules.Utils; -using Jailbreak.Formatting.Base; -using Jailbreak.Formatting.Core; -using Jailbreak.Formatting.Objects; - -namespace Jailbreak.Formatting.Views; - -public interface ISpecialDayMessages { - public static readonly FormatObject PREFIX = - new HiddenFormatObject( - $" {ChatColors.BlueGrey}[{ChatColors.Green}S{ChatColors.Blue}D{ChatColors.BlueGrey}]") { - // Hide in panorama and center text - Plain = false, Panorama = false, Chat = true - }; - - public IView SpecialDayRunning(string name); - public IView InvalidSpecialDay(string name); - public IView SpecialDayCooldown(int rounds); - public IView TooLateForSpecialDay(int maxTime); -} \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Views/LastRequest/ILRB4BLocale.cs b/public/Jailbreak.Formatting/Views/LastRequest/ILRB4BLocale.cs new file mode 100644 index 00000000..8bf7c14a --- /dev/null +++ b/public/Jailbreak.Formatting/Views/LastRequest/ILRB4BLocale.cs @@ -0,0 +1,12 @@ +using CounterStrikeSharp.API.Core; +using Jailbreak.Formatting.Base; + +namespace Jailbreak.Formatting.Views.LastRequest; + +/// +/// Last Request Bullet 4 Bullet Locale +/// +public interface ILRB4BLocale : ILRLocale { + public IView PlayerGoesFirst(CCSPlayerController player); + public IView WeaponSelected(CCSPlayerController player, string weapon); +} \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Views/LastRequest/ILRCFLocale.cs b/public/Jailbreak.Formatting/Views/LastRequest/ILRCFLocale.cs new file mode 100644 index 00000000..16b3bc2c --- /dev/null +++ b/public/Jailbreak.Formatting/Views/LastRequest/ILRCFLocale.cs @@ -0,0 +1,13 @@ +using CounterStrikeSharp.API.Core; +using Jailbreak.Formatting.Base; + +namespace Jailbreak.Formatting.Views.LastRequest; + +/// +/// Last Request Coin Flip Locale +/// +public interface ILRCFLocale : ILRLocale { + public IView FailedToChooseInTime(bool choice); + public IView GuardChose(CCSPlayerController guard, bool choice); + public IView CoinLandsOn(bool heads); +} \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Views/ILastRequestMessages.cs b/public/Jailbreak.Formatting/Views/LastRequest/ILRLocale.cs similarity index 63% rename from public/Jailbreak.Formatting/Views/ILastRequestMessages.cs rename to public/Jailbreak.Formatting/Views/LastRequest/ILRLocale.cs index 0006e376..91aea8de 100644 --- a/public/Jailbreak.Formatting/Views/ILastRequestMessages.cs +++ b/public/Jailbreak.Formatting/Views/LastRequest/ILRLocale.cs @@ -3,19 +3,27 @@ using Jailbreak.Public.Mod.LastRequest; using Jailbreak.Public.Mod.LastRequest.Enums; -namespace Jailbreak.Formatting.Views; +namespace Jailbreak.Formatting.Views.LastRequest; -public interface ILastRequestMessages { +/// +/// Last Request Locale +/// +public interface ILRLocale { public IView DamageBlockedInsideLastRequest { get; } public IView DamageBlockedNotInSameLR { get; } public IView LastRequestEnabled(); public IView LastRequestDisabled(); public IView LastRequestNotEnabled(); public IView InvalidLastRequest(string query); - public IView InvalidPlayerChoice(CCSPlayerController player, string reason); public IView InformLastRequest(AbstractLastRequest lr); public IView AnnounceLastRequest(AbstractLastRequest lr); public IView LastRequestDecided(AbstractLastRequest lr, LRResult result); public IView CannotLR(string reason); public IView CannotLR(CCSPlayerController player, string reason); + public IView LastRequestCountdown(int seconds); + + public IView WinByDefault(CCSPlayerController player); + public IView WinByHealth(CCSPlayerController player); + public IView WinByReason(CCSPlayerController player, string reason); + public IView Win(CCSPlayerController player); } \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Views/LastRequest/ILRRPSLocale.cs b/public/Jailbreak.Formatting/Views/LastRequest/ILRRPSLocale.cs new file mode 100644 index 00000000..90bc2ae6 --- /dev/null +++ b/public/Jailbreak.Formatting/Views/LastRequest/ILRRPSLocale.cs @@ -0,0 +1,16 @@ +using CounterStrikeSharp.API.Core; +using Jailbreak.Formatting.Base; + +namespace Jailbreak.Formatting.Views.LastRequest; + +/// +/// Last Request Rock Paper Scissors Locale +/// +public interface ILRRPSLocale : ILRLocale { + public IView PlayerMadeChoice(CCSPlayerController player); + public IView BothPlayersMadeChoice(); + public IView Tie(); + + public IView Results(CCSPlayerController guard, CCSPlayerController prisoner, + int guardPick, int prisonerPick); +} \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Views/IRaceLRMessages.cs b/public/Jailbreak.Formatting/Views/LastRequest/ILRRaceLocale.cs similarity index 63% rename from public/Jailbreak.Formatting/Views/IRaceLRMessages.cs rename to public/Jailbreak.Formatting/Views/LastRequest/ILRRaceLocale.cs index 30721ade..53149946 100644 --- a/public/Jailbreak.Formatting/Views/IRaceLRMessages.cs +++ b/public/Jailbreak.Formatting/Views/LastRequest/ILRRaceLocale.cs @@ -1,9 +1,12 @@ using CounterStrikeSharp.API.Core; using Jailbreak.Formatting.Base; -namespace Jailbreak.Formatting.Views; +namespace Jailbreak.Formatting.Views.LastRequest; -public interface IRaceLRMessages { +/// +/// Last Request Race Locale +/// +public interface ILRRaceLocale { public IView EndRaceInstruction { get; } public IView RaceStartingMessage(CCSPlayerController prisoner); diff --git a/public/Jailbreak.Formatting/Views/Warden/IWardenCmdOpenLocale.cs b/public/Jailbreak.Formatting/Views/Warden/IWardenCmdOpenLocale.cs new file mode 100644 index 00000000..140ba008 --- /dev/null +++ b/public/Jailbreak.Formatting/Views/Warden/IWardenCmdOpenLocale.cs @@ -0,0 +1,10 @@ +using Jailbreak.Formatting.Base; + +namespace Jailbreak.Formatting.Views.Warden; + +public interface IWardenCmdOpenLocale { + public IView CellsOpened { get; } + public IView OpeningFailed { get; } + public IView AlreadyOpened { get; } + public IView CannotOpenYet(int seconds); +} \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Views/Warden/IWardenCmdRollLocale.cs b/public/Jailbreak.Formatting/Views/Warden/IWardenCmdRollLocale.cs new file mode 100644 index 00000000..9dda48a5 --- /dev/null +++ b/public/Jailbreak.Formatting/Views/Warden/IWardenCmdRollLocale.cs @@ -0,0 +1,7 @@ +using Jailbreak.Formatting.Base; + +namespace Jailbreak.Formatting.Views.Warden; + +public interface IWardenCmdRollLocale { + IView Roll(int roll); +} \ No newline at end of file diff --git a/public/Jailbreak.Formatting/Views/IWardenNotifications.cs b/public/Jailbreak.Formatting/Views/Warden/IWardenLocale.cs similarity index 91% rename from public/Jailbreak.Formatting/Views/IWardenNotifications.cs rename to public/Jailbreak.Formatting/Views/Warden/IWardenLocale.cs index ae5e0815..177eaeb5 100644 --- a/public/Jailbreak.Formatting/Views/IWardenNotifications.cs +++ b/public/Jailbreak.Formatting/Views/Warden/IWardenLocale.cs @@ -3,9 +3,9 @@ // ReSharper disable InconsistentNaming -namespace Jailbreak.Formatting.Views; +namespace Jailbreak.Formatting.Views.Warden; -public interface IWardenNotifications { +public interface IWardenLocale { public IView PickingShortly { get; } public IView NoWardens { get; } public IView WardenLeft { get; } @@ -16,6 +16,8 @@ public interface IWardenNotifications { public IView NotWarden { get; } public IView FireCommandFailed { get; } + public IView CannotWardenDuringWarmup { get; } + /// /// Create a view for when the specified player passes warden /// diff --git a/public/Jailbreak.Formatting/Views/IPeaceMessages.cs b/public/Jailbreak.Formatting/Views/Warden/IWardenPeaceLocale.cs similarity index 84% rename from public/Jailbreak.Formatting/Views/IPeaceMessages.cs rename to public/Jailbreak.Formatting/Views/Warden/IWardenPeaceLocale.cs index 0d42ac6a..fc0e6dbb 100644 --- a/public/Jailbreak.Formatting/Views/IPeaceMessages.cs +++ b/public/Jailbreak.Formatting/Views/Warden/IWardenPeaceLocale.cs @@ -1,8 +1,8 @@ using Jailbreak.Formatting.Base; -namespace Jailbreak.Formatting.Views; +namespace Jailbreak.Formatting.Views.Warden; -public interface IPeaceMessages { +public interface IWardenPeaceLocale { public IView UnmutedGuards { get; } public IView UnmutedPrisoners { get; } diff --git a/public/Jailbreak.Formatting/Views/ISpecialTreatmentNotifications.cs b/public/Jailbreak.Formatting/Views/Warden/IWardenSTLocale.cs similarity index 74% rename from public/Jailbreak.Formatting/Views/ISpecialTreatmentNotifications.cs rename to public/Jailbreak.Formatting/Views/Warden/IWardenSTLocale.cs index 8208d9be..7b8275c3 100644 --- a/public/Jailbreak.Formatting/Views/ISpecialTreatmentNotifications.cs +++ b/public/Jailbreak.Formatting/Views/Warden/IWardenSTLocale.cs @@ -1,9 +1,9 @@ using CounterStrikeSharp.API.Core; using Jailbreak.Formatting.Base; -namespace Jailbreak.Formatting.Views; +namespace Jailbreak.Formatting.Views.Warden; -public interface ISpecialTreatmentNotifications { +public interface IWardenSTLocale { public IView Granted { get; } public IView Revoked { get; } diff --git a/public/Jailbreak.Public/Behaviors/IPluginBehavior.cs b/public/Jailbreak.Public/Behaviors/IPluginBehavior.cs index 74d4f3b4..6baed579 100644 --- a/public/Jailbreak.Public/Behaviors/IPluginBehavior.cs +++ b/public/Jailbreak.Public/Behaviors/IPluginBehavior.cs @@ -12,4 +12,6 @@ void IDisposable.Dispose() { } /// Tells the plugin that it will be starting imminently /// void Start(BasePlugin basePlugin) { } + + void Start(BasePlugin basePlugin, bool hotreload) { Start(basePlugin); } } \ No newline at end of file diff --git a/public/Jailbreak.Public/Extensions/CollectionExtensions.cs b/public/Jailbreak.Public/Extensions/CollectionExtensions.cs new file mode 100644 index 00000000..5b78e4b9 --- /dev/null +++ b/public/Jailbreak.Public/Extensions/CollectionExtensions.cs @@ -0,0 +1,19 @@ +namespace Jailbreak.Public.Extensions; + +public static class CollectionExtensions { + private static readonly Random Random = new(); + + public static void Shuffle(this IList list) { + var n = list.Count; + while (n > 1) { + n--; + var k = Random.Next(n + 1); + (list[k], list[n]) = (list[n], list[k]); + } + } + + public static void Shuffle(this IEnumerable enumerable) { + var list = enumerable.ToList(); + list.Shuffle(); + } +} \ No newline at end of file diff --git a/public/Jailbreak.Public/Extensions/PlayerExtensions.cs b/public/Jailbreak.Public/Extensions/PlayerExtensions.cs index 74793e7e..7e3a13bf 100644 --- a/public/Jailbreak.Public/Extensions/PlayerExtensions.cs +++ b/public/Jailbreak.Public/Extensions/PlayerExtensions.cs @@ -29,10 +29,10 @@ public static void Teleport(this CCSPlayerController player, if (!player.IsReal() || !target.IsReal()) return; var playerPawn = player.Pawn.Value; - if (playerPawn == null) return; + if (playerPawn == null || !playerPawn.IsValid) return; var targetPawn = target.Pawn.Value; - if (targetPawn == null) return; + if (targetPawn == null || !targetPawn.IsValid) return; if (targetPawn is { AbsRotation: not null, AbsOrigin: not null }) Teleport(player, targetPawn.AbsOrigin, targetPawn.AbsRotation); @@ -49,26 +49,22 @@ public static void Teleport(this CCSPlayerController player, Vector pos, } public static void Freeze(this CCSPlayerController player) { - if (!player.Pawn.IsValid - || player.Connected != PlayerConnectedState.PlayerConnected) - return; - - if (player.Pawn.Value == null) return; + if (!player.IsValid) return; + var pawn = player.PlayerPawn.Value; + if (pawn == null || !pawn.IsValid) return; - player.Pawn.Value.Freeze(); + pawn.Freeze(); } public static void UnFreeze(this CCSPlayerController player) { - if (!player.Pawn.IsValid - || player.Connected != PlayerConnectedState.PlayerConnected) - return; - - if (player.Pawn.Value == null) return; - - player.Pawn.Value.UnFreeze(); + if (!player.IsValid) return; + var pawn = player.PlayerPawn.Value; + if (pawn == null || !pawn.IsValid) return; + pawn.UnFreeze(); } public static void Freeze(this CBasePlayerPawn pawn) { + if (!pawn.IsValid) return; pawn.MoveType = MoveType_t.MOVETYPE_OBSOLETE; Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 1); @@ -76,6 +72,7 @@ public static void Freeze(this CBasePlayerPawn pawn) { } public static void UnFreeze(this CBasePlayerPawn pawn) { + if (!pawn.IsValid) return; pawn.MoveType = MoveType_t.MOVETYPE_WALK; Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 2); @@ -83,46 +80,71 @@ public static void UnFreeze(this CBasePlayerPawn pawn) { } public static void SetHealth(this CCSPlayerController player, int health) { + if (!player.IsValid) return; var pawn = player.PlayerPawn.Value; - if (pawn == null) return; + if (pawn == null || !pawn.IsValid) return; pawn.Health = health; Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth"); } public static void SetMaxHealth(this CCSPlayerController player, int health) { + if (!player.IsValid) return; var pawn = player.PlayerPawn.Value; - if (pawn == null) return; + if (pawn == null || !pawn.IsValid) return; pawn.MaxHealth = health; Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iMaxHealth"); } public static void SetArmor(this CCSPlayerController player, int armor) { + if (!player.IsValid) return; var pawn = player.PlayerPawn.Value; - if (pawn == null) return; + if (pawn == null || !pawn.IsValid) return; pawn.ArmorValue = armor; Utilities.SetStateChanged(pawn, "CCSPlayerPawn", "m_ArmorValue"); } public static void SetSpeed(this CCSPlayerController player, float speed) { + if (!player.IsValid) return; var pawn = player.PlayerPawn.Value; - if (pawn == null) return; + if (pawn == null || !pawn.IsValid) return; pawn.VelocityModifier = speed; } public static void SetGravity(this CCSPlayerController player, float gravity) { + if (!player.IsValid) return; var pawn = player.PlayerPawn.Value; - if (pawn == null) return; + if (pawn == null || !pawn.IsValid) return; pawn.GravityScale = gravity; } public static void SetColor(this CCSPlayerController player, Color color) { + if (!player.IsValid) return; var pawn = player.PlayerPawn.Value; - if (!player.IsReal() || pawn == null) return; + if (!player.IsValid || pawn == null || !pawn.IsValid) return; if (color.A == 255) color = Color.FromArgb(254, color.R, color.G, color.B); pawn.RenderMode = RenderMode_t.kRenderTransColor; pawn.Render = color; Utilities.SetStateChanged(pawn, "CBaseModelEntity", "m_clrRender"); } + + public static Color? GetColor(this CCSPlayerController player) { + if (!player.IsValid) return null; + var pawn = player.PlayerPawn.Value; + if (pawn == null || !pawn.IsValid) return null; + + return pawn.Render; + } + + public static CBasePlayerWeapon? GetWeaponBase( + this CCSPlayerController player, string designerName) { + if (!player.IsValid) return null; + var pawn = player.PlayerPawn.Value; + if (pawn == null || !pawn.IsValid) return null; + + return pawn.WeaponServices?.MyWeapons + .FirstOrDefault(w => w.Value?.DesignerName == designerName) + ?.Value; + } } \ No newline at end of file diff --git a/public/Jailbreak.Public/Extensions/WeaponExtensions.cs b/public/Jailbreak.Public/Extensions/WeaponExtensions.cs index 244049b7..4501fc5d 100644 --- a/public/Jailbreak.Public/Extensions/WeaponExtensions.cs +++ b/public/Jailbreak.Public/Extensions/WeaponExtensions.cs @@ -1,10 +1,14 @@ +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; namespace Jailbreak.Public.Extensions; public static class WeaponExtensions { public static string ToFriendlyString(this CCSWeaponBase weaponEntity) { - var designerName = weaponEntity.DesignerName; + return weaponEntity.DesignerName.GetFriendlyWeaponName(); + } + + public static string GetFriendlyWeaponName(this string designerName) { switch (designerName) { case "weapon_ak47": return "AK47"; @@ -126,4 +130,26 @@ public static string ToFriendlyString(this CCSWeaponBase weaponEntity) { return "UNKNOWN: Please Contact Tech"; } } + + public static void AddBulletsToMagazine(this CBasePlayerWeapon? weapon, + int bullets) { + if (weapon == null) return; + if (weapon.Clip1 + bullets > weapon.VData!.MaxClip1) { + var overflowBullets = weapon.Clip1 + bullets - weapon.VData!.MaxClip1; + weapon.Clip1 = weapon.VData!.MaxClip1; + weapon.ReserveAmmo[0] += overflowBullets; + } else { weapon.Clip1 += bullets; } + + Utilities.SetStateChanged(weapon, "CBasePlayerWeapon", "m_iClip1"); + Utilities.SetStateChanged(weapon, "CBasePlayerWeapon", "m_pReserveAmmo"); + } + + public static void SetAmmo(this CBasePlayerWeapon? weapon, int clip, + int reserve) { + if (weapon == null) return; + weapon.Clip1 = clip; + weapon.ReserveAmmo[0] = reserve; + Utilities.SetStateChanged(weapon, "CBasePlayerWeapon", "m_iClip1"); + Utilities.SetStateChanged(weapon, "CBasePlayerWeapon", "m_pReserveAmmo"); + } } \ No newline at end of file diff --git a/public/Jailbreak.Public/Jailbreak.Public.csproj b/public/Jailbreak.Public/Jailbreak.Public.csproj index 7351b823..029c9835 100644 --- a/public/Jailbreak.Public/Jailbreak.Public.csproj +++ b/public/Jailbreak.Public/Jailbreak.Public.csproj @@ -8,6 +8,7 @@ + diff --git a/public/Jailbreak.Public/Mod/Draw/BeamLine.cs b/public/Jailbreak.Public/Mod/Draw/BeamLine.cs index 843c40b1..43d1f7fe 100644 --- a/public/Jailbreak.Public/Mod/Draw/BeamLine.cs +++ b/public/Jailbreak.Public/Mod/Draw/BeamLine.cs @@ -2,6 +2,8 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.Public.Extensions; +using Vector = CounterStrikeSharp.API.Modules.Utils.Vector; namespace Jailbreak.Public.Mod.Draw; @@ -10,6 +12,7 @@ public class BeamLine(BasePlugin plugin, Vector position, Vector end) private CEnvBeam? beam; private Color color = Color.White; private float width = 1f; + public Vector End => end.Clone(); public void SetColor(Color _color) { color = _color; } @@ -29,9 +32,9 @@ public override void Draw() { newBeam.Render = GetColor(); newBeam.Teleport(Position, new QAngle(), new Vector()); - newBeam.EndPos.X = end.X; - newBeam.EndPos.Y = end.Y; - newBeam.EndPos.Z = end.Z; + newBeam.EndPos.X = End.X; + newBeam.EndPos.Y = End.Y; + newBeam.EndPos.Z = End.Z; beam = newBeam; Utilities.SetStateChanged(newBeam, "CBeam", "m_vecEndPos"); diff --git a/public/Jailbreak.Public/Mod/Draw/DrawableShape.cs b/public/Jailbreak.Public/Mod/Draw/DrawableShape.cs index c4e13e79..4a5793b1 100644 --- a/public/Jailbreak.Public/Mod/Draw/DrawableShape.cs +++ b/public/Jailbreak.Public/Mod/Draw/DrawableShape.cs @@ -1,6 +1,7 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Timers; using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.Public.Extensions; using Timer = CounterStrikeSharp.API.Modules.Timers.Timer; namespace Jailbreak.Public.Mod.Draw; @@ -13,12 +14,16 @@ protected Timer? KillTimer; // Internal timer used to remove the shape after a certain amount of time protected BasePlugin Plugin = plugin; - - protected Vector Position = position; // Represents the origin of the shape + private Vector position = position.Clone(); // Note that this can mean different things for different shapes protected DateTime StartTime = DateTime.Now; + public Vector Position { + get => position.Clone(); + protected set => position = value.Clone(); + } + public abstract void Draw(); public virtual void Update() { @@ -33,7 +38,7 @@ public void Draw(float lifetime) { KillTimer = Plugin.AddTimer(lifetime, Remove, TimerFlags.STOP_ON_MAPCHANGE); } - public virtual void Move(Vector position) { Position = position; } + public virtual void Move(Vector newPosition) { Position = newPosition; } public abstract void Remove(); } \ No newline at end of file diff --git a/public/Jailbreak.Public/Mod/Rebel/IJihadC4Service.cs b/public/Jailbreak.Public/Mod/Rebel/IC4Service.cs similarity index 96% rename from public/Jailbreak.Public/Mod/Rebel/IJihadC4Service.cs rename to public/Jailbreak.Public/Mod/Rebel/IC4Service.cs index 3168d3b3..497f0315 100644 --- a/public/Jailbreak.Public/Mod/Rebel/IJihadC4Service.cs +++ b/public/Jailbreak.Public/Mod/Rebel/IC4Service.cs @@ -2,7 +2,7 @@ namespace Jailbreak.Public.Mod.Rebel; -public interface IJihadC4Service { +public interface IC4Service { /// /// Tries to give the jihad C4 to the player, assuming the player object passed in is valid. /// diff --git a/public/Jailbreak.Public/Mod/SpecialDay/AbstractSpecialDay.cs b/public/Jailbreak.Public/Mod/SpecialDay/AbstractSpecialDay.cs index e26ec0ed..a428490a 100644 --- a/public/Jailbreak.Public/Mod/SpecialDay/AbstractSpecialDay.cs +++ b/public/Jailbreak.Public/Mod/SpecialDay/AbstractSpecialDay.cs @@ -1,6 +1,5 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; -using CounterStrikeSharp.API.Core.Attributes.Registration; using CounterStrikeSharp.API.Modules.Cvars; using CounterStrikeSharp.API.Modules.Timers; using CounterStrikeSharp.API.Modules.Utils; @@ -17,10 +16,11 @@ namespace Jailbreak.Public.Mod.SpecialDay; public abstract class AbstractSpecialDay(BasePlugin plugin, IServiceProvider provider) { + protected readonly BasePlugin Plugin = plugin; private readonly Dictionary previousConvarValues = new(); - protected BasePlugin Plugin = plugin; + protected readonly IServiceProvider Provider = provider; - protected IDictionary Timers = + protected readonly IDictionary Timers = new DefaultableDictionary(new Dictionary(), () => { }); @@ -32,6 +32,7 @@ public abstract class AbstractSpecialDay(BasePlugin plugin, /// Use for teleporting, stripping weapons, starting timers, etc. /// public virtual void Setup() { + Plugin.RegisterAllAttributes(this); Plugin.RegisterEventHandler(OnEnd); foreach (var entry in Settings.ConVarValues) { @@ -70,27 +71,27 @@ public virtual void Setup() { if (Settings.StartInvulnerable) DisableDamage(); if (!Settings.AllowLastRequests) - provider.GetRequiredService().DisableLRForRound(); + Provider.GetRequiredService().DisableLRForRound(); if (!Settings.AllowLastGuard) - provider.GetRequiredService() + Provider.GetRequiredService() .DisableLastGuardForRound(); if (!Settings.AllowRebels) - provider.GetRequiredService().DisableRebelForRound(); + Provider.GetRequiredService().DisableRebelForRound(); - if (Settings.OpenCells) MapUtil.OpenCells(); + if (Settings.OpenCells) + MapUtil.OpenCells(Provider.GetRequiredService()); doTeleports(); if (Settings.FreezePlayers) foreach (var player in Utilities.GetPlayers()) { player.Freeze(); - Plugin.AddTimer(Settings.FreezeTime(player), - () => { player.UnFreeze(); }); + Timers[Settings.FreezeTime(player)] += () => player.UnFreeze(); } foreach (var entry in Timers) Plugin.AddTimer(entry.Key, () => { - if (provider.GetRequiredService().CurrentSD != this) + if (Provider.GetRequiredService().CurrentSD != this) return; entry.Value.Invoke(); }, TimerFlags.STOP_ON_MAPCHANGE); @@ -121,30 +122,30 @@ private void doTeleports(SpecialDaySettings.TeleportType type, .FindAllEntitiesByDesignerName("info_player_counterterrorist") .Where(s => s.AbsOrigin != null) .Select(s => s.AbsOrigin!); + var enumerable = tSpawns as Vector[] ?? tSpawns.ToArray(); + enumerable.Shuffle(); + var positions = ctSpawns as Vector[] ?? ctSpawns.ToArray(); + positions.Shuffle(); + IEnumerable spawnPositions; switch (type) { case SpecialDaySettings.TeleportType.CELL: - spawnPositions = tSpawns; + spawnPositions = enumerable; break; case SpecialDaySettings.TeleportType.CELL_STACKED: - spawnPositions = [tSpawns.First()]; + spawnPositions = [enumerable.First()]; break; case SpecialDaySettings.TeleportType.ARMORY: - spawnPositions = ctSpawns; + spawnPositions = positions; break; case SpecialDaySettings.TeleportType.ARMORY_STACKED: - spawnPositions = [ctSpawns.First()]; + spawnPositions = [positions.First()]; break; case SpecialDaySettings.TeleportType.RANDOM: - spawnPositions = getRandomSpawns(false, false).ToList(); - // If we don't have enough manually specified spawns, - // gradually pull from the other spawn types - if (spawnPositions.Count() < PlayerUtil.GetAlive().Count()) { - spawnPositions = getRandomSpawns(true, false).ToList(); - if (spawnPositions.Count() < PlayerUtil.GetAlive().Count()) - spawnPositions = getRandomSpawns().ToList(); - } - + spawnPositions = getAtLeastRandom(PlayerUtil.GetAlive().Count()); + break; + case SpecialDaySettings.TeleportType.RANDOM_STACKED: + spawnPositions = [getAtLeastRandom(1).First()]; break; default: return; @@ -155,8 +156,17 @@ private void doTeleports(SpecialDaySettings.TeleportType type, player.PlayerPawn.Value?.Teleport(baggedSpawns.GetNext()); } - private IEnumerable getRandomSpawns(bool includeSpawns = true, - bool includeTps = true) { + private List getAtLeastRandom(int count) { + // Progressively get more lax with our "randomness quality" + var result = getRandomSpawns(false, false, false); + if (result.Count < count) result = getRandomSpawns(false, false); + if (result.Count < count) result = getRandomSpawns(false); + if (result.Count < count) result = getRandomSpawns(); + return result; + } + + private List getRandomSpawns(bool includeSpawns = true, + bool includeTps = true, bool includeAuto = true) { var result = new List(); if (includeTps) { @@ -182,10 +192,18 @@ private IEnumerable getRandomSpawns(bool includeSpawns = true, result.AddRange(ctSpawns); } - var zoneManager = provider.GetRequiredService(); + var zoneManager = Provider.GetRequiredService(); + if (includeAuto) + result.AddRange(zoneManager.GetZones(ZoneType.SPAWN_AUTO) + .GetAwaiter() + .GetResult() + .Select(z => z.GetCenterPoint())); + var zones = zoneManager.GetZones(ZoneType.SPAWN).GetAwaiter().GetResult(); result.AddRange(zones.Select(z => z.GetCenterPoint())); + result.Shuffle(); + return result; } @@ -248,6 +266,14 @@ protected void SetConvarValue(ConVar? cvar, object value) { } Server.ExecuteCommand(cvar.Name + " " + value); + if (cvar.Name == "mp_teammates_are_enemies") { + var opposite = !(bool)value; + Server.ExecuteCommand("css_cvar mp_teammates_are_enemies " + opposite); + + Server.NextFrame(() => { + Server.ExecuteCommand("css_cvar mp_teammates_are_enemies " + value); + }); + } } catch (Exception e) { Server.PrintToChatAll( $"There was an error setting {cvar.Name} ({cvar.Type}) to {value}"); @@ -265,7 +291,7 @@ public virtual void Execute() { Plugin.RegisterListener(OnTick); } - protected virtual void OnTick() { + virtual protected void OnTick() { foreach (var player in PlayerUtil.GetAlive()) { var weapons = Settings.AllowedWeapons(player); if (weapons == null) continue; @@ -287,20 +313,18 @@ private void disableWeapon(CCSPlayerController player, activeWeapon.NextPrimaryAttackTick = Server.TickCount + 500; } - [GameEventHandler] - public virtual HookResult OnEnd(EventRoundEnd @event, GameEventInfo info) { + virtual protected HookResult OnEnd(EventRoundEnd @event, GameEventInfo info) { foreach (var entry in previousConvarValues) { var cv = ConVar.Find(entry.Key); if (cv == null || entry.Value == null) continue; - try { SetConvarValue(cv, entry.Value); } catch (InvalidOperationException - e) { Console.WriteLine(e); } - - if (Settings.RestrictWeapons) - Plugin.RemoveListener(OnTick); + SetConvarValue(cv, entry.Value); } previousConvarValues.Clear(); + if (Settings.RestrictWeapons) + Plugin.RemoveListener(OnTick); + Plugin.DeregisterEventHandler(OnEnd); return HookResult.Continue; } @@ -314,10 +338,14 @@ protected void EnableDamage() { } protected void DisableDamage(CCSPlayerController player) { - if (player.Pawn.Value != null) player.Pawn.Value.TakesDamage = false; + if (!player.IsValid || player.Pawn.Value == null) return; + if (!player.Pawn.IsValid) return; + player.Pawn.Value.TakesDamage = false; } protected void EnableDamage(CCSPlayerController player) { - if (player.Pawn.Value != null) player.Pawn.Value.TakesDamage = true; + if (!player.IsValid || player.Pawn.Value == null) return; + if (!player.Pawn.IsValid) return; + player.Pawn.Value.TakesDamage = true; } } \ No newline at end of file diff --git a/public/Jailbreak.Public/Mod/SpecialDay/DefaultDictionary.cs b/public/Jailbreak.Public/Mod/SpecialDay/DefaultDictionary.cs index 489a86cd..a4bc1280 100644 --- a/public/Jailbreak.Public/Mod/SpecialDay/DefaultDictionary.cs +++ b/public/Jailbreak.Public/Mod/SpecialDay/DefaultDictionary.cs @@ -1,4 +1,5 @@ using System.Collections; +using JetBrains.Annotations; namespace Jailbreak.Public.Mod.SpecialDay; @@ -12,10 +13,12 @@ public DefaultableDictionary(IDictionary dictionary, this.defaultValue = defaultValue; } + [MustDisposeResource] public IEnumerator> GetEnumerator() { return dictionary.GetEnumerator(); } + [MustDisposeResource] IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Add(KeyValuePair item) { dictionary.Add(item); } diff --git a/public/Jailbreak.Public/Mod/SpecialDay/Enums/SDType.cs b/public/Jailbreak.Public/Mod/SpecialDay/Enums/SDType.cs index 496233a0..1ba16585 100644 --- a/public/Jailbreak.Public/Mod/SpecialDay/Enums/SDType.cs +++ b/public/Jailbreak.Public/Mod/SpecialDay/Enums/SDType.cs @@ -4,41 +4,35 @@ public enum SDType { CUSTOM, FFA, GUNGAME, + GHOST, HNS, + INFECTION, NOSCOPE, OITC, PACMAN, SNAKE, SPEEDRUN, TAG, - WARDAY, - INFECTION + TELEPORT, + WARDAY } public static class SDTypeExtensions { public static SDType? FromString(string type) { if (Enum.TryParse(type, true, out var result)) return result; type = type.ToLower().Replace(" ", ""); - switch (type) { - case "freeforall": - return SDType.FFA; - case "hide": - case "hideseek": - case "seek": - return SDType.HNS; - case "ns": - return SDType.NOSCOPE; - case "war": - return SDType.WARDAY; - case "tron": - return SDType.SNAKE; - case "gun": - return SDType.GUNGAME; - case "zomb": - case "zombie": - return SDType.INFECTION; - } - - return null; + return type switch { + "freeforall" => SDType.FFA, + "hide" or "hideseek" or "seek" => SDType.HNS, + "ns" => SDType.NOSCOPE, + "war" => SDType.WARDAY, + "tron" => SDType.SNAKE, + "gun" => SDType.GUNGAME, + "zomb" or "zombie" => SDType.INFECTION, + "speed" or "speedrun" or "speeders" or "speedrunners" or "race" => SDType + .SPEEDRUN, + "tp" => SDType.TELEPORT, + _ => null + }; } } \ No newline at end of file diff --git a/public/Jailbreak.Public/Mod/SpecialDay/ShuffleBag.cs b/public/Jailbreak.Public/Mod/SpecialDay/ShuffleBag.cs index 90144205..bc873ad5 100644 --- a/public/Jailbreak.Public/Mod/SpecialDay/ShuffleBag.cs +++ b/public/Jailbreak.Public/Mod/SpecialDay/ShuffleBag.cs @@ -23,7 +23,9 @@ public ShuffleBag(IEnumerable items) { public IEnumerator GetEnumerator() { while (true) yield return GetNext(); + // ReSharper disable IteratorNeverReturns } + // ReSharper restore IteratorNeverReturns IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } diff --git a/public/Jailbreak.Public/Mod/SpecialDay/SpecialDaySettings.cs b/public/Jailbreak.Public/Mod/SpecialDay/SpecialDaySettings.cs index ebc12c6d..fdfa72b8 100644 --- a/public/Jailbreak.Public/Mod/SpecialDay/SpecialDaySettings.cs +++ b/public/Jailbreak.Public/Mod/SpecialDay/SpecialDaySettings.cs @@ -33,7 +33,12 @@ public enum TeleportType { /// /// Teleport all players randomly across the map /// - RANDOM + RANDOM, + + /// + /// Pick a random teleport on the map and teleport all players to it + /// + RANDOM_STACKED } public bool AllowLastGuard = false; @@ -45,11 +50,11 @@ public enum TeleportType { public TeleportType CtTeleport = TeleportType.NONE; public bool FreezePlayers = true; - public bool RespawnPlayers = true; public bool OpenCells = true; + public bool RespawnPlayers = true; /// - /// Used to avoid registring a costly OnTick listener if false + /// Used to avoid registring a costly OnTick listener if false /// public bool RestrictWeapons = false; @@ -102,10 +107,10 @@ public virtual int InitialMaxHealth(CCSPlayerController player) { public virtual int InitialArmor(CCSPlayerController player) { return 0; } public SpecialDaySettings WithFriendlyFire() { - ConVarValues["mp_teammates_are_enemies"] = true; - ConVarValues["ff_damage_reduction_bullets"] = 1.0f; - ConVarValues["ff_damage_reduction_grenades"] = 1.0f; - ConVarValues["ff_damage_reduction_other"] = 1.0f; + ConVarValues["mp_teammates_are_enemies"] = true; + ConVarValues["ff_damage_reduction_bullets"] = 1.0f; + ConVarValues["ff_damage_reduction_grenade"] = 1.0f; + ConVarValues["ff_damage_reduction_other"] = 1.0f; return this; } diff --git a/public/Jailbreak.Public/Mod/Trail/AbstractTrail.cs b/public/Jailbreak.Public/Mod/Trail/AbstractTrail.cs new file mode 100644 index 00000000..9fddff66 --- /dev/null +++ b/public/Jailbreak.Public/Mod/Trail/AbstractTrail.cs @@ -0,0 +1,115 @@ +using System.Collections; +using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.Public.Extensions; +using JetBrains.Annotations; + +namespace Jailbreak.Public.Mod.Trail; + +public abstract class AbstractTrail(float lifetime = 20, int maxPoints = 100) + : IEnumerable where T : ITrailSegment { + // Ordered from newest to oldest (0 is the newest) + protected readonly IList Segments = new List(); + + public virtual float Lifetime { + get => lifetime; + set => lifetime = value; + } + + public virtual int MaxPoints { + get => maxPoints; + set => maxPoints = value; + } + + [MustDisposeResource] + public IEnumerator GetEnumerator() { return Segments.GetEnumerator(); } + + [MustDisposeResource] + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + + public virtual T? GetStartSegment() { return Segments.LastOrDefault(); } + public virtual T? GetEndSegment() { return Segments.FirstOrDefault(); } + + virtual protected void Cleanup() { + while (Segments.Count > MaxPoints) { + var seg = Segments[^1]; + seg.Remove(); + Segments.RemoveAt(Segments.Count - 1); + } + } + + public virtual IList GetTrail(float since, int max = 0) { + var result = new List(); + foreach (var segment in Segments) { + if (segment.GetTimeAlive() > since) break; + result.Add(segment); + if (max > 0 && result.Count >= max) break; + } + + return result; + } + + public virtual IList GetTrailPoints(float since, int max = 0) { + var result = new List(); + foreach (var segment in GetTrail(since, max)) { + result.Add(segment.GetStart()); + result.Add(segment.GetEnd()); + } + + return result; + } + + public virtual IList GetTrailPoints(int max) { + return GetTrailPoints(Lifetime, max); + } + + public IList GetTrailPoints() { return GetTrailPoints(Lifetime); } + + public virtual T? + GetNearestSegment(Vector position, float since, int max = 0) { + var nearest = default(T); + var minDistance = double.MaxValue; + foreach (var segment in GetTrail(since, max)) { + var distance = segment.GetDistanceSquared(position); + if (distance >= minDistance) continue; + minDistance = distance; + nearest = segment; + } + + return nearest; + } + + public virtual float GetTrailLength(float since, int max = 0) { + return MathF.Sqrt(GetTrailLengthSquared(since, max)); + } + + public float GetTrailLengthSquared(float since, int max = 0) { + var length = 0f; + var last = default(Vector); + foreach (var segment in GetTrail(since, max)) { + if (last != null) length += last.DistanceSquared(segment.GetStart()); + last = segment.GetEnd(); + } + + return length; + } + + public T? GetNearestSegment(Vector position) { + return GetNearestSegment(position, Lifetime); + } + + public virtual void AddTrailPoint(Vector vector) { + var start = GetEndSegment()?.GetEnd() ?? vector; + var end = vector; + var seg = CreateSegment(start, end); + Segments.Insert(0, seg); + Cleanup(); + } + + public abstract T CreateSegment(Vector start, Vector end); + + public virtual void Kill() { + foreach (var segment in Segments) segment.Remove(); + + Segments.Clear(); + } +} \ No newline at end of file diff --git a/public/Jailbreak.Public/Mod/Trail/ITrailManager.cs b/public/Jailbreak.Public/Mod/Trail/ITrailManager.cs new file mode 100644 index 00000000..4c821b70 --- /dev/null +++ b/public/Jailbreak.Public/Mod/Trail/ITrailManager.cs @@ -0,0 +1,11 @@ +using CounterStrikeSharp.API.Core; +using Jailbreak.Public.Behaviors; + +namespace Jailbreak.Public.Mod.Trail; + +public interface ITrailManager : IPluginBehavior where T : ITrailSegment { + bool AddPlayerTrail(CCSPlayerController player); + bool HasPlayerTrail(CCSPlayerController player); + bool RemovePlayerTrail(CCSPlayerController player); + T? GetPlayerTrail(CCSPlayerController player); +} \ No newline at end of file diff --git a/public/Jailbreak.Public/Mod/Trail/ITrailSegment.cs b/public/Jailbreak.Public/Mod/Trail/ITrailSegment.cs new file mode 100644 index 00000000..d6070152 --- /dev/null +++ b/public/Jailbreak.Public/Mod/Trail/ITrailSegment.cs @@ -0,0 +1,24 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Modules.Utils; +using Jailbreak.Public.Mod.Zones; + +namespace Jailbreak.Public.Mod.Trail; + +public interface ITrailSegment { + float GetTimeAlive() { return Server.CurrentTime - GetSpawnTime(); } + float GetSpawnTime(); + + Vector GetStart(); + Vector GetEnd(); + + float GetDistanceSquared(Vector position) { + return ConvexHullUtil.DistanceToSegmentSquared(position, GetStart(), + GetEnd()); + } + + float GetDistance(Vector position) { + return MathF.Sqrt(GetDistanceSquared(position)); + } + + void Remove(); +} \ No newline at end of file diff --git a/public/Jailbreak.Public/Mod/Zones/ConvexHullUtil.cs b/public/Jailbreak.Public/Mod/Zones/ConvexHullUtil.cs index 62f61f21..4455bbc8 100644 --- a/public/Jailbreak.Public/Mod/Zones/ConvexHullUtil.cs +++ b/public/Jailbreak.Public/Mod/Zones/ConvexHullUtil.cs @@ -1,7 +1,7 @@ using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Public.Extensions; -namespace Jailbreak.Zones; +namespace Jailbreak.Public.Mod.Zones; public static class ConvexHullUtil { // private readonly IList hull; @@ -25,7 +25,8 @@ public static bool IsInsideZone(Vector point, IList hull) { return inside; } - public static float GetMinDistance(Vector position, IList hull) { + public static float + GetMinDistanceSquared(Vector position, IList hull) { if (IsInsideZone(position, hull)) return 0; var minDistance = float.MaxValue; for (var i = 0; i < hull.Count; i++) { @@ -38,6 +39,10 @@ public static float GetMinDistance(Vector position, IList hull) { return minDistance; } + public static float GetMinDistance(Vector position, IList hull) { + return (float)Math.Sqrt(GetMinDistanceSquared(position, hull)); + } + public static IEnumerable ComputeConvexHull(IList points) { if (points.Count < 3) return points; points = points.OrderBy(p => p.X).ThenBy(p => p.Y).ToList(); diff --git a/public/Jailbreak.Public/Mod/Zones/IZone.cs b/public/Jailbreak.Public/Mod/Zones/IZone.cs index b3d53c08..938071ad 100644 --- a/public/Jailbreak.Public/Mod/Zones/IZone.cs +++ b/public/Jailbreak.Public/Mod/Zones/IZone.cs @@ -8,7 +8,7 @@ namespace Jailbreak.Public.Mod.Zones; public interface IZone { public int Id { get; set; } bool IsInsideZone(Vector position); - float GetMinDistance(Vector position); + float GetMinDistanceSquared(Vector position); /// /// Represents a valid center point that players are able to spawn into diff --git a/public/Jailbreak.Public/Mod/Zones/IZoneManager.cs b/public/Jailbreak.Public/Mod/Zones/IZoneManager.cs index 166d4d0c..f18b4666 100644 --- a/public/Jailbreak.Public/Mod/Zones/IZoneManager.cs +++ b/public/Jailbreak.Public/Mod/Zones/IZoneManager.cs @@ -23,5 +23,5 @@ Task PushZone(IZone zone, ZoneType type) { Task UpdateZone(IZone zone, ZoneType type, int id); - Task>> GetAllZones(); + Task>> GetAllZones(); } \ No newline at end of file diff --git a/public/Jailbreak.Public/Mod/Zones/MovementRestrictor.cs b/public/Jailbreak.Public/Mod/Zones/MovementRestrictor.cs index ebb0f75a..30cfbe6a 100644 --- a/public/Jailbreak.Public/Mod/Zones/MovementRestrictor.cs +++ b/public/Jailbreak.Public/Mod/Zones/MovementRestrictor.cs @@ -59,7 +59,6 @@ private void tick() { } onTeleport?.Invoke(); - if (lastValid == null) return; player.PlayerPawn.Value?.Teleport(lastValid ?? GetCenter()); } diff --git a/public/Jailbreak.Public/Mod/Zones/ZoneType.cs b/public/Jailbreak.Public/Mod/Zones/ZoneType.cs index 3a36a293..da48b01e 100644 --- a/public/Jailbreak.Public/Mod/Zones/ZoneType.cs +++ b/public/Jailbreak.Public/Mod/Zones/ZoneType.cs @@ -13,6 +13,11 @@ public enum ZoneType { /// CELL, + /// + /// This zone is a single point, and represents the cell button + /// + CELL_BUTTON, + /// /// This zone captures a secret area that Ts may try to access /// @@ -39,6 +44,12 @@ public enum ZoneType { /// SPAWN, + /// + /// Similar to SPAWN, but this spawn location was automatically + /// generated + /// + SPAWN_AUTO, + /// /// This zone represents an area on the map that only Ts can access /// @@ -50,7 +61,8 @@ public enum ZoneType { ZONE_LIMIT_CT, /// - /// The center of the map (both vertically and horizontally) + /// This zone is a single point, and represents + /// the center of the map (both vertically and horizontally) /// CENTER } @@ -67,11 +79,19 @@ public static Color GetColor(this ZoneType type) { ZoneType.SPAWN => Color.White, ZoneType.ZONE_LIMIT_T => Color.OrangeRed, ZoneType.ZONE_LIMIT_CT => Color.LightBlue, - _ => Color.Black + ZoneType.SPAWN_AUTO => Color.Gray, + ZoneType.CELL_BUTTON => Color.DarkRed, + _ => Color.Magenta }; } public static bool IsSinglePoint(this ZoneType type) { - return type == ZoneType.SPAWN; + return type switch { + ZoneType.CELL_BUTTON => true, + ZoneType.SPAWN => true, + ZoneType.CENTER => true, + ZoneType.SPAWN_AUTO => true, + _ => false + }; } } \ No newline at end of file diff --git a/public/Jailbreak.Public/Utils/MapUtil.cs b/public/Jailbreak.Public/Utils/MapUtil.cs index 78314103..9f2bea35 100644 --- a/public/Jailbreak.Public/Utils/MapUtil.cs +++ b/public/Jailbreak.Public/Utils/MapUtil.cs @@ -3,6 +3,7 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using Jailbreak.Public.Extensions; +using Jailbreak.Public.Mod.Zones; namespace Jailbreak.Public.Utils; @@ -17,8 +18,22 @@ private static Vector getCtSpawn() { .First(); } - public static void OpenCells( - Sensitivity sensitivity = Sensitivity.NAME_CELL_DOOR) { + public static bool OpenCells(IZoneManager zoneManager) { + var zones = zoneManager.GetZones(ZoneType.CELL_BUTTON) + .GetAwaiter() + .GetResult(); + + + if (zones == null || zones.Count == 0) + return OpenCells() <= Sensitivity.TARGET_CELL; + + return OpenCells(Sensitivity.ANY, zones.First().GetCenterPoint()) != null; + } + + public static Sensitivity? OpenCells( + Sensitivity sensitivity = Sensitivity.NAME_CELL_DOOR, + Vector? source = null) { + if (source == null) source = getCtSpawn(); var allButtons = Utilities .FindAllEntitiesByDesignerName("func_button") .ToHashSet(); @@ -37,38 +52,40 @@ public static void OpenCells( foreach (var button in allButtons .Select(cell => entityCache[(int)cell.Index]) .Where(button => button != null && button.IsValid) - .Where(button => IsCellButton(button, sensitivity))) { + .Where(button => IsCellButton(button, sensitivity))) entities.Add(button); - } switch (entities.Count) { case 1: PressButton(entities[0]); - return; + return sensitivity; case 0: { var lower = sensitivity.GetLower(); - if (lower == null) return; + if (lower == null) return lower; sensitivity = lower.Value; continue; } } - var ctSpawn = getCtSpawn(); - var sorted = entities.ToList(); + var sorted = entities.ToList(); sorted.Sort((a, b) => { - var aDist = a.AbsOrigin!.DistanceSquared(ctSpawn); - var bDist = b.AbsOrigin!.DistanceSquared(ctSpawn); + var aDist = a.AbsOrigin!.DistanceSquared(source); + var bDist = b.AbsOrigin!.DistanceSquared(source); return aDist.CompareTo(bDist); }); PressButton(sorted[0]); break; } + + return sensitivity; } private static void PressButton(CBaseEntity entity) { - entity.AcceptInput("Unlock"); - entity.AcceptInput("Press"); + entity.AcceptInput("Unlock", + PlayerUtil.FromTeam(CsTeam.CounterTerrorist).FirstOrDefault()); + entity.AcceptInput("Press", + PlayerUtil.FromTeam(CsTeam.CounterTerrorist).FirstOrDefault()); } private static bool IsCellButton(CBaseEntity ent, Sensitivity sen) { diff --git a/public/Jailbreak.Public/Utils/PlayerUtil.cs b/public/Jailbreak.Public/Utils/PlayerUtil.cs index 8218e5df..87f4c2ba 100644 --- a/public/Jailbreak.Public/Utils/PlayerUtil.cs +++ b/public/Jailbreak.Public/Utils/PlayerUtil.cs @@ -13,8 +13,10 @@ public static IEnumerable FromTeam(CsTeam team, public static CCSPlayerController? GetRandomFromTeam(CsTeam team, bool alive = true) { - return Utilities.GetPlayers() - .FirstOrDefault(p => p.Team == team && (!alive || p.PawnIsAlive)); + var players = FromTeam(team, alive).Where(p => !p.IsBot).ToList(); + return players.Count == 0 ? + null : + players[new Random().Next(players.Count)]; } public static IEnumerable GetAlive() { diff --git a/public/Jailbreak.Tag/Tag.cs b/public/Jailbreak.Tag/Tag.cs index 839c2af4..25254d19 100644 --- a/public/Jailbreak.Tag/Tag.cs +++ b/public/Jailbreak.Tag/Tag.cs @@ -2,7 +2,7 @@ public static class Tag { /// - /// Items that are thrown and exist in the grenade slot + /// Items that are thrown and exist in the grenade slot /// public static readonly IReadOnlySet GRENADES = new HashSet([ "weapon_decoy", "weapon_firebomb", "weapon_flashbang", "weapon_hegrenade", @@ -11,7 +11,7 @@ public static class Tag { ]); /// - /// Items that do not shoot bullets + /// Items that do not shoot bullets /// public static readonly IReadOnlySet UTILITY = new HashSet([ "weapon_healthshot", "item_assaultsuit", "item_kevlar", diff --git a/src/Jailbreak/Jailbreak.cs b/src/Jailbreak/Jailbreak.cs index 1a032e68..2324060b 100644 --- a/src/Jailbreak/Jailbreak.cs +++ b/src/Jailbreak/Jailbreak.cs @@ -56,7 +56,7 @@ public override void Load(bool hotReload) { RegisterAllAttributes(extension); // Tell the extension to start it's magic - extension.Start(this); + extension.Start(this, hotReload); Logger.LogInformation("[Jailbreak] Loaded behavior {@Behavior}", extension.GetType().FullName); diff --git a/src/Jailbreak/JailbreakServiceCollection.cs b/src/Jailbreak/JailbreakServiceCollection.cs index 127905f4..38cf874f 100644 --- a/src/Jailbreak/JailbreakServiceCollection.cs +++ b/src/Jailbreak/JailbreakServiceCollection.cs @@ -9,7 +9,9 @@ using Jailbreak.English.Rebel; using Jailbreak.English.SpecialDay; using Jailbreak.English.Warden; -using Jailbreak.Formatting.Logistics; +using Jailbreak.Formatting.Views; +using Jailbreak.Formatting.Views.LastRequest; +using Jailbreak.Formatting.Views.Warden; using Jailbreak.Generic; using Jailbreak.LastGuard; using Jailbreak.LastRequest; @@ -30,6 +32,23 @@ namespace Jailbreak; public class JailbreakServiceCollection : IPluginServiceCollection { /// public void ConfigureServices(IServiceCollection serviceCollection) { + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + // Do we want to make this scoped? // Not sure how this will behave with multiple rounds and whatnot. serviceCollection.AddTransient(); @@ -43,21 +62,5 @@ public void ConfigureServices(IServiceCollection serviceCollection) { serviceCollection.AddJailbreakLastGuard(); serviceCollection.AddJailbreakSpecialDay(); serviceCollection.AddJailbreakZones(); - - // Add in english localization - serviceCollection.AddLanguage(config => { - config.WithGenericCommand(); - config.WithWarden(); - config.WithRebel(); - config.WithLogging(); - config.WithRollCommand(); - config.WithJihadC4(); - config.WithLastRequest(); - config.WithSpecialTreatment(); - config.WithMute(); - config.WithRaceLR(); - config.WithLastGuard(); - config.WithSpecialDay(); - }); } } \ No newline at end of file