mirror of
https://github.com/edgegamers/Jailbreak.git
synced 2025-12-05 20:40:29 -08:00
Cleaup/aug (#290)
* Reformat * Add cvar config for HNS * Add more HNS customization support * Add customizations to noscope * Remove unused class var * More tidying up * Remove config infra * Add configurability to C4 * Add contributing, add config explanation in readme
This commit is contained in:
20
CONTRIBUTING.md
Normal file
20
CONTRIBUTING.md
Normal file
@@ -0,0 +1,20 @@
|
||||
## Contributing
|
||||
|
||||
The jail plugin is currently in heavy development and all contributions are welcome!
|
||||
Please make sure all contributions use the dependency injection system, or ask to have your contribution
|
||||
ported if you don't know how.
|
||||
|
||||
> [!TIP]
|
||||
> Microsoft has some good documentation on dependency injection here:
|
||||
> [Overview](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection),
|
||||
> [Using Dependency Injection](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-usage),
|
||||
> [Dependency Injection Guidelines](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines).
|
||||
|
||||
All event handlers should derive from `IPluginBehavior` and be registered using
|
||||
`IServiceCollection.AddPluginBehavior<T>`. If your behavior also acts as a service,
|
||||
make sure to use `IServiceCollection.AddPluginBehavior<TInterface, T>`. All `IPluginBehavior` objects
|
||||
have their event handlers automatically registered.
|
||||
|
||||
Code style should follow .NET conventions and use the formatting settings specified
|
||||
in [Jailbreak.sln.DotSettings](./Jailbreak.sln.DotSettings)
|
||||
(if you need help, make sure to check "enable edits from maintainers" and ask for a format)
|
||||
45
README.md
45
README.md
@@ -9,17 +9,20 @@ The classic Jail gamemode, ported to Counter-Strike 2.
|
||||
|
||||
[](https://github.com/edgegamers/Jailbreak/releases/)⠀⠀
|
||||
[](https://nightly.link/edgegamers/Jailbreak/workflows/nightly/main/jailbreak-nightly)⠀⠀
|
||||
[](https://nightly.link/edgegamers/Jailbreak/workflows/nightly/main/jailbreak-nightly)
|
||||
⠀⠀
|
||||
[](https://nightly.link/edgegamers/Jailbreak/workflows/nightly/dev/jailbreak-nightly)
|
||||
|
||||
**Release** builds are our full releases. We try to keep these high-quality and bug-free, when we can.
|
||||
Our **Stable** builds run on EdgeGamers' own Jailbreak servers.
|
||||
Our **Stable** builds run on EdgeGamers' own Jailbreak servers.
|
||||
Our **Nightly** builds are used exclusively for development and staging, and are likely to have problems.
|
||||
|
||||
## Versioning
|
||||
|
||||
Our release tags starting from 'v2.0.0' follow the [Semantic Versioning 2.0.0](https://semver.org/) standard,
|
||||
where `MAJOR.MINOR.PATCH` are incremented based on the following:
|
||||
|
||||
- `MAJOR` when we make incompatible API changes,
|
||||
- `MINOR` when we add functionality in a backwards-compatible manner.
|
||||
- `PATCH` when we make backwards-compatible bug fixes.
|
||||
@@ -27,46 +30,29 @@ where `MAJOR.MINOR.PATCH` are incremented based on the following:
|
||||
## Status
|
||||
|
||||
- **⚙️ Server**
|
||||
- [ ] Stats/Analytics Sinks
|
||||
- [ ] Error reporting
|
||||
- [x] Configuration system
|
||||
- Note: Passable, but in a terrible state. Needs TLC.
|
||||
- [x] Stats/Analytics Sinks
|
||||
- [x] Error reporting
|
||||
- [x] Logging
|
||||
- [x] Zones
|
||||
- **👮 Guards**
|
||||
- [x] Warden Selection
|
||||
- [x] Warden Laser and Paint
|
||||
- [ ] Special Days
|
||||
- [x] Ratio Enforcement
|
||||
- [ ] Bans/Punishments
|
||||
- [x] Special Days
|
||||
- **🎃 Prisoners**
|
||||
- [x] Last Request
|
||||
- [x] Rebel System
|
||||
- **🛕 Maps**
|
||||
- [x] Automagic Cell Opening
|
||||
- [ ] Custom Entities
|
||||
- [ ] Custom I/O
|
||||
- [ ] Warden/Guard/Prisoner Filters
|
||||
|
||||
## Contributing
|
||||
## Configuration
|
||||
|
||||
The jail plugin is currently in heavy development and all contributions are welcome!
|
||||
Please make sure all contributions use the dependency injection system, or ask to have your contribution
|
||||
ported if you don't know how.
|
||||
Configuration is done through CS#'s [FakeConVars](https://docs.cssharp.dev/examples/WithFakeConvars.html?q=fakeconvar).
|
||||
|
||||
Ports to DI containers that have more verbose scoping systems for round-based or game-based scoping are welcome.
|
||||
|
||||
> [!TIP]
|
||||
> Microsoft has some good documentation on dependency injection here:
|
||||
> [Overview](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection),
|
||||
> [Using Dependency Injection](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-usage),
|
||||
> [Dependency Injection Guidelines](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines).
|
||||
|
||||
All event handlers should derive from `IPluginBehavior` and be registered using
|
||||
`IServiceCollection.AddPluginBehavior<T>`. If your behavior also acts as a service,
|
||||
make sure to use `IServiceCollection.AddPluginBehavior<TInterface, T>`. All `IPluginBehavior` objects
|
||||
have their event handlers automatically registered.
|
||||
|
||||
Code style should follow .NET conventions
|
||||
(if you need help, make sure to check "enable edits from maintainers" and ask for a format)
|
||||
You can search for the list of configurable
|
||||
convars [like so](https://github.com/search?q=repo%3Aedgegamers%2FJailbreak%20fakeconvar&type=code).
|
||||
|
||||
## Modding
|
||||
|
||||
@@ -97,7 +83,8 @@ foreach (IPluginBehavior extension in _extensions)
|
||||
|
||||
## Building
|
||||
|
||||
The jailbreak plugin automatically builds to `build/Jailbreak` when using `dotnet publish src/Jailbreak/Jailbreak.csproj`.
|
||||
The jailbreak plugin automatically builds to `build/Jailbreak` when
|
||||
using `dotnet publish src/Jailbreak/Jailbreak.csproj`.
|
||||
Please use [SDK 8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) or higher.
|
||||
|
||||
Note that only the `src/Jailbreak` project is intended to be built directly.
|
||||
|
||||
@@ -4,6 +4,7 @@ using Jailbreak.Formatting.Core;
|
||||
using Jailbreak.Formatting.Logistics;
|
||||
using Jailbreak.Formatting.Objects;
|
||||
using Jailbreak.Formatting.Views;
|
||||
using Jailbreak.Public.Extensions;
|
||||
|
||||
namespace Jailbreak.English.Generic;
|
||||
|
||||
@@ -36,21 +37,23 @@ public class GenericCmdLocale : IGenericCmdLocale,
|
||||
PREFIX,
|
||||
$"{ChatColors.Grey}Command is on cooldown for",
|
||||
seconds,
|
||||
$"{ChatColors.Grey}seconds!"
|
||||
$"{ChatColors.Grey}second" + (seconds == 1 ? "" : "s") + "."
|
||||
};
|
||||
}
|
||||
|
||||
public IView InvalidParameter(string parameter, string expected) {
|
||||
return new SimpleView {
|
||||
PREFIX,
|
||||
$"{ChatColors.Red}Invalid parameter '{ChatColors.LightBlue}{parameter}{ChatColors.Red}', expected a(n) {ChatColors.White}{expected}{ChatColors.Red}."
|
||||
$"{ChatColors.Red}Invalid parameter '{ChatColors.LightBlue}{parameter}{ChatColors.Red}',",
|
||||
"expected a" + (expected[0].IsVowel() ? "n" : ""),
|
||||
$"{ChatColors.White}{expected}{ChatColors.Red}."
|
||||
};
|
||||
}
|
||||
|
||||
public IView NoPermissionMessage(string permission) {
|
||||
return new SimpleView {
|
||||
PREFIX,
|
||||
$"{ChatColors.Red}This command requires the {ChatColors.White}{permission}{ChatColors.Red} permission."
|
||||
$"{ChatColors.DarkRed}This requires the {ChatColors.White}{permission}{ChatColors.Red} permission."
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,9 @@ public class CoinflipLocale : LastRequestLocale, ILRCFLocale {
|
||||
|
||||
public IView CoinLandsOn(bool heads) {
|
||||
return new SimpleView {
|
||||
PREFIX, "The coin lands on" + ChatColors.Green, heads ? "Heads" : "Tails"
|
||||
PREFIX,
|
||||
"The coin landed on" + ChatColors.Green,
|
||||
heads ? "Heads" : "Tails" + ChatColors.White + "."
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ public class LastRequestLocale : ILRLocale,
|
||||
ILanguage<Formatting.Languages.English> {
|
||||
public static readonly FormatObject PREFIX =
|
||||
new HiddenFormatObject(
|
||||
$" {ChatColors.DarkRed}[{ChatColors.LightRed}LR{ChatColors.DarkRed}]") {
|
||||
$" {ChatColors.Green}[{ChatColors.Lime}LR{ChatColors.Green}]") {
|
||||
// Hide in panorama and center text
|
||||
Plain = false, Panorama = false, Chat = true
|
||||
};
|
||||
@@ -24,7 +24,7 @@ public class LastRequestLocale : ILRLocale,
|
||||
return new SimpleView {
|
||||
{
|
||||
PREFIX,
|
||||
$"Last Request has been enabled. {ChatColors.Grey}Type {ChatColors.LightBlue}!lr{ChatColors.Grey} to start a last request."
|
||||
$"Last Request activated. {ChatColors.Grey}Type {ChatColors.LightBlue}!lr{ChatColors.Grey} to start a last request."
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -33,7 +33,7 @@ public class LastRequestLocale : ILRLocale,
|
||||
return new SimpleView {
|
||||
{
|
||||
PREFIX,
|
||||
$"{ChatColors.Grey}Last Request has been {ChatColors.Red}disabled{ChatColors.Grey}."
|
||||
$"{ChatColors.Grey}Last Request {ChatColors.Red}disabled{ChatColors.Grey}."
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -52,9 +52,9 @@ public class LastRequestLocale : ILRLocale,
|
||||
return new SimpleView {
|
||||
PREFIX,
|
||||
lr.Prisoner,
|
||||
ChatColors.Grey + "is preparing a",
|
||||
ChatColors.Grey + "is starting a",
|
||||
ChatColors.White + lr.Type.ToFriendlyString(),
|
||||
ChatColors.Grey + "Last Request against",
|
||||
ChatColors.Grey + "LR against",
|
||||
lr.Guard
|
||||
};
|
||||
}
|
||||
@@ -74,36 +74,29 @@ public class LastRequestLocale : ILRLocale,
|
||||
var tNull = !lr.Prisoner.IsReal();
|
||||
var gNull = !lr.Guard.IsReal();
|
||||
if (tNull && gNull)
|
||||
return new SimpleView { PREFIX, "Last Request has been decided." };
|
||||
return new SimpleView { PREFIX, "Last Request decided." };
|
||||
|
||||
if (tNull && result == LRResult.PRISONER_WIN)
|
||||
return new SimpleView {
|
||||
PREFIX, lr.Guard, "lost the LR, but the prisoner left the game."
|
||||
PREFIX, lr.Guard, "lost the LR, but the prisoner left the game?"
|
||||
};
|
||||
|
||||
if (gNull && result == LRResult.GUARD_WIN)
|
||||
return new SimpleView {
|
||||
PREFIX, lr.Prisoner, "lost the LR, but the guard left the game."
|
||||
PREFIX, lr.Prisoner, "lost the LR, but the guard left the game?"
|
||||
};
|
||||
|
||||
switch (result) {
|
||||
case LRResult.TIMED_OUT:
|
||||
return new SimpleView {
|
||||
PREFIX, ChatColors.Grey.ToString(), "Last Request has timed out."
|
||||
};
|
||||
case LRResult.INTERRUPTED:
|
||||
return new SimpleView {
|
||||
PREFIX,
|
||||
ChatColors.Grey.ToString(),
|
||||
"Last Request has been interrupted."
|
||||
};
|
||||
default:
|
||||
return new SimpleView {
|
||||
PREFIX,
|
||||
result == LRResult.PRISONER_WIN ? lr.Prisoner : lr.Guard,
|
||||
"won the LR."
|
||||
};
|
||||
}
|
||||
return result switch {
|
||||
LRResult.TIMED_OUT => new SimpleView {
|
||||
PREFIX, ChatColors.Grey.ToString(), "Last Request timed out."
|
||||
},
|
||||
LRResult.INTERRUPTED => new SimpleView {
|
||||
PREFIX, ChatColors.Grey.ToString(), "Last Request interrupted."
|
||||
},
|
||||
_ => new SimpleView {
|
||||
PREFIX, result == LRResult.PRISONER_WIN ? lr.Prisoner : lr.Guard, "won."
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public IView CannotLR(string reason) {
|
||||
@@ -149,14 +142,4 @@ public class LastRequestLocale : ILRLocale,
|
||||
=> 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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ public class RPSLocale : LastRequestLocale, ILRRPSLocale {
|
||||
|
||||
public IView BothPlayersMadeChoice() {
|
||||
return new SimpleView {
|
||||
PREFIX, "Both players have rocked, papered, and scissored! (ew)"
|
||||
PREFIX, "Both players rocked, papered, and scissored! (ew)"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ public class RPSLocale : LastRequestLocale, ILRRPSLocale {
|
||||
int guardPick, int prisonerPick) {
|
||||
return new SimpleView {
|
||||
PREFIX,
|
||||
"Results: ",
|
||||
"Results:",
|
||||
guard,
|
||||
" picked ",
|
||||
"picked",
|
||||
toRPS(guardPick),
|
||||
" and ",
|
||||
"and",
|
||||
prisoner,
|
||||
" picked ",
|
||||
"picked",
|
||||
toRPS(prisonerPick)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,15 +20,14 @@ public class RaceLocale : LastRequestLocale, ILRRaceLocale {
|
||||
SimpleView.NEWLINE, {
|
||||
PREFIX,
|
||||
$"Type {ChatColors.Blue}!endrace{ChatColors.White} to set the end point!"
|
||||
},
|
||||
SimpleView.NEWLINE
|
||||
}
|
||||
};
|
||||
|
||||
public IView RaceStartingMessage(CCSPlayerController prisoner) {
|
||||
return new SimpleView {
|
||||
{
|
||||
PREFIX, prisoner,
|
||||
" is starting a race. Pay attention to where they set the end point!"
|
||||
"is racing you. Pay attention to where they set the end point!"
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -37,14 +36,17 @@ public class RaceLocale : LastRequestLocale, ILRRaceLocale {
|
||||
return new SimpleView {
|
||||
{
|
||||
PREFIX,
|
||||
$"You must be in a race {ChatColors.Blue + "!lr" + ChatColors.White} to use this command"
|
||||
$"{ChatColors.Red}You must be in a race {ChatColors.Blue + "!lr" + ChatColors.Red} to use this."
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public IView NotInPendingState() {
|
||||
return new SimpleView {
|
||||
{ PREFIX, "You must be in the pending state to use this command." }
|
||||
{
|
||||
PREFIX,
|
||||
ChatColors.Red + "You must be in the pending state to use this command."
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using Jailbreak.Formatting.Core;
|
||||
using Jailbreak.Formatting.Logistics;
|
||||
using Jailbreak.Formatting.Objects;
|
||||
using Jailbreak.Formatting.Views.Warden;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Jailbreak.English.Mute;
|
||||
|
||||
@@ -17,32 +18,40 @@ public class WardenPeaceLocale : IWardenPeaceLocale,
|
||||
|
||||
public IView PeaceEnactedByAdmin(int seconds) {
|
||||
return new SimpleView {
|
||||
PREFIX, "An admin enacted peace for", seconds, "seconds."
|
||||
PREFIX,
|
||||
$"{ChatColors.Red}An admin {ChatColors.White}enacted peace for",
|
||||
seconds,
|
||||
"second" + (seconds == 1 ? "" : "s") + "."
|
||||
};
|
||||
}
|
||||
|
||||
public IView WardenEnactedPeace(int seconds) {
|
||||
return new SimpleView {
|
||||
PREFIX, "Warden enacted peace for", seconds, "seconds."
|
||||
PREFIX,
|
||||
$"{ChatColors.Blue}The warden {ChatColors.White}enacted peace for",
|
||||
seconds,
|
||||
"seconds."
|
||||
};
|
||||
}
|
||||
|
||||
public IView GeneralPeaceEnacted(int seconds) {
|
||||
return new SimpleView {
|
||||
PREFIX, "Peace has been enacted for", seconds, "seconds."
|
||||
PREFIX,
|
||||
"Peace was enacted for",
|
||||
seconds,
|
||||
"second" + (seconds == 1 ? "" : "s") + "."
|
||||
};
|
||||
}
|
||||
|
||||
public IView UnmutedGuards
|
||||
=> new SimpleView {
|
||||
{ PREFIX, $"{ChatColors.Blue}Guards {ChatColors.Grey}have been unmuted." }
|
||||
{ PREFIX, $"{ChatColors.Blue}Guards {ChatColors.Grey}were unmuted." }
|
||||
};
|
||||
|
||||
public IView UnmutedPrisoners
|
||||
=> new SimpleView {
|
||||
{
|
||||
PREFIX,
|
||||
$"{ChatColors.LightRed}Prisoners {ChatColors.Grey}have been unmuted."
|
||||
PREFIX, $"{ChatColors.LightRed}Prisoners {ChatColors.Grey}were unmuted."
|
||||
}
|
||||
};
|
||||
|
||||
@@ -61,7 +70,7 @@ public class WardenPeaceLocale : IWardenPeaceLocale,
|
||||
|
||||
public IView DeadReminder
|
||||
=> new SimpleView {
|
||||
{ PREFIX, $"{ChatColors.Red}You are dead and cannot speak!" }
|
||||
{ PREFIX, $"{ChatColors.Red}You are dead and cannot speak." }
|
||||
};
|
||||
|
||||
public IView AdminDeadReminder
|
||||
|
||||
@@ -17,5 +17,5 @@ public class RebelLocale : IRebelLocale,
|
||||
};
|
||||
|
||||
public IView NoLongerRebel
|
||||
=> new SimpleView { PREFIX, "You are no longer a rebel." };
|
||||
=> new SimpleView { PREFIX, "You are no longer red." };
|
||||
}
|
||||
@@ -19,15 +19,12 @@ public class GunDayLocale() : SoloDayLocale("Gun Game",
|
||||
public IView PromotedTo(string weapon, int weaponsLeft) {
|
||||
if (weaponsLeft == 1)
|
||||
return new SimpleView {
|
||||
PREFIX,
|
||||
"You were promoted to",
|
||||
weapon + ".",
|
||||
ChatColors.Green + "LAST WEAPON!"
|
||||
PREFIX, "Promoted to", weapon + ".", ChatColors.Green + "LAST WEAPON!"
|
||||
};
|
||||
|
||||
return new SimpleView {
|
||||
PREFIX,
|
||||
"You were promoted to",
|
||||
"Promoted to",
|
||||
weapon + ".",
|
||||
weaponsLeft,
|
||||
"weapons left."
|
||||
@@ -38,7 +35,7 @@ public class GunDayLocale() : SoloDayLocale("Gun Game",
|
||||
return new SimpleView {
|
||||
PREFIX,
|
||||
player,
|
||||
"is on their last weapon!",
|
||||
"is on the last weapon!",
|
||||
ChatColors.LightRed + "Watch out!"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Jailbreak.English.SpecialDay;
|
||||
|
||||
public class HNSDayLocale() : TeamDayLocale("Hide and Seek",
|
||||
"CTs must hide while the Ts seek!", "Ts have 250 HP!") {
|
||||
"CTs must hide while the Ts seek!", "Ts have increased HP!") {
|
||||
public IView StayInArmory
|
||||
=> new SimpleView { PREFIX, "Today is", Name, ", stay in the armory!" };
|
||||
|
||||
@@ -15,13 +15,16 @@ public class HNSDayLocale() : TeamDayLocale("Hide and Seek",
|
||||
Name,
|
||||
"begins in",
|
||||
seconds,
|
||||
"seconds."
|
||||
"second" + (seconds == 1 ? "" : "s") + "."
|
||||
};
|
||||
}
|
||||
|
||||
public IView DamageWarning(int seconds) {
|
||||
return new SimpleView {
|
||||
PREFIX, "You will be vulnerable to damage in", seconds, "seconds."
|
||||
PREFIX,
|
||||
"You will be vulnerable to damage in",
|
||||
seconds,
|
||||
"second" + (seconds == 1 ? "" : "s") + "."
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -25,13 +25,13 @@ public class InfectionDayLocale() : TeamDayLocale("Infection",
|
||||
return player == null || !player.IsValid ?
|
||||
new SimpleView {
|
||||
PREFIX,
|
||||
$"{ChatColors.Red}You were {ChatColors.DarkRed}infected{ChatColors.Red}! You are now a zombie!"
|
||||
$"{ChatColors.Red}You were {ChatColors.DarkRed}infected{ChatColors.Red}!"
|
||||
} :
|
||||
new SimpleView {
|
||||
PREFIX,
|
||||
$"{ChatColors.Red}You were {ChatColors.DarkRed}infected{ChatColors.Red} by",
|
||||
player,
|
||||
"! You are now a zombie!"
|
||||
"!"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,19 @@ public class SDLocale : ISDLocale, ILanguage<Formatting.Languages.English> {
|
||||
};
|
||||
|
||||
public IView SpecialDayRunning(string name) {
|
||||
return new SimpleView { PREFIX, name, "is currently running!" };
|
||||
return new SimpleView {
|
||||
PREFIX,
|
||||
ChatColors.DarkRed + name,
|
||||
ChatColors.Red + "is currently running."
|
||||
};
|
||||
}
|
||||
|
||||
public IView InvalidSpecialDay(string name) {
|
||||
return new SimpleView { PREFIX, name, "is not a valid special day!" };
|
||||
return new SimpleView {
|
||||
PREFIX,
|
||||
ChatColors.DarkRed + name,
|
||||
ChatColors.Red + "is not a valid special day."
|
||||
};
|
||||
}
|
||||
|
||||
public IView SpecialDayCooldown(int rounds) {
|
||||
@@ -28,7 +36,8 @@ public class SDLocale : ISDLocale, ILanguage<Formatting.Languages.English> {
|
||||
PREFIX,
|
||||
"You must wait",
|
||||
rounds,
|
||||
"more rounds before starting a special day."
|
||||
"more round" + (rounds == 1 ? "" : "s")
|
||||
+ " before starting a special day."
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,7 +46,7 @@ public class SDLocale : ISDLocale, ILanguage<Formatting.Languages.English> {
|
||||
PREFIX,
|
||||
"You must start a special day within",
|
||||
maxTime,
|
||||
"seconds of the round start."
|
||||
"second" + (maxTime == 1 ? "" : "s") + " of the round start."
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public class SpeedrunDayLocale() : SoloDayLocale("Speedrunners",
|
||||
return new SimpleView {
|
||||
{
|
||||
PREFIX,
|
||||
$"Round #{ChatColors.Yellow}{round}{ChatColors.Default} begins! The slowest",
|
||||
$"Round {ChatColors.Yellow}#{round}{ChatColors.Default} begins! The slowest",
|
||||
"player to reach the goal will be eliminated!"
|
||||
},
|
||||
SimpleView.NEWLINE,
|
||||
@@ -74,7 +74,7 @@ public class SpeedrunDayLocale() : SoloDayLocale("Speedrunners",
|
||||
PREFIX,
|
||||
"The original speedrunner left, so",
|
||||
player,
|
||||
"is now the speedrunner!"
|
||||
"is now the speedrunner."
|
||||
};
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ public class SpeedrunDayLocale() : SoloDayLocale("Speedrunners",
|
||||
PREFIX,
|
||||
"The original speedrunner isn't moving, so",
|
||||
player,
|
||||
"is now the speedrunner!"
|
||||
"is now the speedrunner."
|
||||
};
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ public class SpeedrunDayLocale() : SoloDayLocale("Speedrunners",
|
||||
"reached the goal. Eliminating one player on each time."
|
||||
},
|
||||
SimpleView.NEWLINE,
|
||||
{ PREFIX, "Randomly selected the path from ", player, "." }
|
||||
{ PREFIX, "Randomly selected the path from", player, "." }
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,9 @@ public class WardenCmdOpenLocale : IWardenCmdOpenLocale,
|
||||
}
|
||||
|
||||
public IView CellsOpened
|
||||
=> new SimpleView { WardenLocale.PREFIX, "Cells were auto-opened." };
|
||||
=> new SimpleView {
|
||||
WardenLocale.PREFIX, ChatColors.Grey + "Cells were auto-opened."
|
||||
};
|
||||
|
||||
public IView OpeningFailed
|
||||
=> new SimpleView { WardenLocale.PREFIX, "Failed to open the cells." };
|
||||
|
||||
@@ -48,7 +48,7 @@ public class PlayerZoneCreator : BasicZoneCreator, ITypedZoneCreator {
|
||||
}
|
||||
|
||||
var pawn = player.PlayerPawn.Value;
|
||||
if (pawn == null) {
|
||||
if (pawn == null || !pawn.IsValid) {
|
||||
timer?.Kill();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -18,43 +18,46 @@ namespace Jailbreak.LastGuard;
|
||||
|
||||
public class LastGuard(ILGLocale notifications, ILastRequestManager lrManager)
|
||||
: ILastGuardService, IPluginBehavior {
|
||||
public readonly FakeConVar<bool> CvAlwaysOverrideCt = new(
|
||||
public static readonly FakeConVar<bool> CV_ALWAYS_OVERRIDE_CT = new(
|
||||
"css_jb_lg_apply_lower_hp",
|
||||
"If true, the LG will be forced lower health if calculated");
|
||||
|
||||
public readonly FakeConVar<double> CvGuardHealthRatio = new(
|
||||
public static readonly FakeConVar<double> CV_GUARD_HEALTH_RATIO = new(
|
||||
"css_jb_lg_ct_hp_ratio", "Ratio of CT : T Health", 0.6,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<double>(0.00001, 10));
|
||||
|
||||
public readonly FakeConVar<int> CvLGBaseRoundTime = new("css_jb_lg_time_base",
|
||||
"Round time to set when LG is activated, 0 to disable", 60);
|
||||
public static readonly FakeConVar<int> CV_LG_BASE_ROUND_TIME =
|
||||
new("css_jb_lg_time_base",
|
||||
"Round time to set when LG is activated, 0 to disable", 60);
|
||||
|
||||
public readonly FakeConVar<int> CvLGKillBonusTime =
|
||||
public static readonly FakeConVar<int> CV_LG_KILL_BONUS_TIME =
|
||||
new("css_jb_lg_time_per_kill",
|
||||
"Additional round time to add per prisoner kill", 10);
|
||||
|
||||
public readonly FakeConVar<int> CvLGMaxTime = new("css_jb_lg_time_max",
|
||||
"Max round time to give the LG regardless of bonuses", 120);
|
||||
public static readonly FakeConVar<int> CV_LG_MAX_TIME =
|
||||
new("css_jb_lg_time_max",
|
||||
"Max round time to give the LG regardless of bonuses", 120);
|
||||
|
||||
public readonly FakeConVar<int> CvLGPerPrisonerTime =
|
||||
public static readonly FakeConVar<int> CV_LG_PER_PRISONER_TIME =
|
||||
new("css_jb_lg_time_per_prisoner",
|
||||
"Additional round time to add per prisoner", 10);
|
||||
|
||||
public readonly FakeConVar<string> CvLGWeapon = new("css_jb_lg_t_weapon",
|
||||
"Weapon to give remaining prisoners once LG activates", "",
|
||||
ConVarFlags.FCVAR_NONE, new WeaponValidator());
|
||||
public static readonly FakeConVar<string> CV_LG_WEAPON =
|
||||
new("css_jb_lg_t_weapon",
|
||||
"Weapon to give remaining prisoners once LG activates", "",
|
||||
ConVarFlags.FCVAR_NONE, new ItemValidator());
|
||||
|
||||
public readonly FakeConVar<int> CvMaxCtHealth = new("css_jb_lg_max_hp",
|
||||
"Max HP that the LG can have otherwise", 125, ConVarFlags.FCVAR_NONE,
|
||||
new RangeValidator<int>(1, 1000));
|
||||
public static readonly FakeConVar<int> CV_MAX_CT_HEALTH =
|
||||
new("css_jb_lg_max_hp", "Max HP that the LG can have otherwise", 125,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(1, 1000));
|
||||
|
||||
public readonly FakeConVar<int> CvMaxTHealthContribution = new(
|
||||
public static readonly FakeConVar<int> CV_MAX_T_HEALTH_CONTRIBUTION = new(
|
||||
"css_jb_lg_t_max_hp", "Max HP to contribute per T to LG", 200,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(1, 1000));
|
||||
|
||||
public readonly FakeConVar<int> CvMinimumCts = new("css_jb_lg_min_cts",
|
||||
"Minimum number of CTs to start last guard", 2, ConVarFlags.FCVAR_NONE,
|
||||
new RangeValidator<int>(1, 32));
|
||||
public static readonly FakeConVar<int> CV_MINIMUM_CTS =
|
||||
new("css_jb_lg_min_cts", "Minimum number of CTs to start last guard", 2,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(1, 32));
|
||||
|
||||
private readonly Random rng = new();
|
||||
private bool canStart;
|
||||
@@ -71,11 +74,11 @@ public class LastGuard(ILGLocale notifications, ILastRequestManager lrManager)
|
||||
API.Stats?.PushStat(new ServerStat("JB_LASTGUARD",
|
||||
lastGuard.SteamID.ToString()));
|
||||
|
||||
var calculated = CalculateHealth();
|
||||
var calculated = calculateHealth();
|
||||
|
||||
if (calculated < guardPlayerPawn.Health && !CvAlwaysOverrideCt.Value) {
|
||||
if (guardPlayerPawn.Health > CvMaxCtHealth.Value)
|
||||
guardPlayerPawn.Health = CvMaxCtHealth.Value;
|
||||
if (calculated < guardPlayerPawn.Health && !CV_ALWAYS_OVERRIDE_CT.Value) {
|
||||
if (guardPlayerPawn.Health > CV_MAX_CT_HEALTH.Value)
|
||||
guardPlayerPawn.Health = CV_MAX_CT_HEALTH.Value;
|
||||
} else { guardPlayerPawn.Health = calculated; }
|
||||
|
||||
Utilities.SetStateChanged(guardPlayerPawn, "CBaseEntity", "m_iHealth");
|
||||
@@ -87,11 +90,11 @@ public class LastGuard(ILGLocale notifications, ILastRequestManager lrManager)
|
||||
.Where(p => p is { PawnIsAlive: true, Team: CsTeam.Terrorist })
|
||||
.ToList();
|
||||
|
||||
if (CvLGBaseRoundTime.Value != 0)
|
||||
RoundUtil.SetTimeRemaining(Math.Min(CvLGBaseRoundTime.Value,
|
||||
CvLGMaxTime.Value));
|
||||
addRoundTimeCapped(CvLGPerPrisonerTime.Value * lastGuardPrisoners.Count,
|
||||
CvLGMaxTime.Value);
|
||||
if (CV_LG_BASE_ROUND_TIME.Value != 0)
|
||||
RoundUtil.SetTimeRemaining(Math.Min(CV_LG_BASE_ROUND_TIME.Value,
|
||||
CV_LG_MAX_TIME.Value));
|
||||
addRoundTimeCapped(CV_LG_PER_PRISONER_TIME.Value * lastGuardPrisoners.Count,
|
||||
CV_LG_MAX_TIME.Value);
|
||||
|
||||
var prisonerHp =
|
||||
lastGuardPrisoners.Sum(prisoner
|
||||
@@ -101,15 +104,15 @@ public class LastGuard(ILGLocale notifications, ILastRequestManager lrManager)
|
||||
.ToAllCenter()
|
||||
.ToAllChat();
|
||||
|
||||
if (string.IsNullOrEmpty(CvLGWeapon.Value)) return;
|
||||
if (string.IsNullOrEmpty(CV_LG_WEAPON.Value)) return;
|
||||
|
||||
foreach (var player in lastGuardPrisoners)
|
||||
player.GiveNamedItem(CvLGWeapon.Value);
|
||||
player.GiveNamedItem(CV_LG_WEAPON.Value);
|
||||
}
|
||||
|
||||
public void DisableLastGuardForRound() { canStart = false; }
|
||||
|
||||
public int CalculateHealth() {
|
||||
private int calculateHealth() {
|
||||
var aliveTerrorists = Utilities.GetPlayers()
|
||||
.Where(plr => plr is { PawnIsAlive: true, Team: CsTeam.Terrorist })
|
||||
.ToList();
|
||||
@@ -117,8 +120,8 @@ public class LastGuard(ILGLocale notifications, ILastRequestManager lrManager)
|
||||
return (int)Math.Floor(aliveTerrorists
|
||||
.Select(player => player.PlayerPawn.Value?.Health ?? 0)
|
||||
.Select(playerHealth
|
||||
=> Math.Min(playerHealth, CvMaxTHealthContribution.Value))
|
||||
.Sum() * CvGuardHealthRatio.Value);
|
||||
=> Math.Min(playerHealth, CV_MAX_T_HEALTH_CONTRIBUTION.Value))
|
||||
.Sum() * CV_GUARD_HEALTH_RATIO.Value);
|
||||
}
|
||||
|
||||
[GameEventHandler]
|
||||
@@ -132,7 +135,7 @@ public class LastGuard(ILGLocale notifications, ILastRequestManager lrManager)
|
||||
|
||||
if (player.Team != CsTeam.Terrorist) return HookResult.Continue;
|
||||
|
||||
addRoundTimeCapped(CvLGKillBonusTime.Value, CvLGMaxTime.Value);
|
||||
addRoundTimeCapped(CV_LG_KILL_BONUS_TIME.Value, CV_LG_MAX_TIME.Value);
|
||||
|
||||
giveGun(player);
|
||||
return HookResult.Continue;
|
||||
@@ -200,7 +203,7 @@ public class LastGuard(ILGLocale notifications, ILastRequestManager lrManager)
|
||||
canStart = Utilities.GetPlayers()
|
||||
.Count(plr
|
||||
=> plr is { PawnIsAlive: true, Team: CsTeam.CounterTerrorist })
|
||||
>= CvMinimumCts.Value;
|
||||
>= CV_MINIMUM_CTS.Value;
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Jailbreak.LastGuard;
|
||||
|
||||
[Obsolete("No longer used, use FakeConvars")]
|
||||
public class LastGuardConfig {
|
||||
public string? LastGuardWeapon { get; } = "";
|
||||
public int MinimumCTs { get; } = 4;
|
||||
}
|
||||
@@ -20,16 +20,16 @@ namespace Jailbreak.LastRequest;
|
||||
|
||||
public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
|
||||
: ILastRequestManager, IDamageBlocker {
|
||||
public readonly FakeConVar<int> CvLRBaseTime = new("css_jb_lr_time_base",
|
||||
public static readonly FakeConVar<int> CV_LR_BASE_TIME = new("css_jb_lr_time_base",
|
||||
"Round time to set when LR is activated, 0 to disable", 60);
|
||||
|
||||
public readonly FakeConVar<int> CvLRBonusTime = new("css_jb_lr_time_per_lr",
|
||||
public static readonly FakeConVar<int> CV_LR_BONUS_TIME = new("css_jb_lr_time_per_lr",
|
||||
"Additional round time to add per LR completion", 30);
|
||||
|
||||
public readonly FakeConVar<int> CvLRGuardTime =
|
||||
public static readonly FakeConVar<int> CV_LR_GUARD_TIME =
|
||||
new("css_jb_lr_time_per_guard", "Additional round time to add per guard");
|
||||
|
||||
public readonly FakeConVar<int> CvPrisonerToLR =
|
||||
public static readonly FakeConVar<int> CV_PRISONER_TO_LR =
|
||||
new("css_jb_lr_activate_lr_at", "Number of prisoners to activate LR at", 2,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(1, 32));
|
||||
|
||||
@@ -92,12 +92,12 @@ public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
|
||||
var cts = Utilities.GetPlayers()
|
||||
.Count(p => p is { Team: CsTeam.CounterTerrorist, PawnIsAlive: true });
|
||||
|
||||
if (CvLRBaseTime.Value != 0) RoundUtil.SetTimeRemaining(CvLRBaseTime.Value);
|
||||
if (CV_LR_BASE_TIME.Value != 0) RoundUtil.SetTimeRemaining(CV_LR_BASE_TIME.Value);
|
||||
|
||||
RoundUtil.AddTimeRemaining(CvLRGuardTime.Value * cts);
|
||||
RoundUtil.AddTimeRemaining(CV_LR_GUARD_TIME.Value * cts);
|
||||
|
||||
foreach (var player in Utilities.GetPlayers()) {
|
||||
player.ExecuteClientCommand($"play sounds/lr");
|
||||
player.ExecuteClientCommand("play sounds/lr");
|
||||
if (player.Team != CsTeam.Terrorist || !player.PawnIsAlive) continue;
|
||||
if (died != null && player.SteamID == died.SteamID) continue;
|
||||
player.ExecuteClientCommandFromServer("css_lr");
|
||||
@@ -132,7 +132,7 @@ public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
|
||||
|
||||
public bool EndLastRequest(AbstractLastRequest lr, LRResult result) {
|
||||
if (result is LRResult.GUARD_WIN or LRResult.PRISONER_WIN) {
|
||||
RoundUtil.AddTimeRemaining(CvLRBonusTime.Value);
|
||||
RoundUtil.AddTimeRemaining(CV_LR_BONUS_TIME.Value);
|
||||
messages.LastRequestDecided(lr, result).ToAllChat();
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
|
||||
|
||||
if (player.Team != CsTeam.Terrorist) return HookResult.Continue;
|
||||
|
||||
if (countAlivePrisoners() - 1 > CvPrisonerToLR.Value)
|
||||
if (countAlivePrisoners() - 1 > CV_PRISONER_TO_LR.Value)
|
||||
return HookResult.Continue;
|
||||
|
||||
if (Utilities.GetPlayers().All(p => p.Team != CsTeam.CounterTerrorist))
|
||||
@@ -222,7 +222,7 @@ public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
|
||||
if (!IsLREnabledForRound) return HookResult.Continue;
|
||||
|
||||
if (player.Team != CsTeam.Terrorist) return HookResult.Continue;
|
||||
if (countAlivePrisoners() > CvPrisonerToLR.Value)
|
||||
if (countAlivePrisoners() > CV_PRISONER_TO_LR.Value)
|
||||
return HookResult.Continue;
|
||||
|
||||
EnableLR();
|
||||
|
||||
@@ -45,7 +45,8 @@ public class BulletForBullet : TeleportingRequest {
|
||||
designerName = designer;
|
||||
MenuManager.CloseActiveMenu(player);
|
||||
|
||||
msg.WeaponSelected(player, designerName).ToChat(Prisoner, Guard);
|
||||
msg.WeaponSelected(player, designerName.GetFriendlyWeaponName())
|
||||
.ToChat(Prisoner, Guard);
|
||||
|
||||
State = LRState.ACTIVE;
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Jailbreak.Formatting.Extensions;
|
||||
@@ -25,11 +27,27 @@ public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService)
|
||||
|
||||
private BasePlugin? plugin;
|
||||
|
||||
public static readonly FakeConVar<bool> CV_GIVE_BOMB = new("css_jb_c4_give",
|
||||
"Whether to give a random prisoner a bomb at the beginning of the round.",
|
||||
true);
|
||||
|
||||
public static readonly FakeConVar<float> CV_C4_DELAY = new("css_jb_c4_delay",
|
||||
"Time in seconds that the bomb takes to explode", .75f,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(0, 2));
|
||||
|
||||
public static readonly FakeConVar<float> CV_C4_RADIUS =
|
||||
new("css_jb_c4_radius", "Bomb explosion radius", 350,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(0, 10000));
|
||||
|
||||
public static readonly FakeConVar<float> CV_C4_BASE_DAMAGE =
|
||||
new("css_jb_c4_damage", "Base damage to apply", 340, ConVarFlags.FCVAR_NONE,
|
||||
new RangeValidator<float>(0, 10000));
|
||||
|
||||
public void ClearActiveC4s() { bombs.Clear(); }
|
||||
|
||||
public void TryGiveC4ToPlayer(CCSPlayerController player) {
|
||||
var bombEntity = new CC4(player.GiveNamedItem("weapon_c4"));
|
||||
bombs.Add(bombEntity, new C4Metadata(0.75f, false));
|
||||
bombs.Add(bombEntity, new C4Metadata(false));
|
||||
|
||||
ic4Locale.JihadC4Received.ToChat(player);
|
||||
ic4Locale.JihadC4Usage1.ToChat(player);
|
||||
@@ -45,7 +63,6 @@ public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService)
|
||||
|
||||
tryEmitSound(player, "jb.jihad", 1, 1f, 0f);
|
||||
|
||||
bombs[bombEntity].Delay = delay;
|
||||
bombs[bombEntity].IsDetonating = true;
|
||||
|
||||
rebelService.MarkRebel(player);
|
||||
@@ -95,14 +112,15 @@ public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService)
|
||||
|| activeWeapon.Handle != bomb.Handle)
|
||||
continue;
|
||||
|
||||
StartDetonationAttempt(bombCarrier, meta.Delay, bomb);
|
||||
StartDetonationAttempt(bombCarrier, CV_C4_DELAY.Value, bomb);
|
||||
}
|
||||
}
|
||||
|
||||
[GameEventHandler]
|
||||
public HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) {
|
||||
ClearActiveC4s();
|
||||
TryGiveC4ToRandomTerrorist();
|
||||
|
||||
if (CV_GIVE_BOMB.Value) TryGiveC4ToRandomTerrorist();
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
@@ -154,11 +172,10 @@ public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService)
|
||||
var distanceFromBomb =
|
||||
ct.PlayerPawn.Value!.AbsOrigin!.Distance(player.PlayerPawn.Value
|
||||
.AbsOrigin!);
|
||||
if (distanceFromBomb > 350f) continue;
|
||||
if (distanceFromBomb > CV_C4_RADIUS.Value) continue;
|
||||
|
||||
// 350f = "bombRadius"
|
||||
var damage = 340f;
|
||||
damage *= (350f - distanceFromBomb) / 350f;
|
||||
var damage = CV_C4_BASE_DAMAGE.Value;
|
||||
damage *= (CV_C4_RADIUS.Value - distanceFromBomb) / CV_C4_RADIUS.Value;
|
||||
float healthRef = ct.PlayerPawn.Value.Health;
|
||||
if (healthRef <= damage) {
|
||||
ct.CommitSuicide(true, true);
|
||||
@@ -183,8 +200,7 @@ public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService)
|
||||
volume, delay);
|
||||
}
|
||||
|
||||
private class C4Metadata(float delay, bool isDetonating) {
|
||||
public float Delay { get; set; } = delay;
|
||||
private class C4Metadata(bool isDetonating) {
|
||||
public bool IsDetonating { get; set; } = isDetonating;
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,9 @@ public class RebelManager(IRebelLocale notifs, IRichLogService logs)
|
||||
[Obsolete("No longer used, use FakeConvar")]
|
||||
public static readonly int MAX_REBEL_TIME = 45;
|
||||
|
||||
public readonly FakeConVar<int> CvRebelTime = new("css_jb_rebel_time",
|
||||
"Time to mark a rebel for", 30, ConVarFlags.FCVAR_NONE,
|
||||
new RangeValidator<int>(0, 500));
|
||||
public static readonly FakeConVar<int> CV_REBEL_TIME =
|
||||
new("css_jb_rebel_time", "Time to mark a rebel for", 45,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 500));
|
||||
|
||||
private readonly Dictionary<CCSPlayerController, long> rebelTimes = new();
|
||||
private bool enabled = true;
|
||||
@@ -71,7 +71,7 @@ public class RebelManager(IRebelLocale notifs, IRichLogService logs)
|
||||
API.Stats?.PushStat(new ServerStat("JB_REBEL_STARTED",
|
||||
$"{player.SteamID} {pos.X:F2} {pos.Y:F2} {pos.Z:F2}"));
|
||||
|
||||
if (time == -1) time = CvRebelTime.Value;
|
||||
if (time == -1) time = CV_REBEL_TIME.Value;
|
||||
|
||||
rebelTimes[player] = DateTimeOffset.Now.ToUnixTimeSeconds() + time;
|
||||
applyRebelColor(player);
|
||||
@@ -128,10 +128,10 @@ public class RebelManager(IRebelLocale notifs, IRichLogService logs)
|
||||
// https://www.desmos.com/calculator/g2v6vvg7ax
|
||||
private float getRebelTimePercentage(CCSPlayerController player) {
|
||||
var x = GetRebelTimeLeft(player);
|
||||
if (x > CvRebelTime.Value) return 1;
|
||||
if (x > CV_REBEL_TIME.Value) return 1;
|
||||
if (x <= 0) return 0;
|
||||
return (float)(100 - (CvRebelTime.Value - x)
|
||||
* Math.Sqrt(CvRebelTime.Value - x) / 3.8f) / 100;
|
||||
return (float)(100 - (CV_REBEL_TIME.Value - x)
|
||||
* Math.Sqrt(CV_REBEL_TIME.Value - x) / 3.8f) / 100;
|
||||
}
|
||||
|
||||
private Color getRebelColor(CCSPlayerController player) {
|
||||
|
||||
@@ -20,15 +20,15 @@ namespace Jailbreak.SpecialDay;
|
||||
public class SpecialDayCommand(IWardenService warden,
|
||||
ISpecialDayFactory factory, IWardenLocale wardenMsg, ISDLocale sdMsg,
|
||||
ISpecialDayManager sd) : IPluginBehavior {
|
||||
public static FakeConVar<int> CvRoundsBetweenSD = new(
|
||||
public static readonly FakeConVar<int> CV_ROUNDS_BETWEEN_SD = new(
|
||||
"css_jb_sd_round_cooldown", "Rounds between special days", 4);
|
||||
|
||||
public static FakeConVar<int> CvMaxElapsedTime = new(
|
||||
public static readonly FakeConVar<int> CV_MAX_ELAPSED_TIME = new(
|
||||
"css_jb_sd_max_elapsed_time",
|
||||
"Max time elapsed in a round to be able to call a special day", 30);
|
||||
|
||||
private SpecialDayMenuSelector? menuSelector;
|
||||
private BasePlugin? plugin;
|
||||
private SpecialDayMenuSelector menuSelector = null!;
|
||||
private BasePlugin plugin = null!;
|
||||
|
||||
// css_lr <player> <LRType>
|
||||
public void Start(BasePlugin basePlugin) {
|
||||
@@ -41,6 +41,17 @@ public class SpecialDayCommand(IWardenService warden,
|
||||
[ConsoleCommand("css_startday", "Start a special day as the warden")]
|
||||
public void Command_SpecialDay(CCSPlayerController? executor,
|
||||
CommandInfo info) {
|
||||
if (executor != null && sd.IsSDRunning && info.ArgCount == 1) {
|
||||
// SD is already running
|
||||
if (sd.CurrentSD is ISpecialDayMessageProvider messaged)
|
||||
sdMsg.SpecialDayRunning(messaged.Locale.Name).ToChat(executor);
|
||||
else
|
||||
sdMsg.SpecialDayRunning(sd.CurrentSD?.Type.ToString() ?? "Unknown")
|
||||
.ToChat(executor);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (executor != null
|
||||
&& !AdminManager.PlayerHasPermissions(executor, "@css/rcon")) {
|
||||
if (!warden.IsWarden(executor) || RoundUtil.IsWarmup()) {
|
||||
@@ -59,14 +70,14 @@ public class SpecialDayCommand(IWardenService warden,
|
||||
return;
|
||||
}
|
||||
|
||||
var roundsToNext = sd.RoundsSinceLastSD - CvRoundsBetweenSD.Value;
|
||||
var roundsToNext = sd.RoundsSinceLastSD - CV_ROUNDS_BETWEEN_SD.Value;
|
||||
if (roundsToNext < 0) {
|
||||
sdMsg.SpecialDayCooldown(Math.Abs(roundsToNext)).ToChat(executor);
|
||||
return;
|
||||
}
|
||||
|
||||
if (RoundUtil.GetTimeElapsed() > CvMaxElapsedTime.Value) {
|
||||
sdMsg.TooLateForSpecialDay(CvMaxElapsedTime.Value);
|
||||
if (RoundUtil.GetTimeElapsed() > CV_MAX_ELAPSED_TIME.Value) {
|
||||
sdMsg.TooLateForSpecialDay(CV_MAX_ELAPSED_TIME.Value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -77,8 +88,7 @@ public class SpecialDayCommand(IWardenService warden,
|
||||
return;
|
||||
}
|
||||
|
||||
MenuManager.OpenCenterHtmlMenu(plugin!, executor,
|
||||
menuSelector!.GetMenu());
|
||||
MenuManager.OpenCenterHtmlMenu(plugin, executor, menuSelector.GetMenu());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ using Jailbreak.Zones;
|
||||
namespace Jailbreak.SpecialDay.SpecialDays;
|
||||
|
||||
public abstract class AbstractZoneRestrictedDay : AbstractSpecialDay {
|
||||
protected readonly CsTeam RestrictedTeam;
|
||||
protected CsTeam RestrictedTeam;
|
||||
|
||||
protected readonly IList<MovementRestrictor> Restrictors =
|
||||
new List<MovementRestrictor>();
|
||||
|
||||
@@ -23,7 +23,6 @@ public class FFADay(BasePlugin plugin, IServiceProvider provider)
|
||||
base.Setup();
|
||||
}
|
||||
|
||||
|
||||
public override void Execute() {
|
||||
base.Execute();
|
||||
Locale.BeginsIn(0).ToAllChat();
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Jailbreak.English.SpecialDay;
|
||||
using Jailbreak.Formatting.Base;
|
||||
@@ -8,6 +11,7 @@ using Jailbreak.Public.Extensions;
|
||||
using Jailbreak.Public.Mod.SpecialDay;
|
||||
using Jailbreak.Public.Mod.SpecialDay.Enums;
|
||||
using Jailbreak.Public.Utils;
|
||||
using Jailbreak.Validator;
|
||||
|
||||
namespace Jailbreak.SpecialDay.SpecialDays;
|
||||
|
||||
@@ -15,68 +19,161 @@ public class HideAndSeekDay(BasePlugin plugin, IServiceProvider provider)
|
||||
: AbstractArmoryRestrictedDay(plugin, provider), ISpecialDayMessageProvider {
|
||||
public override SDType Type => SDType.HNS;
|
||||
|
||||
private HNSDayLocale msg => (HNSDayLocale)Locale;
|
||||
|
||||
public override SpecialDaySettings Settings => new HNSSettings();
|
||||
|
||||
public override IView ArmoryReminder => msg.StayInArmory;
|
||||
|
||||
private HNSDayLocale Msg => (HNSDayLocale)Locale;
|
||||
public override SpecialDaySettings Settings => new HnsSettings();
|
||||
public override IView ArmoryReminder => Msg.StayInArmory;
|
||||
public ISDInstanceLocale Locale => new HNSDayLocale();
|
||||
|
||||
// Set to -1 to not modify values
|
||||
public static readonly FakeConVar<int> CV_PRISONER_PRE_HEALTH = new(
|
||||
"jb_sd_hns_hide_hp_t", "Health to give to prisoners during HNS hide time",
|
||||
300, ConVarFlags.FCVAR_NONE, new NonZeroRangeValidator<int>(-1, 1000));
|
||||
|
||||
public static readonly FakeConVar<int> CV_GUARD_PRE_HEALTH =
|
||||
new("jb_sd_hns_hide_hp_ct", "Health to give to guards during HNS hide time",
|
||||
150, ConVarFlags.FCVAR_NONE, new RangeValidator<int>(-1, 1000));
|
||||
|
||||
public static readonly FakeConVar<int> CV_PRISONER_PRE_ARMOR = new(
|
||||
"jb_sd_hns_hide_armor_t", "Armor to give to prisoners during HNS hide time",
|
||||
200, ConVarFlags.FCVAR_NONE, new RangeValidator<int>(-1, 1000));
|
||||
|
||||
public static readonly FakeConVar<int> CV_GUARD_PRE_ARMOR =
|
||||
new("jb_sd_hns_hide_armor_ct",
|
||||
"Armor to give to guards during HNS hide time", 300,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(-1, 1000));
|
||||
|
||||
public static readonly FakeConVar<int> CV_PRISONER_POST_HEALTH = new(
|
||||
"jb_sd_hns_seek_hp_t", "Health to give to prisoners during HNS seek time",
|
||||
300, ConVarFlags.FCVAR_NONE, new NonZeroRangeValidator<int>(1, 1000));
|
||||
|
||||
public static readonly FakeConVar<int> CV_GUARD_POST_HEALTH = new(
|
||||
"jb_sd_hns_seek_hp_ct", "Health to give to guards during HNS seek time", 25,
|
||||
ConVarFlags.FCVAR_NONE, new NonZeroRangeValidator<int>(1, 1000));
|
||||
|
||||
public static readonly FakeConVar<int> CV_PRISONER_POST_ARMOR = new(
|
||||
"jb_sd_hns_seek_armor_t", "Armor to give to prisoners during HNS seek time",
|
||||
500, ConVarFlags.FCVAR_NONE, new RangeValidator<int>(-1, 1000));
|
||||
|
||||
public static readonly FakeConVar<int> CV_GUARD_POST_ARMOR = new(
|
||||
"jb_sd_hns_seek_armor_ct", "Armor to give to guards during HNS seek time",
|
||||
-1, ConVarFlags.FCVAR_NONE, new RangeValidator<int>(-1, 1000));
|
||||
|
||||
public static readonly FakeConVar<string> CV_GUARD_WEAPONS = new(
|
||||
"jb_sd_hns_weapons_ct",
|
||||
"List of weapons/items CTs may use, empty for no restrictions",
|
||||
string.Join(",", Tag.PISTOLS.Union(Tag.UTILITY)), ConVarFlags.FCVAR_NONE,
|
||||
new ItemValidator(allowMultiple: true));
|
||||
|
||||
public static readonly FakeConVar<string> CV_PRISONER_WEAPONS = new(
|
||||
"jb_sd_hns_weapons_t",
|
||||
"List of weapons/items Ts may use, empty for no restrictions", "",
|
||||
ConVarFlags.FCVAR_NONE, new ItemValidator(allowMultiple: true));
|
||||
|
||||
public static readonly FakeConVar<string> CV_SEEKER_TEAM =
|
||||
new("jb_sd_hns_seekers", "Team to assign as seekers and restrict to armory",
|
||||
"t", ConVarFlags.FCVAR_NONE, new TeamValidator(false));
|
||||
|
||||
public static readonly FakeConVar<int> CV_SEEK_TIME =
|
||||
new("jb_sd_hns_seektime",
|
||||
"Duration in seconds to give the hiders time to hide", 45,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(5, 120));
|
||||
|
||||
private CsTeam? SeekerTeam => TeamUtil.FromString(CV_SEEKER_TEAM.Value);
|
||||
|
||||
private CsTeam? HiderTeam
|
||||
=> (SeekerTeam ?? CsTeam.Terrorist) == CsTeam.Terrorist ?
|
||||
CsTeam.CounterTerrorist :
|
||||
CsTeam.Terrorist;
|
||||
|
||||
public override void Setup() {
|
||||
Timers[10] += () => {
|
||||
foreach (var ct in PlayerUtil.FromTeam(CsTeam.CounterTerrorist))
|
||||
ct.SetSpeed(1.5f);
|
||||
if (SeekerTeam == null || HiderTeam == null) return;
|
||||
RestrictedTeam = SeekerTeam.Value;
|
||||
|
||||
msg.DamageWarning(15).ToTeamChat(CsTeam.CounterTerrorist);
|
||||
if (CV_SEEK_TIME.Value >= 10)
|
||||
Timers[10] += () => {
|
||||
foreach (var ct in PlayerUtil.FromTeam(SeekerTeam.Value))
|
||||
ct.SetSpeed(1.5f);
|
||||
|
||||
Locale.BeginsIn(35).ToAllChat();
|
||||
};
|
||||
Timers[25] += () => {
|
||||
foreach (var ct in PlayerUtil.FromTeam(CsTeam.CounterTerrorist)) {
|
||||
ct.SetSpeed(1.25f);
|
||||
EnableDamage(ct);
|
||||
}
|
||||
};
|
||||
Timers[30] += () => {
|
||||
foreach (var ct in PlayerUtil.FromTeam(CsTeam.CounterTerrorist))
|
||||
ct.SetSpeed(1.1f);
|
||||
Locale.BeginsIn(15).ToAllChat();
|
||||
};
|
||||
Timers[45] += Execute;
|
||||
Msg.DamageWarning(15).ToTeamChat(SeekerTeam.Value);
|
||||
};
|
||||
if (CV_SEEK_TIME.Value >= 25)
|
||||
Timers[25] += () => {
|
||||
foreach (var player in PlayerUtil.FromTeam(SeekerTeam.Value)) {
|
||||
player.SetSpeed(1.25f);
|
||||
EnableDamage(player);
|
||||
}
|
||||
};
|
||||
|
||||
if (CV_SEEK_TIME.Value >= 30)
|
||||
Timers[30] += () => {
|
||||
foreach (var ct in PlayerUtil.FromTeam(SeekerTeam.Value))
|
||||
ct.SetSpeed(1.1f);
|
||||
};
|
||||
|
||||
for (var offset = 15; offset < CV_SEEK_TIME.Value; offset += 15) {
|
||||
var beginsIn = CV_SEEK_TIME.Value - offset;
|
||||
Timers[CV_SEEK_TIME.Value - offset] +=
|
||||
() => Locale.BeginsIn(beginsIn).ToAllChat();
|
||||
}
|
||||
|
||||
Timers[CV_SEEK_TIME.Value] += Execute;
|
||||
|
||||
base.Setup();
|
||||
|
||||
foreach (var ct in PlayerUtil.FromTeam(CsTeam.CounterTerrorist))
|
||||
ct.SetSpeed(2f);
|
||||
foreach (var player in PlayerUtil.FromTeam(SeekerTeam.Value))
|
||||
player.SetSpeed(2f);
|
||||
}
|
||||
|
||||
public override void Execute() {
|
||||
if (SeekerTeam == null || HiderTeam == null) return;
|
||||
base.Execute();
|
||||
foreach (var t in PlayerUtil.FromTeam(CsTeam.Terrorist)) t.SetArmor(100);
|
||||
foreach (var ct in PlayerUtil.FromTeam(CsTeam.CounterTerrorist))
|
||||
ct.SetSpeed(1);
|
||||
foreach (var player in PlayerUtil.GetAlive()) {
|
||||
var hp = (player.Team == CsTeam.Terrorist ?
|
||||
CV_PRISONER_POST_HEALTH :
|
||||
CV_GUARD_POST_HEALTH).Value;
|
||||
|
||||
var armor = (player.Team == CsTeam.Terrorist ?
|
||||
CV_PRISONER_POST_ARMOR :
|
||||
CV_GUARD_POST_ARMOR).Value;
|
||||
|
||||
if (hp != -1) player.SetHealth(hp);
|
||||
if (armor != -1) player.SetArmor(armor);
|
||||
}
|
||||
|
||||
foreach (var ct in PlayerUtil.FromTeam(SeekerTeam.Value)) ct.SetSpeed(1);
|
||||
}
|
||||
|
||||
public class HNSSettings : SpecialDaySettings {
|
||||
public HNSSettings() {
|
||||
public class HnsSettings : SpecialDaySettings {
|
||||
private readonly ISet<string>? cachedGuardWeapons, cachedPrisonerWeapons;
|
||||
|
||||
public HnsSettings() {
|
||||
AllowLastRequests = true;
|
||||
TTeleport = TeleportType.ARMORY;
|
||||
CtTeleport = TeleportType.ARMORY;
|
||||
|
||||
cachedGuardWeapons = CV_GUARD_WEAPONS.Value.Split(",").ToHashSet();
|
||||
cachedPrisonerWeapons = CV_PRISONER_WEAPONS.Value.Split(",").ToHashSet();
|
||||
|
||||
if (cachedGuardWeapons.Count == 0) cachedGuardWeapons = null;
|
||||
if (cachedPrisonerWeapons.Count == 0) cachedPrisonerWeapons = null;
|
||||
}
|
||||
|
||||
public override int InitialHealth(CCSPlayerController player) {
|
||||
return player.Team == CsTeam.Terrorist ? 250 : 50;
|
||||
return player.Team == CsTeam.Terrorist ?
|
||||
CV_PRISONER_PRE_HEALTH.Value :
|
||||
CV_GUARD_PRE_HEALTH.Value;
|
||||
}
|
||||
|
||||
public override int InitialArmor(CCSPlayerController player) {
|
||||
if (player.Team != CsTeam.Terrorist) return -1;
|
||||
return 500;
|
||||
return player.Team == CsTeam.Terrorist ?
|
||||
CV_PRISONER_PRE_ARMOR.Value :
|
||||
CV_GUARD_PRE_ARMOR.Value;
|
||||
}
|
||||
|
||||
public override ISet<string>? AllowedWeapons(CCSPlayerController player) {
|
||||
if (player.Team != CsTeam.CounterTerrorist) return null;
|
||||
return Tag.PISTOLS.Union(Tag.UTILITY).ToHashSet();
|
||||
return player.Team == CsTeam.Terrorist ?
|
||||
cachedPrisonerWeapons :
|
||||
cachedGuardWeapons;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,13 +12,11 @@ using Jailbreak.Public.Utils;
|
||||
|
||||
namespace Jailbreak.SpecialDay.SpecialDays;
|
||||
|
||||
public class InfectionDay : AbstractArmoryRestrictedDay,
|
||||
ISpecialDayMessageProvider {
|
||||
public class InfectionDay(BasePlugin plugin, IServiceProvider provider)
|
||||
: AbstractArmoryRestrictedDay(plugin, provider, CsTeam.CounterTerrorist),
|
||||
ISpecialDayMessageProvider {
|
||||
private readonly ICollection<int> swappedPrisoners = new HashSet<int>();
|
||||
|
||||
public InfectionDay(BasePlugin Plugin, IServiceProvider provider) : base(
|
||||
Plugin, provider, CsTeam.CounterTerrorist) { }
|
||||
|
||||
public override SDType Type => SDType.INFECTION;
|
||||
|
||||
public override SpecialDaySettings Settings => new InfectionSettings();
|
||||
@@ -40,12 +38,12 @@ public class InfectionDay : AbstractArmoryRestrictedDay,
|
||||
foreach (var ct in PlayerUtil.FromTeam(CsTeam.CounterTerrorist))
|
||||
ct.SetColor(Color.LimeGreen);
|
||||
|
||||
Plugin.RegisterEventHandler<EventPlayerDeath>(OnPlayerDeath);
|
||||
Plugin.RegisterEventHandler<EventPlayerSpawn>(OnRespawn);
|
||||
Plugin.RegisterEventHandler<EventPlayerDeath>(onPlayerDeath);
|
||||
Plugin.RegisterEventHandler<EventPlayerSpawn>(onRespawn);
|
||||
}
|
||||
|
||||
private HookResult
|
||||
OnPlayerDeath(EventPlayerDeath @event, GameEventInfo info) {
|
||||
onPlayerDeath(EventPlayerDeath @event, GameEventInfo info) {
|
||||
var player = @event.Userid;
|
||||
if (player == null || !player.IsValid) return HookResult.Continue;
|
||||
if (player.Team != CsTeam.Terrorist) return HookResult.Continue;
|
||||
@@ -101,7 +99,7 @@ public class InfectionDay : AbstractArmoryRestrictedDay,
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
public HookResult OnRespawn(EventPlayerSpawn @event, GameEventInfo info) {
|
||||
private HookResult onRespawn(EventPlayerSpawn @event, GameEventInfo info) {
|
||||
var player = @event.Userid;
|
||||
if (player == null || !player.IsValid) return HookResult.Continue;
|
||||
if (player.Team != CsTeam.CounterTerrorist) return HookResult.Continue;
|
||||
@@ -119,13 +117,13 @@ public class InfectionDay : AbstractArmoryRestrictedDay,
|
||||
override protected HookResult
|
||||
OnEnd(EventRoundEnd @event, GameEventInfo info) {
|
||||
var result = base.OnEnd(@event, info);
|
||||
Plugin.DeregisterEventHandler<EventPlayerDeath>(OnPlayerDeath);
|
||||
Plugin.DeregisterEventHandler<EventPlayerSpawn>(OnRespawn);
|
||||
Plugin.DeregisterEventHandler<EventPlayerDeath>(onPlayerDeath);
|
||||
Plugin.DeregisterEventHandler<EventPlayerSpawn>(onRespawn);
|
||||
|
||||
Plugin.AddTimer(0.1f, () => {
|
||||
foreach (var index in swappedPrisoners) {
|
||||
var player = Utilities.GetPlayerFromSlot(index);
|
||||
if (player == null) continue;
|
||||
if (player == null || !player.IsValid) continue;
|
||||
player.SwitchTeam(CsTeam.Terrorist);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
using Jailbreak.English.SpecialDay;
|
||||
using Jailbreak.Formatting.Views.SpecialDay;
|
||||
using Jailbreak.Public.Extensions;
|
||||
using Jailbreak.Public.Mod.SpecialDay;
|
||||
using Jailbreak.Public.Mod.SpecialDay.Enums;
|
||||
using Jailbreak.Public.Utils;
|
||||
using Jailbreak.Validator;
|
||||
|
||||
namespace Jailbreak.SpecialDay.SpecialDays;
|
||||
|
||||
@@ -19,18 +22,42 @@ public class NoScopeDay(BasePlugin plugin, IServiceProvider provider)
|
||||
|
||||
public override SpecialDaySettings Settings => new NoScopeSettings();
|
||||
|
||||
public static readonly FakeConVar<string> CV_WEAPON = new(
|
||||
"jb_sd_noscope_weapon",
|
||||
"Weapon to give to all players, recommended it be a weapon with a scope (duh)",
|
||||
"weapon_ssg08", ConVarFlags.FCVAR_NONE,
|
||||
new ItemValidator(allowMultiple: true));
|
||||
|
||||
public static readonly FakeConVar<string> CV_WEAPON_WHITELIST = new(
|
||||
"jb_sd_noscope_allowedweapons",
|
||||
"Weapons to allow players to use, empty for no restrictions",
|
||||
string.Join(",",
|
||||
Tag.UTILITY.Union(new[] { "weapon_ssg08", "weapon_knife" }.ToHashSet())),
|
||||
ConVarFlags.FCVAR_NONE, new ItemValidator(allowMultiple: true));
|
||||
|
||||
public static readonly FakeConVar<int> CV_KNIFE_DELAY = new(
|
||||
"jb_sd_noscope_knife_delay",
|
||||
"Time delay in seconds to give knives at, 0 to disable", 120,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 500));
|
||||
|
||||
public static readonly FakeConVar<float> CV_GRAVITY =
|
||||
new("jb_sd_noscope_gravity",
|
||||
"Gravity to set during the special day, default is 800", 200f);
|
||||
|
||||
public override void Setup() {
|
||||
Timers[120] += () => {
|
||||
foreach (var player in PlayerUtil.GetAlive())
|
||||
player.GiveNamedItem("weapon_knife");
|
||||
};
|
||||
if (CV_KNIFE_DELAY.Value > 0)
|
||||
Timers[CV_KNIFE_DELAY.Value] += () => {
|
||||
foreach (var player in PlayerUtil.GetAlive())
|
||||
player.GiveNamedItem("weapon_knife");
|
||||
};
|
||||
base.Setup();
|
||||
}
|
||||
|
||||
public override void Execute() {
|
||||
foreach (var player in PlayerUtil.GetAlive()) {
|
||||
player.RemoveWeapons();
|
||||
player.GiveNamedItem("weapon_ssg08");
|
||||
foreach (var weapon in CV_WEAPON.Value.Split(","))
|
||||
player.GiveNamedItem(weapon);
|
||||
}
|
||||
|
||||
base.Execute();
|
||||
@@ -50,8 +77,9 @@ public class NoScopeDay(BasePlugin plugin, IServiceProvider provider)
|
||||
if (activeWeapon == null || !activeWeapon.IsValid) return;
|
||||
activeWeapon.NextSecondaryAttackTick = Server.TickCount + 500;
|
||||
|
||||
if (activeWeapon.DesignerName is "weapon_ssg08" or "weapon_knife") return;
|
||||
if (Tag.UTILITY.Contains(activeWeapon.DesignerName)) return;
|
||||
if (CV_WEAPON_WHITELIST.Value.Contains(activeWeapon.DesignerName,
|
||||
StringComparison.CurrentCultureIgnoreCase))
|
||||
return;
|
||||
activeWeapon.NextPrimaryAttackTick = Server.TickCount + 500;
|
||||
}
|
||||
|
||||
@@ -61,7 +89,7 @@ public class NoScopeDay(BasePlugin plugin, IServiceProvider provider)
|
||||
TTeleport = TeleportType.RANDOM;
|
||||
RestrictWeapons = true;
|
||||
|
||||
ConVarValues["sv_gravity"] = (float)200;
|
||||
ConVarValues["sv_gravity"] = CV_GRAVITY.Value;
|
||||
ConVarValues["sv_infinite_ammo"] = 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using Jailbreak.English.SpecialDay;
|
||||
using Jailbreak.Formatting.Views.SpecialDay;
|
||||
using Jailbreak.Public.Extensions;
|
||||
using Jailbreak.Public.Mod.SpecialDay;
|
||||
using Jailbreak.Public.Mod.SpecialDay.Enums;
|
||||
using Jailbreak.Public.Utils;
|
||||
using Jailbreak.Validator;
|
||||
|
||||
namespace Jailbreak.SpecialDay.SpecialDays;
|
||||
|
||||
@@ -18,6 +21,15 @@ public class OneInTheChamberDay(BasePlugin plugin, IServiceProvider provider)
|
||||
|
||||
public override SpecialDaySettings Settings => new OitcSettings();
|
||||
|
||||
public static readonly FakeConVar<string> CV_WEAPON = new("jb_sd_oitc_weapon",
|
||||
"Weapon to give to players for the day", "weapon_deagle",
|
||||
ConVarFlags.FCVAR_NONE, new ItemValidator(ItemValidator.WeaponType.GUNS));
|
||||
|
||||
public static readonly FakeConVar<string> CV_ADDITIONAL_WEAPON = new(
|
||||
"jb_sd_oitc_additionalweapon",
|
||||
"Additional (non-ammo restricted) weapons to give for the day",
|
||||
"weapon_knife", ConVarFlags.FCVAR_NONE, new ItemValidator());
|
||||
|
||||
public override void Setup() {
|
||||
base.Setup();
|
||||
Plugin.RegisterEventHandler<EventPlayerHurt>(OnPlayerDamage);
|
||||
@@ -29,9 +41,12 @@ public class OneInTheChamberDay(BasePlugin plugin, IServiceProvider provider)
|
||||
|
||||
foreach (var player in PlayerUtil.GetAlive()) {
|
||||
player.RemoveWeapons();
|
||||
player.GiveNamedItem("weapon_knife");
|
||||
player.GiveNamedItem("weapon_deagle");
|
||||
player.GetWeaponBase("weapon_deagle")?.SetAmmo(1, 0);
|
||||
if (CV_ADDITIONAL_WEAPON.Value.Length > 0)
|
||||
player.GiveNamedItem(CV_ADDITIONAL_WEAPON.Value);
|
||||
if (CV_WEAPON.Value.Length > 0) {
|
||||
player.GiveNamedItem(CV_WEAPON.Value);
|
||||
player.GetWeaponBase(CV_WEAPON.Value)?.SetAmmo(1, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +61,7 @@ public class OneInTheChamberDay(BasePlugin plugin, IServiceProvider provider)
|
||||
private HookResult
|
||||
OnPlayerDeath(EventPlayerDeath @event, GameEventInfo info) {
|
||||
if (@event.Attacker == null) return HookResult.Continue;
|
||||
@event.Attacker.GetWeaponBase("weapon_deagle")?.AddBulletsToMagazine(1);
|
||||
@event.Attacker.GetWeaponBase(CV_WEAPON.Value)?.AddBulletsToMagazine(1);
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
@@ -56,19 +71,21 @@ public class OneInTheChamberDay(BasePlugin plugin, IServiceProvider provider)
|
||||
Plugin.DeregisterEventHandler<EventPlayerDeath>(OnPlayerDeath);
|
||||
return base.OnEnd(@event, info);
|
||||
}
|
||||
}
|
||||
|
||||
public class OitcSettings : SpecialDaySettings {
|
||||
public OitcSettings() {
|
||||
CtTeleport = TeleportType.RANDOM;
|
||||
TTeleport = TeleportType.RANDOM;
|
||||
RestrictWeapons = true;
|
||||
WithFriendlyFire();
|
||||
public class OitcSettings : SpecialDaySettings {
|
||||
public OitcSettings() {
|
||||
CtTeleport = TeleportType.RANDOM;
|
||||
TTeleport = TeleportType.RANDOM;
|
||||
RestrictWeapons = true;
|
||||
WithFriendlyFire();
|
||||
|
||||
ConVarValues["mp_death_drop_gun"] = 0;
|
||||
}
|
||||
ConVarValues["mp_death_drop_gun"] = 0;
|
||||
}
|
||||
|
||||
public override ISet<string> AllowedWeapons(CCSPlayerController player) {
|
||||
return new HashSet<string> { "weapon_deagle", "weapon_knife" };
|
||||
public override ISet<string> AllowedWeapons(CCSPlayerController player) {
|
||||
return new HashSet<string> {
|
||||
CV_WEAPON.Value, CV_ADDITIONAL_WEAPON.Value
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,56 +26,56 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
: AbstractSpecialDay(plugin, provider), ISpecialDayMessageProvider {
|
||||
private const int MAX_POINTS = 500;
|
||||
|
||||
public static readonly FakeConVar<int> CvInitialSpeedrunTime =
|
||||
public static readonly FakeConVar<int> CV_INITIAL_SPEEDRUN_TIME =
|
||||
new("css_jb_speedrun_initial_time",
|
||||
"Duration in seconds to grant the speedrunner", 40);
|
||||
|
||||
public static readonly FakeConVar<int> CvFirstRoundFreeze =
|
||||
public static readonly FakeConVar<int> CV_FIRST_ROUND_FREEZE =
|
||||
new("css_jb_speedrun_first_round_freeze",
|
||||
"Duration in seconds to give players time to read the rules of speedrun",
|
||||
6);
|
||||
|
||||
public static readonly FakeConVar<int> CvFreezeTime =
|
||||
public static readonly FakeConVar<int> CV_FREEZE_TIME =
|
||||
new("css_jb_speedrun_freeze_time",
|
||||
"Duration in seconds to freeze players before the speedrun starts", 2);
|
||||
|
||||
public static readonly FakeConVar<int> CvWinTimeBase =
|
||||
public static readonly FakeConVar<int> CV_WIN_TIME_BASE =
|
||||
new("css_jb_speedrun_win_time_base",
|
||||
"Base duration in seconds to give the winner to kill other competitors",
|
||||
25);
|
||||
|
||||
public static readonly FakeConVar<int> CvWinTimeBonus =
|
||||
public static readonly FakeConVar<int> CV_WIN_TIME_BONUS =
|
||||
new("css_jb_speedrun_win_time_bonus",
|
||||
"Bonus duration in seconds to give the winner for every competitor", 5);
|
||||
|
||||
public static readonly FakeConVar<int> CvWinTimeMax =
|
||||
public static readonly FakeConVar<int> CV_WIN_TIME_MAX =
|
||||
new("css_jb_speedrun_win_max",
|
||||
"Max time to give the winner regardless of bonus", 60);
|
||||
|
||||
public static readonly FakeConVar<string> CvWinWeapon = new(
|
||||
public static readonly FakeConVar<string> CV_WIN_WEAPONS = new(
|
||||
"css_jb_speedrun_win_weapons",
|
||||
"Weapon(s) to give to the winner to kill other competitors",
|
||||
"weapon_knife,weapon_negev",
|
||||
customValidators: new WeaponValidator(allowMultiple: true));
|
||||
customValidators: new ItemValidator(allowMultiple: true));
|
||||
|
||||
public static readonly FakeConVar<string> CvLosersWeapon = new(
|
||||
public static readonly FakeConVar<string> CV_LOSERS_WEAPONS = new(
|
||||
"css_jb_speedrun_loser_weapons",
|
||||
"Weapon(s) to give to the losers to use against the winner", "",
|
||||
customValidators: new WeaponValidator(allowMultiple: true));
|
||||
customValidators: new ItemValidator(allowMultiple: true));
|
||||
|
||||
public static readonly FakeConVar<bool> CvWinnerDamageable = new(
|
||||
public static readonly FakeConVar<bool> CV_WINNER_DAMAGEABLE = new(
|
||||
"css_jb_speedrun_winner_damageable", "Whether the winner can be damaged");
|
||||
|
||||
public static readonly FakeConVar<bool> CvLoserDamageable = new(
|
||||
public static readonly FakeConVar<bool> CV_LOSER_DAMAGEABLE = new(
|
||||
"css_jb_speedrun_loser_damageable", "Whether the losers can be damaged",
|
||||
true);
|
||||
|
||||
public static readonly FakeConVar<int> CvTeleportType =
|
||||
public static readonly FakeConVar<int> CV_TELEPORT_TYPE =
|
||||
new("css_jb_speedrun_teleport_type",
|
||||
"0 = Dont teleport at end, 1 = Teleport losers to winner, 2 = Teleport winner to loser(s)",
|
||||
2);
|
||||
|
||||
public static readonly FakeConVar<int> CvMaxPlayersToFinish = new(
|
||||
public static readonly FakeConVar<int> CV_MAX_PLAYERS_TO_FINISH = new(
|
||||
"css_jb_speedrun_finish_at",
|
||||
"Number of players required to declare a winner", 2,
|
||||
customValidators: new RangeValidator<int>(2, 10));
|
||||
@@ -88,7 +88,7 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
/// Positive values represent players who are still alive, and the value
|
||||
/// being the distance they are from the target.
|
||||
/// </summary>
|
||||
private readonly HashSet<int> finishedPlayers = new();
|
||||
private readonly HashSet<int> finishedPlayers = [];
|
||||
|
||||
private readonly Random rng = new();
|
||||
private float? bestTime;
|
||||
@@ -96,7 +96,7 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
|
||||
private AbstractTrail<BeamTrailSegment>? bestTrail;
|
||||
|
||||
private LinkedList<(int, float)> finishTimestampList = new();
|
||||
private LinkedList<(int, float)> finishTimestampList = [];
|
||||
|
||||
private IGenericCmdLocale generics = null!;
|
||||
private int round, playersAliveAtStart;
|
||||
@@ -107,9 +107,9 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
private Vector? start;
|
||||
private Vector? target;
|
||||
private BeamCircle? targetCircle;
|
||||
private ISpeedDayLocale msg => (ISpeedDayLocale)Locale;
|
||||
private ISpeedDayLocale Msg => (ISpeedDayLocale)Locale;
|
||||
|
||||
private bool isRoundActive
|
||||
private bool IsRoundActive
|
||||
=> Provider.GetRequiredService<ISpecialDayManager>().CurrentSD == this;
|
||||
|
||||
public override SDType Type => SDType.SPEEDRUN;
|
||||
@@ -144,7 +144,7 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
player.SetColor(Color.FromArgb(100, 255, 255, 255));
|
||||
}
|
||||
};
|
||||
Timers[CvFirstRoundFreeze.Value - 4] += () => {
|
||||
Timers[CV_FIRST_ROUND_FREEZE.Value - 4] += () => {
|
||||
if (!speedrunner.IsValid || speedrunner.Connected
|
||||
!= PlayerConnectedState.PlayerConnected)
|
||||
speedrunner = getRunner();
|
||||
@@ -153,11 +153,11 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
return;
|
||||
}
|
||||
|
||||
msg.RunnerAssigned(speedrunner).ToAllChat();
|
||||
Msg.RunnerAssigned(speedrunner).ToAllChat();
|
||||
speedrunner.SetColor(Color.DodgerBlue);
|
||||
msg.YouAreRunner(CvInitialSpeedrunTime.Value).ToChat(speedrunner);
|
||||
Msg.YouAreRunner(CV_INITIAL_SPEEDRUN_TIME.Value).ToChat(speedrunner);
|
||||
};
|
||||
Timers[CvFirstRoundFreeze.Value] += () => {
|
||||
Timers[CV_FIRST_ROUND_FREEZE.Value] += () => {
|
||||
if (!speedrunner.IsValid || speedrunner.Connected
|
||||
!= PlayerConnectedState.PlayerConnected) {
|
||||
speedrunner = getRunner();
|
||||
@@ -168,8 +168,8 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
}
|
||||
|
||||
speedrunner.SetColor(Color.DodgerBlue);
|
||||
msg.RunnerLeftAndReassigned(speedrunner).ToAllChat();
|
||||
msg.YouAreRunner(CvInitialSpeedrunTime.Value).ToChat(speedrunner);
|
||||
Msg.RunnerLeftAndReassigned(speedrunner).ToAllChat();
|
||||
Msg.YouAreRunner(CV_INITIAL_SPEEDRUN_TIME.Value).ToChat(speedrunner);
|
||||
}
|
||||
|
||||
start = speedrunner.PlayerPawn.Value!.AbsOrigin!.Clone();
|
||||
@@ -177,8 +177,9 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
bestTrail = createFirstTrail(speedrunner);
|
||||
};
|
||||
|
||||
if (CvInitialSpeedrunTime.Value > 30)
|
||||
Timers[CvInitialSpeedrunTime.Value + CvFirstRoundFreeze.Value - 30] += ()
|
||||
if (CV_INITIAL_SPEEDRUN_TIME.Value > 30)
|
||||
Timers[
|
||||
CV_INITIAL_SPEEDRUN_TIME.Value + CV_FIRST_ROUND_FREEZE.Value - 30] += ()
|
||||
=> {
|
||||
if (target != null) return;
|
||||
if (!speedrunner.IsValid || speedrunner.Connected
|
||||
@@ -190,15 +191,16 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
return;
|
||||
}
|
||||
|
||||
msg.RuntimeLeft(RoundUtil.GetTimeRemaining()).ToChat(speedrunner);
|
||||
Msg.RuntimeLeft(RoundUtil.GetTimeRemaining()).ToChat(speedrunner);
|
||||
};
|
||||
|
||||
Timers[CvInitialSpeedrunTime.Value + CvFirstRoundFreeze.Value - 10] += ()
|
||||
=> {
|
||||
if (target != null) return;
|
||||
msg.RuntimeLeft(RoundUtil.GetTimeRemaining()).ToChat(speedrunner);
|
||||
};
|
||||
Timers[CvInitialSpeedrunTime.Value + CvFirstRoundFreeze.Value] += Execute;
|
||||
Timers[CV_INITIAL_SPEEDRUN_TIME.Value + CV_FIRST_ROUND_FREEZE.Value - 10] +=
|
||||
() => {
|
||||
if (target != null) return;
|
||||
Msg.RuntimeLeft(RoundUtil.GetTimeRemaining()).ToChat(speedrunner);
|
||||
};
|
||||
Timers[CV_INITIAL_SPEEDRUN_TIME.Value + CV_FIRST_ROUND_FREEZE.Value] +=
|
||||
Execute;
|
||||
|
||||
base.Setup();
|
||||
}
|
||||
@@ -234,7 +236,7 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
|
||||
bestTime = timeSpent;
|
||||
|
||||
var minTime = CvInitialSpeedrunTime.Value * 0.5;
|
||||
var minTime = CV_INITIAL_SPEEDRUN_TIME.Value * 0.5;
|
||||
|
||||
startRound((int)Math.Ceiling(Math.Max(timeSpent * 1.1, minTime)));
|
||||
|
||||
@@ -290,8 +292,8 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
|
||||
furthest.Pawn.Value?.Teleport(end);
|
||||
furthest.SetColor(Color.DodgerBlue);
|
||||
msg.RunnerAFKAndReassigned(furthest).ToAllChat();
|
||||
msg.YouAreRunner(RoundUtil.GetTimeRemaining()).ToChat(furthest);
|
||||
Msg.RunnerAFKAndReassigned(furthest).ToAllChat();
|
||||
Msg.YouAreRunner(RoundUtil.GetTimeRemaining()).ToChat(furthest);
|
||||
trail.StartTracking(furthest);
|
||||
trail.DidntMoveTicks = 0;
|
||||
return;
|
||||
@@ -299,7 +301,7 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
|
||||
if (trail.DidntMoveTicks < thresholdTicks) return;
|
||||
if (trail.DidntMoveTicks == thresholdTicks)
|
||||
msg.StayStillToSpeedup.ToChat(trail.Player);
|
||||
Msg.StayStillToSpeedup.ToChat(trail.Player);
|
||||
if (didntMoveSeconds % 2 == 0) RoundUtil.AddTimeRemaining(-1);
|
||||
if (RoundUtil.GetTimeRemaining() <= 0) Execute();
|
||||
};
|
||||
@@ -327,8 +329,8 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
speedrunner = nearest;
|
||||
nearest.Pawn.Value?.Teleport(end);
|
||||
nearest.SetColor(Color.DodgerBlue);
|
||||
msg.RunnerLeftAndReassigned(nearest).ToAllChat();
|
||||
msg.YouAreRunner(RoundUtil.GetTimeRemaining()).ToChat(nearest);
|
||||
Msg.RunnerLeftAndReassigned(nearest).ToAllChat();
|
||||
Msg.YouAreRunner(RoundUtil.GetTimeRemaining()).ToChat(nearest);
|
||||
trail.StartTracking(nearest);
|
||||
};
|
||||
return trail;
|
||||
@@ -344,17 +346,17 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
|
||||
private void startRound(int seconds) {
|
||||
roundStartTime = null;
|
||||
if (!isRoundActive) {
|
||||
if (!IsRoundActive) {
|
||||
panic("Round is not active but we are in startRound");
|
||||
return;
|
||||
}
|
||||
|
||||
var alive = PlayerUtil.GetAlive().ToArray();
|
||||
playersAliveAtStart = PlayerUtil.GetAlive().Count();
|
||||
msg.BeginRound(++round, getEliminations(playersAliveAtStart), seconds)
|
||||
Msg.BeginRound(++round, getEliminations(playersAliveAtStart), seconds)
|
||||
.ToAllChat();
|
||||
|
||||
RoundUtil.SetTimeRemaining(seconds + CvFreezeTime.Value);
|
||||
RoundUtil.SetTimeRemaining(seconds + CV_FREEZE_TIME.Value);
|
||||
|
||||
foreach (var player in alive) {
|
||||
var pawn = player.PlayerPawn.Value;
|
||||
@@ -367,19 +369,19 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
finishedPlayers.Clear();
|
||||
finishTimestampList.Clear();
|
||||
|
||||
Plugin.AddTimer(CvFreezeTime.Value, () => {
|
||||
if (!isRoundActive) return;
|
||||
Plugin.AddTimer(CV_FREEZE_TIME.Value, () => {
|
||||
if (!IsRoundActive) return;
|
||||
foreach (var player in PlayerUtil.GetAlive()) player.UnFreeze();
|
||||
roundStartTime = Server.CurrentTime;
|
||||
}, TimerFlags.STOP_ON_MAPCHANGE);
|
||||
|
||||
roundEndTimer = Plugin.AddTimer(seconds + CvFreezeTime.Value, endRound,
|
||||
roundEndTimer = Plugin.AddTimer(seconds + CV_FREEZE_TIME.Value, endRound,
|
||||
TimerFlags.STOP_ON_MAPCHANGE);
|
||||
}
|
||||
|
||||
private void checkFinishers() {
|
||||
if (target == null || roundStartTime == null) return;
|
||||
if (!isRoundActive) {
|
||||
if (!IsRoundActive) {
|
||||
panic("Round is not active but we are in checkFinishers");
|
||||
return;
|
||||
}
|
||||
@@ -432,9 +434,9 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
foreach (var (slot, dist) in unfinished)
|
||||
finishTimestampList.AddLast((slot, dist));
|
||||
|
||||
const int TOTAL_LINES = 8;
|
||||
var pos = 1;
|
||||
var current = finishTimestampList.First;
|
||||
const int totalLines = 8;
|
||||
var pos = 1;
|
||||
var current = finishTimestampList.First;
|
||||
|
||||
string? top = null;
|
||||
while (current != null) {
|
||||
@@ -451,14 +453,14 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
var playerLine = current;
|
||||
|
||||
var d = 0;
|
||||
while (playerLine != null && (display < TOTAL_LINES / 2
|
||||
|| pos - d > finishTimestampList.Count - TOTAL_LINES
|
||||
&& display < TOTAL_LINES)) {
|
||||
while (playerLine != null && (display < totalLines / 2
|
||||
|| pos - d > finishTimestampList.Count - totalLines
|
||||
&& display < totalLines)) {
|
||||
var (slot, dist) = playerLine.Value;
|
||||
playerLine = playerLine.Previous;
|
||||
var p = Utilities.GetPlayerFromSlot(slot);
|
||||
if (p == null) continue;
|
||||
lines = generateHTMLLine(p, pos - d++, dist) + (d == 1 ? "" : "<br>")
|
||||
lines = generateHtmlLine(p, pos - d++, dist) + (d == 1 ? "" : "<br>")
|
||||
+ lines;
|
||||
display++;
|
||||
}
|
||||
@@ -467,12 +469,12 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
playerLine = current;
|
||||
|
||||
d = 0;
|
||||
while (playerLine != null && display < TOTAL_LINES) {
|
||||
while (playerLine != null && display < totalLines) {
|
||||
var (slot, dist) = playerLine.Value;
|
||||
playerLine = playerLine.Next;
|
||||
var p = Utilities.GetPlayerFromSlot(slot);
|
||||
if (p == null) continue;
|
||||
lines += "<br>" + generateHTMLLine(p, pos + ++d, dist);
|
||||
lines += "<br>" + generateHtmlLine(p, pos + ++d, dist);
|
||||
display++;
|
||||
}
|
||||
|
||||
@@ -488,7 +490,7 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
finishTimestampList = originalCompletions;
|
||||
}
|
||||
|
||||
private string generateHTMLLine(CCSPlayerController player, int position,
|
||||
private string generateHtmlLine(CCSPlayerController player, int position,
|
||||
float distance) {
|
||||
string color;
|
||||
var eliminations = getEliminations(playersAliveAtStart);
|
||||
@@ -541,10 +543,10 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
if (bestTime == null || time < bestTime) {
|
||||
bestTime = time;
|
||||
bestTimePlayerSlot = player.Slot;
|
||||
msg.BestTime(player, time).ToAllChat();
|
||||
Msg.BestTime(player, time).ToAllChat();
|
||||
player.SetColor(Color.FromArgb(255, Color.Gold));
|
||||
} else {
|
||||
msg.PlayerTime(player, finishedPlayers.Count + 1, -time).ToAllChat();
|
||||
Msg.PlayerTime(player, finishedPlayers.Count + 1, -time).ToAllChat();
|
||||
}
|
||||
|
||||
finishTimestampList.AddLast((player.Slot, -Server.CurrentTime));
|
||||
@@ -605,7 +607,7 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isRoundActive) {
|
||||
if (!IsRoundActive) {
|
||||
panic("Round is not active but we are in endRound");
|
||||
return;
|
||||
}
|
||||
@@ -642,7 +644,7 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
|
||||
if (random != null && activeTrails.TryGetValue(random.Slot,
|
||||
out var randomTrail)) {
|
||||
msg.ImpossibleLocation(
|
||||
Msg.ImpossibleLocation(
|
||||
ctMade ? CsTeam.Terrorist : CsTeam.CounterTerrorist, random);
|
||||
|
||||
bestTrail?.Kill();
|
||||
@@ -657,7 +659,7 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
|
||||
announceTimes();
|
||||
|
||||
if (aliveCount <= CvMaxPlayersToFinish.Value) {
|
||||
if (aliveCount <= CV_MAX_PLAYERS_TO_FINISH.Value) {
|
||||
if (finishTimestampList.Count == 0) {
|
||||
generics.Error("No slowest times found").ToAllChat();
|
||||
return;
|
||||
@@ -678,29 +680,30 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
.Where(p => p.Slot != winner.Slot)
|
||||
.ToList();
|
||||
|
||||
var timeToSet = CvWinTimeBase.Value + CvWinTimeBonus.Value * losers.Count;
|
||||
var timeToSet = CV_WIN_TIME_BASE.Value
|
||||
+ CV_WIN_TIME_BONUS.Value * losers.Count;
|
||||
|
||||
msg.PlayerWon(winner).ToAllChat();
|
||||
Msg.PlayerWon(winner).ToAllChat();
|
||||
|
||||
foreach (var loser in losers) {
|
||||
loser.SetColor(Color.FromArgb(254, Color.White));
|
||||
if (CvTeleportType.Value == 1)
|
||||
if (CV_TELEPORT_TYPE.Value == 1)
|
||||
loser.Teleport(winner);
|
||||
else if (CvTeleportType.Value == 2) winner.Teleport(loser);
|
||||
if (CvLoserDamageable.Value) EnableDamage(loser);
|
||||
else if (CV_TELEPORT_TYPE.Value == 2) winner.Teleport(loser);
|
||||
if (CV_LOSER_DAMAGEABLE.Value) EnableDamage(loser);
|
||||
}
|
||||
|
||||
if (CvWinnerDamageable.Value) EnableDamage(winner);
|
||||
if (CV_WINNER_DAMAGEABLE.Value) EnableDamage(winner);
|
||||
|
||||
foreach (var weapon in CvLosersWeapon.Value.Split(','))
|
||||
foreach (var weapon in CV_LOSERS_WEAPONS.Value.Split(','))
|
||||
foreach (var loser in losers)
|
||||
loser.GiveNamedItem(weapon);
|
||||
|
||||
foreach (var weapon in CvWinWeapon.Value.Split(','))
|
||||
foreach (var weapon in CV_WIN_WEAPONS.Value.Split(','))
|
||||
winner.GiveNamedItem(weapon);
|
||||
|
||||
Plugin.RemoveListener<Listeners.OnTick>(checkFinishers);
|
||||
RoundUtil.SetTimeRemaining(Math.Min(timeToSet, CvWinTimeMax.Value));
|
||||
RoundUtil.SetTimeRemaining(Math.Min(timeToSet, CV_WIN_TIME_MAX.Value));
|
||||
Server.ExecuteCommand("mp_ignore_round_win_conditions 0");
|
||||
return;
|
||||
}
|
||||
@@ -709,7 +712,7 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
var nextRoundTime = (int)Math.Ceiling((bestTime ?? 20) + 10 - round * 1.5);
|
||||
|
||||
if (toEliminate <= 0) {
|
||||
msg.NoneEliminated.ToAllChat();
|
||||
Msg.NoneEliminated.ToAllChat();
|
||||
Plugin.AddTimer(3f, () => { startRound(nextRoundTime); },
|
||||
TimerFlags.STOP_ON_MAPCHANGE);
|
||||
return;
|
||||
@@ -750,7 +753,7 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
if (player == null || !player.IsValid) continue;
|
||||
EnableDamage(player);
|
||||
player.CommitSuicide(false, true);
|
||||
msg.PlayerEliminated(player).ToAllChat();
|
||||
Msg.PlayerEliminated(player).ToAllChat();
|
||||
}
|
||||
|
||||
Plugin.AddTimer(3f, () => { startRound(nextRoundTime); },
|
||||
@@ -760,7 +763,7 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
private void eliminatePlayer(CCSPlayerController player) {
|
||||
EnableDamage(player);
|
||||
player.CommitSuicide(false, true);
|
||||
msg.PlayerEliminated(player).ToAllChat();
|
||||
Msg.PlayerEliminated(player).ToAllChat();
|
||||
}
|
||||
|
||||
private void panic(string reason) {
|
||||
@@ -794,7 +797,7 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
var player = Utilities.GetPlayerFromSlot(slot);
|
||||
slowest = slowest.Previous;
|
||||
if (player == null) continue;
|
||||
msg.PlayerTime(player, position--, dist).ToChat(player);
|
||||
Msg.PlayerTime(player, position--, dist).ToChat(player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -825,7 +828,7 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
}
|
||||
|
||||
public override Func<int> RoundTime
|
||||
=> () => CvInitialSpeedrunTime.Value + CvFirstRoundFreeze.Value;
|
||||
=> () => CV_INITIAL_SPEEDRUN_TIME.Value + CV_FIRST_ROUND_FREEZE.Value;
|
||||
|
||||
public override ISet<string> AllowedWeapons(CCSPlayerController player) {
|
||||
// Return empty set to allow no weapons
|
||||
@@ -833,7 +836,7 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
|
||||
}
|
||||
|
||||
public override float FreezeTime(CCSPlayerController player) {
|
||||
return CvFirstRoundFreeze.Value;
|
||||
return CV_FIRST_ROUND_FREEZE.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,13 @@ using Jailbreak.Formatting.Views.Warden;
|
||||
using Jailbreak.Public.Behaviors;
|
||||
using Jailbreak.Public.Mod.Warden;
|
||||
using Jailbreak.Public.Utils;
|
||||
using Jailbreak.Warden.Global;
|
||||
|
||||
namespace Jailbreak.Warden.Commands;
|
||||
|
||||
public class WardenCommandsBehavior(IWardenLocale locale,
|
||||
IWardenSelectionService queue, IWardenService warden,
|
||||
IGenericCmdLocale generics, WardenConfig config) : IPluginBehavior {
|
||||
IGenericCmdLocale generics) : IPluginBehavior {
|
||||
private readonly Dictionary<CCSPlayerController, DateTime> lastPassCommand =
|
||||
new();
|
||||
|
||||
@@ -39,7 +40,7 @@ public class WardenCommandsBehavior(IWardenLocale locale,
|
||||
// GetPlayers() returns valid players, no need to error check here.
|
||||
foreach (var clients in Utilities.GetPlayers())
|
||||
clients.ExecuteClientCommand(
|
||||
$"play sounds/{config.WardenPassedSoundName}");
|
||||
$"play sounds/{WardenBehavior.CV_WARDEN_SOUND_PASSED.Value}");
|
||||
|
||||
locale.BecomeNextWarden.ToAllChat();
|
||||
|
||||
@@ -71,7 +72,7 @@ public class WardenCommandsBehavior(IWardenLocale locale,
|
||||
locale.FireWarden(warden.Warden).ToChat(client);
|
||||
|
||||
client.ExecuteClientCommand(
|
||||
$"play sounds/{config.WardenPassedSoundName}");
|
||||
$"play sounds/{WardenBehavior.CV_WARDEN_SOUND_PASSED.Value}");
|
||||
}
|
||||
|
||||
locale.BecomeNextWarden.ToAllChat();
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Jailbreak.Warden.Commands;
|
||||
public class WardenOpenCommandsBehavior(IWardenService warden,
|
||||
IWardenLocale msg, IWardenCmdOpenLocale wardenCmdOpenMsg,
|
||||
IZoneManager zoneManager) : IPluginBehavior, IWardenOpenCommand {
|
||||
public static readonly FakeConVar<int> CvOpenCommandCooldown = new(
|
||||
public static readonly FakeConVar<int> CV_OPEN_COMMAND_COOLDOWN = new(
|
||||
"css_jb_warden_open_cooldown",
|
||||
"Minimum seconds warden must wait before being able to open the cells.", 30,
|
||||
customValidators: new RangeValidator<int>(0, 300));
|
||||
@@ -40,8 +40,8 @@ public class WardenOpenCommandsBehavior(IWardenService warden,
|
||||
return;
|
||||
}
|
||||
|
||||
if (RoundUtil.GetTimeElapsed() < CvOpenCommandCooldown.Value) {
|
||||
wardenCmdOpenMsg.CannotOpenYet(CvOpenCommandCooldown.Value)
|
||||
if (RoundUtil.GetTimeElapsed() < CV_OPEN_COMMAND_COOLDOWN.Value) {
|
||||
wardenCmdOpenMsg.CannotOpenYet(CV_OPEN_COMMAND_COOLDOWN.Value)
|
||||
.ToChat(executor);
|
||||
return;
|
||||
}
|
||||
@@ -56,9 +56,10 @@ public class WardenOpenCommandsBehavior(IWardenService warden,
|
||||
var result = MapUtil.OpenCells(zoneManager);
|
||||
IView message;
|
||||
if (result) {
|
||||
if (executor != null && !warden.IsWarden(executor)) {
|
||||
if (executor != null && !warden.IsWarden(executor))
|
||||
message = wardenCmdOpenMsg.CellsOpenedBy(executor);
|
||||
} else { message = wardenCmdOpenMsg.CellsOpenedBy(null); }
|
||||
else
|
||||
message = wardenCmdOpenMsg.CellsOpenedBy(null);
|
||||
} else { message = wardenCmdOpenMsg.OpeningFailed; }
|
||||
|
||||
message.ToAllChat();
|
||||
|
||||
@@ -36,39 +36,54 @@ public struct PreWardenStats(int armorValue, int health, int maxHealth,
|
||||
public class WardenBehavior(ILogger<WardenBehavior> logger,
|
||||
IWardenLocale locale, IRichLogService logs,
|
||||
ISpecialTreatmentService specialTreatment, IRebelService rebels,
|
||||
WardenConfig config, IMuteService mute, IServiceProvider provider)
|
||||
IMuteService mute, IServiceProvider provider)
|
||||
: IPluginBehavior, IWardenService {
|
||||
private readonly ISet<CCSPlayerController> bluePrisoners =
|
||||
new HashSet<CCSPlayerController>();
|
||||
|
||||
public readonly FakeConVar<int> CvArmorEqual = new("css_jb_hp_outnumbered",
|
||||
"Health points for CTs have equal balance", 50, ConVarFlags.FCVAR_NONE,
|
||||
new RangeValidator<int>(1, 200));
|
||||
public static readonly FakeConVar<int> CV_ARMOR_EQUAL =
|
||||
new("css_jb_hp_outnumbered", "Health points for CTs have equal balance", 50,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(1, 200));
|
||||
|
||||
public readonly FakeConVar<int> CvArmorOutnumber = new("css_jb_hp_outnumber",
|
||||
"HP for CTs when outnumbering Ts", 25, ConVarFlags.FCVAR_NONE,
|
||||
new RangeValidator<int>(1, 200));
|
||||
public static readonly FakeConVar<int> CV_ARMOR_OUTNUMBER =
|
||||
new("css_jb_hp_outnumber", "HP for CTs when outnumbering Ts", 25,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(1, 200));
|
||||
|
||||
public readonly FakeConVar<int> CvArmorOutnumbered =
|
||||
public static readonly FakeConVar<int> CV_ARMOR_OUTNUMBERED =
|
||||
new("css_jb_hp_outnumbered", "Health points for CTs when outnumbered by Ts",
|
||||
100, ConVarFlags.FCVAR_NONE, new RangeValidator<int>(1, 200));
|
||||
|
||||
public readonly FakeConVar<int> CvWardenArmor = new("css_jb_warden_armor",
|
||||
"Armor for the warden", 125, ConVarFlags.FCVAR_NONE,
|
||||
new RangeValidator<int>(1, 200));
|
||||
public static readonly FakeConVar<int> CV_WARDEN_ARMOR =
|
||||
new("css_jb_warden_armor", "Armor for the warden", 125,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(1, 200));
|
||||
|
||||
public readonly FakeConVar<int> CvWardenHealth = new("css_jb_warden_hp",
|
||||
"HP for the warden", 125, ConVarFlags.FCVAR_NONE,
|
||||
new RangeValidator<int>(1, 200));
|
||||
|
||||
public readonly FakeConVar<int> CvWardenMaxHealth = new("css_jb_warden_maxhp",
|
||||
"Max HP for the warden", 100, ConVarFlags.FCVAR_NONE,
|
||||
new RangeValidator<int>(1, 200));
|
||||
|
||||
public readonly FakeConVar<int> CvWardenAutoOpenCells =
|
||||
public static readonly FakeConVar<int> CV_WARDEN_AUTO_OPEN_CELLS =
|
||||
new("css_jb_warden_opencells_delay",
|
||||
"Delay in seconds to auto-open cells at, -1 to disable", 60);
|
||||
|
||||
public static readonly FakeConVar<int> CV_WARDEN_HEALTH =
|
||||
new("css_jb_warden_hp", "HP for the warden", 125, ConVarFlags.FCVAR_NONE,
|
||||
new RangeValidator<int>(1, 200));
|
||||
|
||||
public static readonly FakeConVar<int> CV_WARDEN_MAX_HEALTH =
|
||||
new("css_jb_warden_maxhp", "Max HP for the warden", 100,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(1, 200));
|
||||
|
||||
public static readonly FakeConVar<string> CV_WARDEN_SOUND_KILLED =
|
||||
new("css_jb_warden_sound_killed", "Sound to play when the warden is killed",
|
||||
"wardenKilled");
|
||||
|
||||
public static readonly FakeConVar<string> CV_WARDEN_SOUND_PASSED =
|
||||
new("css_jb_warden_sound_killed", "Sound to play when the warden passes",
|
||||
"wardenPassed");
|
||||
|
||||
public static readonly FakeConVar<string> CV_WARDEN_SOUND_NEW =
|
||||
new("css_jb_warden_sound_killed",
|
||||
"Sound to play when the warden is assigned", "wardenNew");
|
||||
|
||||
public static readonly FakeConVar<int> CV_WARDEN_TERRORIST_RATIO =
|
||||
new("css_jb_warden_t_ratio", "Ratio of T:CT to use for HP adjustments", 3);
|
||||
|
||||
private bool firstWarden;
|
||||
private string? oldTag;
|
||||
private char? oldTagColor;
|
||||
@@ -133,7 +148,7 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
|
||||
}
|
||||
|
||||
foreach (var player in Utilities.GetPlayers())
|
||||
player.ExecuteClientCommand($"play sounds/{config.WardenNewSoundName}");
|
||||
player.ExecuteClientCommand($"play sounds/{CV_WARDEN_SOUND_NEW.Value}");
|
||||
|
||||
logs.Append(logs.Player(Warden), "is now the warden.");
|
||||
|
||||
@@ -157,10 +172,10 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
|
||||
if (!hasHelmet) Warden.GiveNamedItem("item_assaultsuit");
|
||||
|
||||
var ctArmorValue = getBalance() switch {
|
||||
0 => CvArmorEqual.Value, // Balanced teams
|
||||
1 => CvArmorOutnumbered.Value, // Ts outnumber CTs
|
||||
-1 => CvArmorOutnumber.Value, // CTs outnumber Ts
|
||||
_ => CvArmorEqual.Value // default (should never happen)
|
||||
0 => CV_ARMOR_EQUAL.Value, // Balanced teams
|
||||
1 => CV_ARMOR_OUTNUMBERED.Value, // Ts outnumber CTs
|
||||
-1 => CV_ARMOR_OUTNUMBER.Value, // CTs outnumber Ts
|
||||
_ => CV_ARMOR_EQUAL.Value // default (should never happen)
|
||||
};
|
||||
|
||||
/* Round start CT buff */
|
||||
@@ -173,8 +188,8 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
|
||||
Utilities.SetStateChanged(guardPawn, "CCSPlayerPawn", "m_ArmorValue");
|
||||
}
|
||||
|
||||
setWardenStats(wardenPawn, CvWardenArmor.Value, CvWardenHealth.Value,
|
||||
CvWardenMaxHealth.Value);
|
||||
setWardenStats(wardenPawn, CV_WARDEN_ARMOR.Value, CV_WARDEN_HEALTH.Value,
|
||||
CV_WARDEN_MAX_HEALTH.Value);
|
||||
if (!hasHealthshot) Warden.GiveNamedItem("weapon_healthshot");
|
||||
} else { preWardenStats = null; }
|
||||
|
||||
@@ -282,7 +297,7 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
|
||||
foreach (var player in Utilities.GetPlayers()) {
|
||||
if (!player.IsReal()) continue;
|
||||
player.ExecuteClientCommand(
|
||||
$"play sounds/{config.WardenKilledSoundName}");
|
||||
$"play sounds/{CV_WARDEN_SOUND_KILLED.Value}");
|
||||
}
|
||||
|
||||
locale.BecomeNextWarden.ToAllChat();
|
||||
@@ -333,7 +348,7 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
|
||||
var tCount = Utilities.GetPlayers().Count(p => p.Team == CsTeam.Terrorist);
|
||||
|
||||
// Casting to a float ensures if we're diving by zero, we get infinity instead of an error.
|
||||
var ratio = (float)tCount / config.TerroristRatio - ctCount;
|
||||
var ratio = (float)tCount / CV_WARDEN_TERRORIST_RATIO.Value - ctCount;
|
||||
|
||||
return ratio switch {
|
||||
> 0 => 1,
|
||||
@@ -403,14 +418,14 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
|
||||
firstWarden = true;
|
||||
preWardenStats = null;
|
||||
|
||||
if (CvWardenAutoOpenCells.Value < 0 || RoundUtil.IsWarmup())
|
||||
if (CV_WARDEN_AUTO_OPEN_CELLS.Value < 0 || RoundUtil.IsWarmup())
|
||||
return HookResult.Continue;
|
||||
var openCmd = provider.GetService<IWardenOpenCommand>();
|
||||
if (openCmd == null) return HookResult.Continue;
|
||||
var cmdLocale = provider.GetRequiredService<IWardenCmdOpenLocale>();
|
||||
|
||||
openCellsTimer?.Kill();
|
||||
openCellsTimer = parent.AddTimer(CvWardenAutoOpenCells.Value, () => {
|
||||
openCellsTimer = parent.AddTimer(CV_WARDEN_AUTO_OPEN_CELLS.Value, () => {
|
||||
if (openCmd.OpenedCells) return;
|
||||
var zone = provider.GetService<IZoneManager>();
|
||||
if (zone != null)
|
||||
@@ -436,7 +451,7 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
|
||||
|
||||
foreach (var player in Utilities.GetPlayers())
|
||||
player.ExecuteClientCommand(
|
||||
$"play sounds/{config.WardenPassedSoundName}");
|
||||
$"play sounds/{CV_WARDEN_SOUND_PASSED.Value}");
|
||||
|
||||
locale.BecomeNextWarden.ToAllChat();
|
||||
return HookResult.Continue;
|
||||
|
||||
@@ -13,13 +13,13 @@ using MStatsShared;
|
||||
namespace Jailbreak.Warden.Markers;
|
||||
|
||||
public class WardenMarkerBehavior(IWardenService warden) : IPluginBehavior {
|
||||
public readonly FakeConVar<float> CvMaxRadius = new(
|
||||
public static readonly FakeConVar<float> CV_MAX_RADIUS = new(
|
||||
"css_jb_warden_marker_max_radius", "Maximum radius for warden marker", 360);
|
||||
|
||||
public readonly FakeConVar<float> CvMinRadius = new(
|
||||
public static readonly FakeConVar<float> CV_MIN_RADIUS = new(
|
||||
"css_jb_warden_marker_min_radius", "Minimum radius for warden marker", 60);
|
||||
|
||||
public readonly FakeConVar<long> CvResizeTime = new(
|
||||
public static readonly FakeConVar<long> CV_RESIZE_TIME = new(
|
||||
"css_jb_warden_resize_time", "Milliseconds to wait for resizing marker",
|
||||
800);
|
||||
|
||||
@@ -30,7 +30,7 @@ public class WardenMarkerBehavior(IWardenService warden) : IPluginBehavior {
|
||||
private float radius;
|
||||
|
||||
public void Start(BasePlugin basePlugin) {
|
||||
marker = new BeamCircle(basePlugin, new Vector(), CvMinRadius.Value,
|
||||
marker = new BeamCircle(basePlugin, new Vector(), CV_MIN_RADIUS.Value,
|
||||
(int)Math.PI * 15);
|
||||
basePlugin.AddCommandListener("player_ping", CommandListener_PlayerPing);
|
||||
}
|
||||
@@ -46,9 +46,9 @@ public class WardenMarkerBehavior(IWardenService warden) : IPluginBehavior {
|
||||
var distance = currentPos.Distance(vec);
|
||||
var timeElapsed = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
|
||||
- placementTime;
|
||||
if (timeElapsed < CvResizeTime.Value) {
|
||||
if (distance <= CvMaxRadius.Value * 1.3) {
|
||||
distance = Math.Clamp(distance, CvMinRadius.Value, CvMaxRadius.Value);
|
||||
if (timeElapsed < CV_RESIZE_TIME.Value) {
|
||||
if (distance <= CV_MAX_RADIUS.Value * 1.3) {
|
||||
distance = Math.Clamp(distance, CV_MIN_RADIUS.Value, CV_MAX_RADIUS.Value);
|
||||
marker?.SetRadius(distance);
|
||||
marker?.Update();
|
||||
radius = distance;
|
||||
@@ -60,7 +60,7 @@ public class WardenMarkerBehavior(IWardenService warden) : IPluginBehavior {
|
||||
}
|
||||
}
|
||||
|
||||
radius = CvMinRadius.Value;
|
||||
radius = CV_MIN_RADIUS.Value;
|
||||
currentPos = vec;
|
||||
placementTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Jailbreak.Warden;
|
||||
|
||||
public class WardenConfig {
|
||||
public string WardenKilledSoundName { get; } = "wardenKilled";
|
||||
public string WardenPassedSoundName { get; } = "wardenPassed";
|
||||
public string WardenNewSoundName { get; } = "wardenNew";
|
||||
public int TerroristRatio { get; } = 3;
|
||||
}
|
||||
@@ -13,13 +13,13 @@ namespace Jailbreak.Warden;
|
||||
public static class WardenServiceExtension {
|
||||
public static void AddJailbreakWarden(
|
||||
this IServiceCollection serviceCollection) {
|
||||
serviceCollection.AddConfig<WardenConfig>("warden");
|
||||
serviceCollection.AddPluginBehavior<IWardenService, WardenBehavior>();
|
||||
serviceCollection
|
||||
.AddPluginBehavior<IWardenSelectionService, WardenSelectionBehavior>();
|
||||
serviceCollection
|
||||
.AddPluginBehavior<ISpecialTreatmentService, SpecialTreatmentBehavior>();
|
||||
serviceCollection.AddPluginBehavior<IWardenOpenCommand, WardenOpenCommandsBehavior>();
|
||||
serviceCollection
|
||||
.AddPluginBehavior<IWardenOpenCommand, WardenOpenCommandsBehavior>();
|
||||
|
||||
serviceCollection.AddPluginBehavior<SpecialTreatmentCommandsBehavior>();
|
||||
serviceCollection.AddPluginBehavior<PeaceCommandsBehavior>();
|
||||
|
||||
@@ -8,10 +8,10 @@ using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
|
||||
namespace Jailbreak.Zones;
|
||||
|
||||
public class SqlZoneManager(IZoneFactory factory) : IZoneManager {
|
||||
public static readonly FakeConVar<string> CvSqlTable = new("css_jb_zonetable",
|
||||
public static readonly FakeConVar<string> CV_SQL_TABLE = new("css_jb_zonetable",
|
||||
"The table name for the zones", "cs2_jb_zones");
|
||||
|
||||
public static readonly FakeConVar<string> CvSqlConnectionString =
|
||||
public static readonly FakeConVar<string> CV_SQL_CONNECTION_STRING =
|
||||
new("css_jb_sqlconnection", "The connection string for the database", "",
|
||||
ConVarFlags.FCVAR_PROTECTED);
|
||||
|
||||
@@ -22,7 +22,7 @@ public class SqlZoneManager(IZoneFactory factory) : IZoneManager {
|
||||
public void Start(BasePlugin basePlugin) {
|
||||
plugin = basePlugin;
|
||||
|
||||
CvSqlConnectionString.ValueChanged += async (_, _) => {
|
||||
CV_SQL_CONNECTION_STRING.ValueChanged += async (_, _) => {
|
||||
await LoadZones(Server.MapName);
|
||||
};
|
||||
|
||||
@@ -56,7 +56,7 @@ public class SqlZoneManager(IZoneFactory factory) : IZoneManager {
|
||||
await conn.OpenAsync();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = $"""
|
||||
DELETE FROM {CvSqlTable.Value.Trim('"')}
|
||||
DELETE FROM {CV_SQL_TABLE.Value.Trim('"')}
|
||||
WHERE zoneid = @zoneid
|
||||
AND map = @map
|
||||
""";
|
||||
@@ -91,7 +91,7 @@ public class SqlZoneManager(IZoneFactory factory) : IZoneManager {
|
||||
await conn.OpenAsync();
|
||||
|
||||
var insertPointCommand = $"""
|
||||
INSERT INTO {CvSqlTable.Value.Trim('"')} (map, type, zoneid, pointid, X, Y, Z)
|
||||
INSERT INTO {CV_SQL_TABLE.Value.Trim('"')} (map, type, zoneid, pointid, X, Y, Z)
|
||||
VALUES (@map, @type, @zoneid, @pointid, @X, @Y, @Z)
|
||||
""";
|
||||
var pointId = 0;
|
||||
@@ -160,7 +160,7 @@ public class SqlZoneManager(IZoneFactory factory) : IZoneManager {
|
||||
await conn.OpenAsync();
|
||||
|
||||
var cmdText = $"""
|
||||
CREATE TABLE IF NOT EXISTS {CvSqlTable.Value.Trim('"')}(
|
||||
CREATE TABLE IF NOT EXISTS {CV_SQL_TABLE.Value.Trim('"')}(
|
||||
zoneid INT NOT NULL,
|
||||
pointid INT NOT NULL,
|
||||
map VARCHAR(64) NOT NULL,
|
||||
@@ -188,7 +188,7 @@ public class SqlZoneManager(IZoneFactory factory) : IZoneManager {
|
||||
private MySqlCommand queryAllZones(MySqlConnection conn, string map,
|
||||
ZoneType type) {
|
||||
var cmdText = $"""
|
||||
SELECT * FROM {CvSqlTable.Value.Trim('"')}
|
||||
SELECT * FROM {CV_SQL_TABLE.Value.Trim('"')}
|
||||
WHERE map = '{map}' AND type = '{type}'
|
||||
ORDER BY zoneid, pointid DESC
|
||||
""";
|
||||
@@ -260,7 +260,7 @@ public class SqlZoneManager(IZoneFactory factory) : IZoneManager {
|
||||
}
|
||||
|
||||
private MySqlConnection? createConnection() {
|
||||
var str = CvSqlConnectionString.Value.Trim('"');
|
||||
var str = CV_SQL_CONNECTION_STRING.Value.Trim('"');
|
||||
return string.IsNullOrWhiteSpace(str) ? null : new MySqlConnection(str);
|
||||
}
|
||||
}
|
||||
@@ -5,19 +5,20 @@ namespace Jailbreak.Formatting.Views.Warden;
|
||||
|
||||
public interface IWardenCmdOpenLocale {
|
||||
/// <summary>
|
||||
/// The cells were auto-opened.
|
||||
/// The cells were auto-opened.
|
||||
/// </summary>
|
||||
public IView CellsOpened { get; }
|
||||
|
||||
public IView OpeningFailed { get; }
|
||||
public IView AlreadyOpened { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The cells were opened by the specified player.
|
||||
/// If the player is null, the cells were opened by the warden.
|
||||
/// The cells were opened by the specified player.
|
||||
/// If the player is null, the cells were opened by the warden.
|
||||
/// </summary>
|
||||
/// <param name="player"></param>
|
||||
/// <returns></returns>
|
||||
public IView CellsOpenedBy(CCSPlayerController? player);
|
||||
|
||||
public IView OpeningFailed { get; }
|
||||
public IView AlreadyOpened { get; }
|
||||
public IView CannotOpenYet(int seconds);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace Jailbreak.Public.Configuration;
|
||||
|
||||
public interface IConfigService {
|
||||
public const string CONFIG_PATH = "jailbreak.json";
|
||||
|
||||
/// <summary>
|
||||
/// Get the configuration object with the provided name
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="failOnDefault">If the configuration service would return the default value, fail instead. Loudly.</param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
T Get<T>(string path, bool failOnDefault = false) where T : class, new();
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
namespace Jailbreak.Public.Extensions;
|
||||
|
||||
public static class CollectionExtensions {
|
||||
private static readonly Random Random = new();
|
||||
private static readonly Random RANDOM = new();
|
||||
|
||||
public static void Shuffle<T>(this IList<T> list) {
|
||||
var n = list.Count;
|
||||
while (n > 1) {
|
||||
n--;
|
||||
var k = Random.Next(n + 1);
|
||||
var k = RANDOM.Next(n + 1);
|
||||
(list[k], list[n]) = (list[n], list[k]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Jailbreak.Public.Behaviors;
|
||||
using Jailbreak.Public.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Jailbreak.Public.Extensions;
|
||||
@@ -44,24 +43,4 @@ public static class ServiceCollectionExtensions {
|
||||
collection.AddTransient<IPluginBehavior, TExtension>(provider
|
||||
=> provider.GetRequiredService<TExtension>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an object to be loaded from the configuration file
|
||||
/// </summary>
|
||||
/// <param name="collection"></param>
|
||||
/// <param name="sectionName">The section where the configuration object will be loaded from</param>
|
||||
/// <typeparam name="TConfig">The configuration object. Must auto-fill all default values!</typeparam>
|
||||
[Obsolete(
|
||||
"Conguration is up to each module, and should ideally be done through CVars")]
|
||||
public static void AddConfig<TConfig>(this IServiceCollection collection,
|
||||
string sectionName) where TConfig : class, new() {
|
||||
// Get the object by resolving IConfigService
|
||||
// and use the Get<T>() method.
|
||||
|
||||
// Not *really* important... but do we want to fail here or return default if section
|
||||
// isn't available?
|
||||
collection.AddTransient<TConfig>(provider => provider
|
||||
.GetRequiredService<IConfigService>()
|
||||
.Get<TConfig>(sectionName));
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,7 @@ public abstract class AbstractSpecialDay(BasePlugin plugin,
|
||||
/// know why you are not calling it.
|
||||
/// </summary>
|
||||
public virtual void Setup() {
|
||||
Plugin.RegisterFakeConVars(this);
|
||||
Plugin.RegisterEventHandler<EventRoundEnd>(OnEnd);
|
||||
|
||||
foreach (var entry in Settings.ConVarValues) {
|
||||
|
||||
19
public/Jailbreak.Public/Utils/TeamUtil.cs
Normal file
19
public/Jailbreak.Public/Utils/TeamUtil.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace Jailbreak.Public.Utils;
|
||||
|
||||
public static class TeamUtil {
|
||||
public static CsTeam? FromString(string team) {
|
||||
return team.ToLower() switch {
|
||||
"0" or "n" or "none" => CsTeam.None,
|
||||
"1" or "s" or "spec" or "spectator" or "spectators" or "specs" => CsTeam
|
||||
.Spectator,
|
||||
"2" or "t" or "ts" or "terror" or "terrorist" or "terrorists"
|
||||
or "prisoner" or "prisoners" => CsTeam.Terrorist,
|
||||
"3" or "ct" or "cts" or "counter" or "counterterrorist"
|
||||
or "counterterrorists" or "guard"
|
||||
or "guards" => CsTeam.CounterTerrorist,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
}
|
||||
70
public/Jailbreak.Validator/ItemValidator.cs
Normal file
70
public/Jailbreak.Validator/ItemValidator.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
|
||||
namespace Jailbreak.Validator;
|
||||
|
||||
public class ItemValidator(
|
||||
ItemValidator.WeaponType type = ItemValidator.WeaponType.WEAPON
|
||||
| ItemValidator.WeaponType.UTILITY, bool allowEmpty = true,
|
||||
bool allowMultiple = false) : IValidator<string> {
|
||||
[Flags]
|
||||
public enum WeaponType {
|
||||
GRENADE,
|
||||
UTILITY,
|
||||
WEAPON,
|
||||
SNIPERS,
|
||||
RIFLES,
|
||||
PISTOLS,
|
||||
SHOTGUNS,
|
||||
SMGS,
|
||||
HEAVY,
|
||||
GUNS
|
||||
}
|
||||
|
||||
public bool Validate(string value, out string? errorMessage) {
|
||||
if (value.Contains(',') && !allowMultiple) {
|
||||
errorMessage = "Value cannot contain multiple values";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(value)) {
|
||||
errorMessage = allowEmpty ? null : "weapon cannot be empty";
|
||||
return allowEmpty;
|
||||
}
|
||||
|
||||
foreach (var weapon in value.Split(',')) {
|
||||
if (string.IsNullOrWhiteSpace(weapon)) {
|
||||
if (!allowEmpty) {
|
||||
errorMessage = allowEmpty ? null : "weapon cannot be empty";
|
||||
return allowEmpty;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
errorMessage = $"invalid {nameof(type).ToLower()}: {weapon}";
|
||||
return Enum.GetValues<WeaponType>()
|
||||
.Where(t => (t & type) == type)
|
||||
.Any(t => validateType(t, weapon));
|
||||
}
|
||||
|
||||
errorMessage = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool validateType(WeaponType current, string weapon) {
|
||||
var result = current switch {
|
||||
WeaponType.GRENADE => Tag.GRENADES.Contains(weapon),
|
||||
WeaponType.UTILITY => Tag.UTILITY.Contains(weapon),
|
||||
WeaponType.WEAPON => Tag.WEAPONS.Contains(weapon),
|
||||
WeaponType.SNIPERS => Tag.SNIPERS.Contains(weapon),
|
||||
WeaponType.RIFLES => Tag.RIFLES.Contains(weapon),
|
||||
WeaponType.PISTOLS => Tag.PISTOLS.Contains(weapon),
|
||||
WeaponType.SHOTGUNS => Tag.SHOTGUNS.Contains(weapon),
|
||||
WeaponType.SMGS => Tag.SMGS.Contains(weapon),
|
||||
WeaponType.HEAVY => Tag.HEAVY.Contains(weapon),
|
||||
WeaponType.GUNS => Tag.GUNS.Contains(weapon),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(weapon))
|
||||
};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
21
public/Jailbreak.Validator/NonZeroRangeValidator.cs
Normal file
21
public/Jailbreak.Validator/NonZeroRangeValidator.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
|
||||
namespace Jailbreak.Validator;
|
||||
|
||||
public class NonZeroRangeValidator<T>(T min, T max)
|
||||
: IValidator<T> where T : IComparable<T> {
|
||||
public bool Validate(T value, out string? errorMessage) {
|
||||
if (value.Equals(default(T))) {
|
||||
errorMessage = $"Value must be non-zero between {min} and {max}";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0) {
|
||||
errorMessage = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
errorMessage = $"Value must be between {min} and {max}";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
25
public/Jailbreak.Validator/TeamValidator.cs
Normal file
25
public/Jailbreak.Validator/TeamValidator.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Jailbreak.Public.Utils;
|
||||
|
||||
namespace Jailbreak.Validator;
|
||||
|
||||
public class TeamValidator(bool allowSpecs = true) : IValidator<string> {
|
||||
public bool Validate(string value, out string? errorMessage) {
|
||||
errorMessage = null;
|
||||
|
||||
var team = TeamUtil.FromString(value);
|
||||
|
||||
if (team == null) {
|
||||
errorMessage = $"Unknown team: \"{value}\"";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (team is CsTeam.None or CsTeam.Spectator && !allowSpecs) {
|
||||
errorMessage = "Team must be CT or T";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
|
||||
namespace Jailbreak.Validator;
|
||||
|
||||
public class WeaponValidator(
|
||||
WeaponValidator.WeaponType type = WeaponValidator.WeaponType.WEAPON,
|
||||
bool allowEmpty = true, bool allowMultiple = false) : IValidator<string> {
|
||||
public enum WeaponType {
|
||||
GRENADE,
|
||||
UTILITY,
|
||||
WEAPON,
|
||||
SNIPERS,
|
||||
RIFLES,
|
||||
PISTOLS,
|
||||
SHOTGUNS,
|
||||
SMGS,
|
||||
HEAVY
|
||||
}
|
||||
|
||||
public bool Validate(string value, out string? errorMessage) {
|
||||
if (value.Contains(',') && !allowMultiple) {
|
||||
errorMessage = "Value cannot contain multiple values";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(value)) {
|
||||
errorMessage = allowEmpty ? null : "weapon cannot be empty";
|
||||
return allowEmpty;
|
||||
}
|
||||
|
||||
foreach (var weapon in value.Split(',')) {
|
||||
if (string.IsNullOrWhiteSpace(weapon)) {
|
||||
errorMessage = allowEmpty ? null : "weapon cannot be empty";
|
||||
return allowEmpty;
|
||||
}
|
||||
|
||||
errorMessage = $"invalid {nameof(type).ToLower()}: {weapon}";
|
||||
var result = type switch {
|
||||
WeaponType.GRENADE => Tag.GRENADES.Contains(weapon),
|
||||
WeaponType.UTILITY => Tag.UTILITY.Contains(weapon),
|
||||
WeaponType.WEAPON => Tag.WEAPONS.Contains(weapon),
|
||||
WeaponType.SNIPERS => Tag.SNIPERS.Contains(weapon),
|
||||
WeaponType.RIFLES => Tag.RIFLES.Contains(weapon),
|
||||
WeaponType.PISTOLS => Tag.PISTOLS.Contains(weapon),
|
||||
WeaponType.SHOTGUNS => Tag.SHOTGUNS.Contains(weapon),
|
||||
WeaponType.SMGS => Tag.SMGS.Contains(weapon),
|
||||
WeaponType.HEAVY => Tag.HEAVY.Contains(weapon),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(weapon))
|
||||
};
|
||||
if (!result) return false;
|
||||
}
|
||||
|
||||
errorMessage = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using CounterStrikeSharp.API;
|
||||
using Jailbreak.Public.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jailbreak.Config;
|
||||
|
||||
/// <summary>
|
||||
/// A service to load and parse configuration files.
|
||||
/// </summary>
|
||||
public class ConfigService : IConfigService {
|
||||
private readonly ILogger<ConfigService> logger;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
public ConfigService(ILogger<ConfigService> logger) { this.logger = logger; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="fail"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public T Get<T>(string path, bool fail = false) where T : class, new() {
|
||||
var jsonPath =
|
||||
Path.Combine(Server.GameDirectory, IConfigService.CONFIG_PATH);
|
||||
|
||||
if (!File.Exists(jsonPath))
|
||||
return fail<T>(fail, "Config file does not exist");
|
||||
|
||||
var jsonText = File.ReadAllText(jsonPath);
|
||||
|
||||
var jsonObject = JsonNode.Parse(jsonText);
|
||||
if (jsonObject == null)
|
||||
return fail<T>(fail, $"Unable to parse configuration file at {jsonPath}");
|
||||
|
||||
var configObject = jsonObject[path];
|
||||
if (configObject == null)
|
||||
return fail<T>(fail, $"Unable to navigate to config section {path}");
|
||||
|
||||
var config = configObject.Deserialize<T>();
|
||||
if (config == null)
|
||||
return fail<T>(fail,
|
||||
$"Unable to deserialize ({configObject.ToJsonString()}) into {typeof(T).FullName}.");
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local
|
||||
private T fail<T>(bool fail, string message) where T : class, new() {
|
||||
// We would be returning default.
|
||||
// Check if caller wants us to cry and scream instead.
|
||||
if (fail) throw new InvalidOperationException(message);
|
||||
|
||||
logger.LogWarning(
|
||||
"[Config] Tripped load fail state with message: {@Message}", message);
|
||||
|
||||
return new T();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using Jailbreak.Config;
|
||||
using Jailbreak.Debug;
|
||||
using Jailbreak.English.Generic;
|
||||
using Jailbreak.English.LastGuard;
|
||||
@@ -18,7 +17,6 @@ using Jailbreak.LastGuard;
|
||||
using Jailbreak.LastRequest;
|
||||
using Jailbreak.Logs;
|
||||
using Jailbreak.Mute;
|
||||
using Jailbreak.Public.Configuration;
|
||||
using Jailbreak.Rebel;
|
||||
using Jailbreak.SpecialDay;
|
||||
using Jailbreak.Warden;
|
||||
@@ -52,7 +50,6 @@ public class JailbreakServiceCollection : IPluginServiceCollection<Jailbreak> {
|
||||
|
||||
// Do we want to make this scoped?
|
||||
// Not sure how this will behave with multiple rounds and whatnot.
|
||||
serviceCollection.AddTransient<IConfigService, ConfigService>();
|
||||
serviceCollection.AddJailbreakGeneric();
|
||||
serviceCollection.AddJailbreakLogs();
|
||||
serviceCollection.AddJailbreakRebel();
|
||||
|
||||
Reference in New Issue
Block a user