mirror of
https://github.com/MSWS/TTT.git
synced 2025-12-06 22:36:35 -08:00
Compare commits
56 Commits
0.22.3-dev
...
1.2.1-dev.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edbff4e17f | ||
|
|
b4452d7ff3 | ||
|
|
fd32744bf6 | ||
|
|
657306c1c7 | ||
|
|
2c800b471b | ||
|
|
2787823f86 | ||
|
|
a5f419aad9 | ||
|
|
430a8c4a7f | ||
|
|
a7a44b50f9 | ||
|
|
3b4bf490bc | ||
|
|
abe75d0347 | ||
|
|
eb79552ba3 | ||
|
|
e2011b8d24 | ||
|
|
ec41a6f367 | ||
|
|
9b1bed6982 | ||
|
|
8584877739 | ||
|
|
410dd407b3 | ||
|
|
a0bba2c4ba | ||
|
|
8aa508bf6d | ||
|
|
642155b1bc | ||
|
|
bacd288fe7 | ||
|
|
29e28038b8 | ||
|
|
7ce5293ad3 | ||
|
|
b253d8ee12 | ||
|
|
02575b51e2 | ||
|
|
d8d365b497 | ||
|
|
1ac38dc0ad | ||
|
|
62e57ffa97 | ||
|
|
81e6b2e695 | ||
|
|
ae99fab18e | ||
|
|
2ce0457346 | ||
|
|
ed90c54e53 | ||
|
|
06d2d71f76 | ||
|
|
c6ba041a6b | ||
|
|
f283d7407e | ||
|
|
51ff4df545 | ||
|
|
e0ee4bf325 | ||
|
|
4a4c7e0782 | ||
|
|
d4f67ced0c | ||
|
|
33ca0c8385 | ||
|
|
ff2e97a3ce | ||
|
|
a56cdc1285 | ||
|
|
ceda5cba64 | ||
|
|
7c203bcd91 | ||
|
|
99ed6bd69b | ||
|
|
f91fc54897 | ||
|
|
79ab6f9705 | ||
|
|
80a9cb2af1 | ||
|
|
c4a73f9a24 | ||
|
|
8fa2377e1e | ||
|
|
dbe664d18f | ||
|
|
5717ab612a | ||
|
|
f6b79ef038 | ||
|
|
cc52d19108 | ||
|
|
a1d595ce8a | ||
|
|
23f502a381 |
37
LICENSES.MD
37
LICENSES.MD
@@ -1,20 +1,17 @@
|
||||
| Package | Version | License Information Origin | License Expression | License Url | Copyright | Authors | Package Project Url |
|
||||
| ----------------------------------------------------- | -------- | -------------------------- | ------------------ | --------------------------------------- | ----------------------------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------ |
|
||||
| CounterStrikeSharp.API | 1.0.332 | Expression | GPL-3.0-only | https://licenses.nuget.org/GPL-3.0-only | | Roflmuffin | http://docs.cssharp.dev/ |
|
||||
| CounterStrikeSharp.API | 1.0.340 | Expression | GPL-3.0-only | https://licenses.nuget.org/GPL-3.0-only | | Roflmuffin | http://docs.cssharp.dev/ |
|
||||
| Dapper | 2.1.66 | Expression | Apache-2.0 | https://licenses.nuget.org/Apache-2.0 | 2019 Stack Exchange, Inc. | Sam Saffron,Marc Gravell,Nick Craver | https://github.com/DapperLib/Dapper |
|
||||
| JetBrains.Annotations | 2025.2.0 | Expression | MIT | https://licenses.nuget.org/MIT | Copyright (c) 2016-2025 JetBrains s.r.o. | JetBrains | https://www.jetbrains.com/help/resharper/Code_Analysis__Code_Annotations.html |
|
||||
| Microsoft.Data.Sqlite | 9.0.9 | Expression | MIT | https://licenses.nuget.org/MIT | © Microsoft Corporation. All rights reserved. | Microsoft | https://docs.microsoft.com/dotnet/standard/data/sqlite/ |
|
||||
| Microsoft.Extensions.DependencyInjection.Abstractions | 9.0.7 | Expression | MIT | https://licenses.nuget.org/MIT | © Microsoft Corporation. All rights reserved. | Microsoft | https://dot.net/ |
|
||||
| Microsoft.Extensions.Localization.Abstractions | 8.0.3 | Expression | MIT | https://licenses.nuget.org/MIT | © Microsoft Corporation. All rights reserved. | Microsoft | https://asp.net/ |
|
||||
| Microsoft.NET.Test.Sdk | 17.14.1 | Expression | MIT | https://licenses.nuget.org/MIT | © Microsoft Corporation. All rights reserved. | Microsoft | https://github.com/microsoft/vstest |
|
||||
| Microsoft.Reactive.Testing | 6.0.1 | Expression | MIT | https://licenses.nuget.org/MIT | Copyright (c) .NET Foundation and Contributors. | .NET Foundation and Contributors | https://github.com/dotnet/reactive |
|
||||
| Microsoft.Testing.Extensions.CodeCoverage | 17.14.2 | Unknown | | https://aka.ms/deprecateLicenseUrl | © Microsoft Corporation. All rights reserved. | Microsoft | https://github.com/microsoft/codecoverage |
|
||||
| MySqlConnector | 2.4.0 | Expression | MIT | https://licenses.nuget.org/MIT | Copyright 2016–2024 Bradley Grainger | Bradley Grainger | https://mysqlconnector.net/ |
|
||||
| SQLite | 3.13.0 | Unknown | | | Public Domain | SQLite Development Team | |
|
||||
| System.Reactive | 6.0.1 | Expression | MIT | https://licenses.nuget.org/MIT | Copyright (c) .NET Foundation and Contributors. | .NET Foundation and Contributors | https://github.com/dotnet/reactive |
|
||||
| System.Text.Json | 8.0.5 | Expression | MIT | https://licenses.nuget.org/MIT | © Microsoft Corporation. All rights reserved. | Microsoft | https://dot.net/ |
|
||||
| Xunit.DependencyInjection | 10.6.0 | Expression | MIT | https://licenses.nuget.org/MIT | Copyright © 2019 | Wei Peng | https://github.com/pengweiqhca/Xunit.DependencyInjection/tree/main/src/Xunit.DependencyInjection |
|
||||
| xunit.runner.visualstudio | 3.1.3 | Expression | Apache-2.0 | https://licenses.nuget.org/Apache-2.0 | Copyright (C) .NET Foundation | jnewkirk,bradwilson | |
|
||||
| xunit.v3 | 3.0.0 | Expression | Apache-2.0 | https://licenses.nuget.org/Apache-2.0 | Copyright (C) .NET Foundation | jnewkirk,bradwilson | |
|
||||
| YamlDotNet | 16.3.0 | Expression | MIT | https://licenses.nuget.org/MIT | Copyright (c) Antoine Aubry and contributors | Antoine Aubry | https://github.com/aaubry/YamlDotNet/wiki |
|
||||
| Package | Version | License Information Origin | License Expression | License Url | Copyright | Authors | Package Project Url |
|
||||
| ----------------------------------------------------- | -------- | -------------------------- | ------------------ | --------------------------------------- | ----------------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| CounterStrikeSharp.API | 1.0.332 | Expression | GPL-3.0-only | https://licenses.nuget.org/GPL-3.0-only | | Roflmuffin | http://docs.cssharp.dev/ |
|
||||
| CounterStrikeSharp.API | 1.0.342 | Expression | GPL-3.0-only | https://licenses.nuget.org/GPL-3.0-only | | Roflmuffin | http://docs.cssharp.dev/ |
|
||||
| JetBrains.Annotations | 2025.2.0 | Expression | MIT | https://licenses.nuget.org/MIT | Copyright (c) 2016-2025 JetBrains s.r.o. | JetBrains | https://www.jetbrains.com/help/resharper/Code_Analysis__Code_Annotations.html |
|
||||
| Microsoft.Data.Sqlite | 9.0.9 | Expression | MIT | https://licenses.nuget.org/MIT | © Microsoft Corporation. All rights reserved. | Microsoft | https://docs.microsoft.com/dotnet/standard/data/sqlite/ |
|
||||
| Microsoft.Extensions.DependencyInjection.Abstractions | 9.0.7 | Expression | MIT | https://licenses.nuget.org/MIT | © Microsoft Corporation. All rights reserved. | Microsoft | https://dot.net/ |
|
||||
| Microsoft.Extensions.Localization.Abstractions | 8.0.3 | Expression | MIT | https://licenses.nuget.org/MIT | © Microsoft Corporation. All rights reserved. | Microsoft | https://asp.net/ |
|
||||
| Microsoft.NET.Test.Sdk | 17.14.1 | Expression | MIT | https://licenses.nuget.org/MIT | © Microsoft Corporation. All rights reserved. | Microsoft | https://github.com/microsoft/vstest |
|
||||
| Microsoft.Reactive.Testing | 6.0.1 | Expression | MIT | https://licenses.nuget.org/MIT | Copyright (c) .NET Foundation and Contributors. | .NET Foundation and Contributors | https://github.com/dotnet/reactive |
|
||||
| Microsoft.Testing.Extensions.CodeCoverage | 17.14.2 | Unknown | | https://aka.ms/deprecateLicenseUrl | © Microsoft Corporation. All rights reserved. | Microsoft | https://github.com/microsoft/codecoverage |
|
||||
| System.Reactive | 6.0.1 | Expression | MIT | https://licenses.nuget.org/MIT | Copyright (c) .NET Foundation and Contributors. | .NET Foundation and Contributors | https://github.com/dotnet/reactive |
|
||||
| System.Text.Json | 8.0.5 | Expression | MIT | https://licenses.nuget.org/MIT | © Microsoft Corporation. All rights reserved. | Microsoft | https://dot.net/ |
|
||||
| Xunit.DependencyInjection | 10.6.0 | Expression | MIT | https://licenses.nuget.org/MIT | Copyright © 2019 | Wei Peng | https://github.com/pengweiqhca/Xunit.DependencyInjection/tree/main/src/Xunit.DependencyInjection |
|
||||
| xunit.runner.visualstudio | 3.1.3 | Expression | Apache-2.0 | https://licenses.nuget.org/Apache-2.0 | Copyright (C) .NET Foundation | jnewkirk,bradwilson | |
|
||||
| xunit.v3 | 3.0.0 | Expression | Apache-2.0 | https://licenses.nuget.org/Apache-2.0 | Copyright (C) .NET Foundation | jnewkirk,bradwilson | |
|
||||
| YamlDotNet | 16.3.0 | Expression | MIT | https://licenses.nuget.org/MIT | Copyright (c) Antoine Aubry and contributors | Antoine Aubry | https://github.com/aaubry/YamlDotNet/wiki |
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using TTT.API;
|
||||
using SpecialRoundAPI.Configs;
|
||||
using TTT.API.Events;
|
||||
using TTT.Game.Events.Game;
|
||||
using TTT.Game.Listeners;
|
||||
using TTT.Locale;
|
||||
|
||||
namespace SpecialRoundAPI;
|
||||
|
||||
public abstract class AbstractSpecialRound(IServiceProvider provider)
|
||||
: ITerrorModule, IListener {
|
||||
protected readonly IServiceProvider Provider = provider;
|
||||
|
||||
: BaseListener(provider) {
|
||||
protected readonly ISpecialRoundTracker Tracker =
|
||||
provider.GetRequiredService<ISpecialRoundTracker>();
|
||||
|
||||
@@ -18,9 +17,6 @@ public abstract class AbstractSpecialRound(IServiceProvider provider)
|
||||
public abstract IMsg Description { get; }
|
||||
public abstract SpecialRoundConfig Config { get; }
|
||||
|
||||
public void Dispose() { }
|
||||
public void Start() { }
|
||||
|
||||
public abstract void ApplyRoundEffects();
|
||||
|
||||
[UsedImplicitly]
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
namespace SpecialRoundAPI;
|
||||
|
||||
public record BhopRoundConfig : SpecialRoundConfig {
|
||||
public override float Weight { get; init; } = 0.2f;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace SpecialRoundAPI.Configs;
|
||||
|
||||
public record BhopRoundConfig : SpecialRoundConfig {
|
||||
public override float Weight { get; init; } = 0.25f;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace SpecialRoundAPI.Configs;
|
||||
|
||||
public record PistolRoundConfig : SpecialRoundConfig {
|
||||
public override float Weight { get; init; } = 0.75f;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace SpecialRoundAPI.Configs;
|
||||
|
||||
public record SilentRoundConfig : SpecialRoundConfig {
|
||||
public override float Weight { get; init; } = 0.5f;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace SpecialRoundAPI;
|
||||
namespace SpecialRoundAPI.Configs;
|
||||
|
||||
public abstract record SpecialRoundConfig {
|
||||
public abstract float Weight { get; init; }
|
||||
@@ -1,8 +1,9 @@
|
||||
namespace SpecialRoundAPI;
|
||||
namespace SpecialRoundAPI.Configs;
|
||||
|
||||
public record SpeedRoundConfig : SpecialRoundConfig {
|
||||
public override float Weight { get; init; } = 0.5f;
|
||||
public override float Weight { get; init; } = 1;
|
||||
|
||||
public TimeSpan InitialSeconds { get; init; } = TimeSpan.FromSeconds(40);
|
||||
public TimeSpan SecondsPerKill { get; init; } = TimeSpan.FromSeconds(10);
|
||||
public TimeSpan SecondsPerKill { get; init; } = TimeSpan.FromSeconds(8);
|
||||
public TimeSpan MaxTimeEver { get; init; } = TimeSpan.FromSeconds(90);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace SpecialRoundAPI.Configs;
|
||||
|
||||
public record SuppressedRoundConfig : SpecialRoundConfig {
|
||||
public override float Weight { get; init; } = 0.75f;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace SpecialRoundAPI.Configs;
|
||||
|
||||
public record VanillaRoundConfig : SpecialRoundConfig {
|
||||
public override float Weight { get; init; } = 0.5f;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
namespace SpecialRoundAPI;
|
||||
|
||||
public record VanillaRoundConfig : SpecialRoundConfig {
|
||||
public override float Weight { get; init; } = 0.3f;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ShopAPI;
|
||||
using ShopAPI.Configs;
|
||||
using ShopAPI.Configs.Detective;
|
||||
using ShopAPI.Configs.Traitor;
|
||||
@@ -63,26 +64,28 @@ public static class CS2ServiceCollection {
|
||||
collection
|
||||
.AddModBehavior<IStorage<OneHitKnifeConfig>, CS2OneHitKnifeConfig>();
|
||||
collection.AddModBehavior<IStorage<SilentAWPConfig>, CS2SilentAWPConfig>();
|
||||
collection
|
||||
.AddModBehavior<IStorage<HealthshotConfig>, CS2HealthshotConfig>();
|
||||
collection.AddModBehavior<IStorage<TripwireConfig>, CS2TripwireConfig>();
|
||||
|
||||
// TTT - CS2 Specific optionals
|
||||
collection.AddScoped<ITextSpawner, TextSpawner>();
|
||||
|
||||
// GameHandlers
|
||||
collection.AddModBehavior<BodySpawner>();
|
||||
collection.AddModBehavior<BombPlantSuppressor>();
|
||||
collection.AddModBehavior<BuyMenuHandler>();
|
||||
collection.AddModBehavior<CombatHandler>();
|
||||
collection.AddModBehavior<DamageCanceler>();
|
||||
collection.AddModBehavior<MapChangeCausesEndListener>();
|
||||
collection.AddModBehavior<MapZoneRemover>();
|
||||
collection.AddModBehavior<NameUpdater>();
|
||||
collection.AddModBehavior<PlayerConnectionsHandler>();
|
||||
collection.AddModBehavior<PlayerMuter>();
|
||||
collection.AddModBehavior<PropMover>();
|
||||
collection.AddModBehavior<RoundStart_GameStartHandler>();
|
||||
collection.AddModBehavior<BombPlantSuppressor>();
|
||||
collection.AddModBehavior<MapZoneRemover>();
|
||||
collection.AddModBehavior<BuyMenuHandler>();
|
||||
collection.AddModBehavior<TeamChangeHandler>();
|
||||
collection.AddModBehavior<TraitorChatHandler>();
|
||||
collection.AddModBehavior<PlayerMuter>();
|
||||
collection.AddModBehavior<MapChangeCausesEndListener>();
|
||||
collection.AddModBehavior<NameUpdater>();
|
||||
// collection.AddModBehavior<EntityTargetHandlers>();
|
||||
|
||||
// Damage Cancelers
|
||||
collection.AddModBehavior<OutOfRoundCanceler>();
|
||||
@@ -92,13 +95,14 @@ public static class CS2ServiceCollection {
|
||||
collection.AddModBehavior<AfkTimerListener>();
|
||||
collection.AddModBehavior<BodyPickupListener>();
|
||||
collection.AddModBehavior<IBodyTracker, BodyTracker>();
|
||||
collection.AddModBehavior<KarmaBanner>();
|
||||
collection.AddModBehavior<KarmaSyncer>();
|
||||
collection.AddModBehavior<LateSpawnListener>();
|
||||
collection.AddModBehavior<MapHookListener>();
|
||||
collection.AddModBehavior<PlayerStatsTracker>();
|
||||
collection.AddModBehavior<RoundTimerListener>();
|
||||
collection.AddModBehavior<ScreenColorApplier>();
|
||||
collection.AddModBehavior<KarmaBanner>();
|
||||
collection.AddModBehavior<KarmaSyncer>();
|
||||
collection.AddModBehavior<MapHookListener>();
|
||||
collection.AddModBehavior<WardenTagAssigner>();
|
||||
|
||||
// Commands
|
||||
collection.AddModBehavior<TestCommand>();
|
||||
|
||||
@@ -41,15 +41,15 @@ public class PlayerPingShopAlias(IServiceProvider provider) : IPluginModule {
|
||||
|
||||
private void onButton(CCSPlayerController? player, int index) {
|
||||
if (player == null) return;
|
||||
if (converter.GetPlayer(player) is not IOnlinePlayer gamePlayer) return;
|
||||
if (converter.GetPlayer(player) is not IOnlinePlayer apiPlayer) return;
|
||||
|
||||
var lastUpdated = itemSorter.GetLastUpdate(gamePlayer);
|
||||
var lastUpdated = itemSorter.GetLastUpdate(apiPlayer);
|
||||
if (lastUpdated == null
|
||||
|| DateTime.Now - lastUpdated > TimeSpan.FromSeconds(20))
|
||||
return;
|
||||
var cmdInfo = new CS2CommandInfo(provider, gamePlayer, 0, "css_shop", "buy",
|
||||
(index - 1).ToString());
|
||||
cmdInfo.CallingContext = CommandCallingContext.Chat;
|
||||
var cmdInfo = new CS2CommandInfo(provider, apiPlayer, 0, "css_shop", "buy",
|
||||
(index - 1).ToString()) { CallingContext = CommandCallingContext.Chat };
|
||||
provider.GetRequiredService<ICommandManager>().ProcessCommand(cmdInfo);
|
||||
itemSorter.InvalidateOrder(apiPlayer);
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,6 @@ public class GiveItemCommand(IServiceProvider provider) : ICommand {
|
||||
|
||||
var purchaseEv = new PlayerPurchaseItemEvent(target, item);
|
||||
provider.GetRequiredService<IEventBus>().Dispatch(purchaseEv);
|
||||
if (purchaseEv.IsCanceled) return;
|
||||
|
||||
shop.GiveItem(target, item);
|
||||
info.ReplySync($"Gave item '{item.Name}' to {target.Name}.");
|
||||
|
||||
@@ -11,6 +11,8 @@ public class SpecCommand(IServiceProvider provider) : ICommand {
|
||||
public void Dispose() { }
|
||||
public void Start() { }
|
||||
|
||||
public string Id => "spec";
|
||||
|
||||
public Task<CommandResult>
|
||||
Execute(IOnlinePlayer? executor, ICommandInfo info) {
|
||||
var target = executor;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using CounterStrikeSharp.API;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SpecialRoundAPI;
|
||||
using TTT.API;
|
||||
using TTT.API.Command;
|
||||
@@ -25,7 +26,7 @@ public class SpecialRoundCommand(IServiceProvider provider) : ICommand {
|
||||
|
||||
var rounds = provider.GetServices<ITerrorModule>()
|
||||
.OfType<AbstractSpecialRound>()
|
||||
.ToDictionary(r => r.GetType().Name.ToLower(), r => r);
|
||||
.ToDictionary(r => r.Name.ToLower(), r => r);
|
||||
|
||||
var roundName = info.Args[1].ToLower();
|
||||
if (!rounds.TryGetValue(roundName, out var round)) {
|
||||
@@ -34,8 +35,10 @@ public class SpecialRoundCommand(IServiceProvider provider) : ICommand {
|
||||
return Task.FromResult(CommandResult.INVALID_ARGS);
|
||||
}
|
||||
|
||||
tracker.TryStartSpecialRound(round);
|
||||
info.ReplySync($"Started special round '{roundName}'.");
|
||||
Server.NextWorldUpdate(() => {
|
||||
tracker.TryStartSpecialRound(round);
|
||||
info.ReplySync($"Started special round '{roundName}'.");
|
||||
});
|
||||
return Task.FromResult(CommandResult.SUCCESS);
|
||||
}
|
||||
}
|
||||
@@ -63,21 +63,19 @@ public class CS2GameConfig : IStorage<TTTConfig>, IPluginModule {
|
||||
|
||||
public static readonly FakeConVar<string> CV_TRAITOR_WEAPONS = new(
|
||||
"css_ttt_roleweapons_traitor",
|
||||
"Weapons available to traitors at start of round",
|
||||
"weapon_knife,weapon_glock", ConVarFlags.FCVAR_NONE,
|
||||
new ItemValidator(allowMultiple: true));
|
||||
"Weapons available to traitors at start of round", "",
|
||||
ConVarFlags.FCVAR_NONE, new ItemValidator(allowMultiple: true));
|
||||
|
||||
public static readonly FakeConVar<string> CV_DETECTIVE_WEAPONS = new(
|
||||
"css_ttt_roleweapons_detective",
|
||||
"Weapons available to detectives at start of round",
|
||||
"weapon_knife,weapon_taser,weapon_m4a1,weapon_revolver",
|
||||
ConVarFlags.FCVAR_NONE, new ItemValidator(allowMultiple: true));
|
||||
"weapon_taser,weapon_m4a1_silencer,weapon_revolver", ConVarFlags.FCVAR_NONE,
|
||||
new ItemValidator(allowMultiple: true));
|
||||
|
||||
public static readonly FakeConVar<string> CV_INNOCENT_WEAPONS = new(
|
||||
"css_ttt_roleweapons_innocent",
|
||||
"Weapons available to innocents at start of round",
|
||||
"weapon_knife,weapon_glock", ConVarFlags.FCVAR_NONE,
|
||||
new ItemValidator(allowMultiple: true));
|
||||
"Weapons available to innocents at start of round", "",
|
||||
ConVarFlags.FCVAR_NONE, new ItemValidator(allowMultiple: true));
|
||||
|
||||
public static readonly FakeConVar<int> CV_TIME_BETWEEN_ROUNDS = new(
|
||||
"css_ttt_time_between_rounds", "Time to wait between rounds in seconds", 1,
|
||||
|
||||
@@ -74,13 +74,13 @@ public class CS2KarmaConfig : IStorage<KarmaConfig>, IPluginModule {
|
||||
|
||||
public static readonly FakeConVar<int> CV_KARMA_PER_ROUND = new(
|
||||
"css_ttt_karma_per_round",
|
||||
"Amount of karma a player will gain at the end of each round", 2,
|
||||
"Amount of karma a player will gain at the end of each round", 1,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 50));
|
||||
|
||||
public static readonly FakeConVar<int> CV_KARMA_PER_ROUND_WIN = new(
|
||||
"css_ttt_karma_per_round_win",
|
||||
"Amount of karma a player will gain at the end of each round if their team won",
|
||||
4, ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 50));
|
||||
1, ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 50));
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace TTT.CS2.Configs.ShopItems;
|
||||
|
||||
public class CS2BodyPaintConfig : IStorage<BodyPaintConfig>, IPluginModule {
|
||||
public static readonly FakeConVar<int> CV_PRICE = new(
|
||||
"css_ttt_shop_bodypaint_price", "Price of the Body Paint item", 40,
|
||||
"css_ttt_shop_bodypaint_price", "Price of the Body Paint item", 30,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 10000));
|
||||
|
||||
public static readonly FakeConVar<int> CV_MAX_USES = new(
|
||||
|
||||
@@ -16,7 +16,7 @@ public class CS2CamoConfig : IStorage<CamoConfig>, IPluginModule {
|
||||
public static readonly FakeConVar<float> CV_CAMO_VISIBILITY = new(
|
||||
"css_ttt_shop_camo_visibility",
|
||||
"Player visibility multiplier while camouflaged (0 = invisible, 1 = fully visible)",
|
||||
0.6f, ConVarFlags.FCVAR_NONE, new RangeValidator<float>(0f, 1f));
|
||||
0.5f, ConVarFlags.FCVAR_NONE, new RangeValidator<float>(0f, 1f));
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
|
||||
43
TTT/CS2/Configs/ShopItems/CS2HealthshotConfig.cs
Normal file
43
TTT/CS2/Configs/ShopItems/CS2HealthshotConfig.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
using ShopAPI;
|
||||
using TTT.API;
|
||||
using TTT.API.Storage;
|
||||
|
||||
namespace TTT.CS2.Configs.ShopItems;
|
||||
|
||||
public class CS2HealthshotConfig : IStorage<HealthshotConfig>, IPluginModule {
|
||||
public static readonly FakeConVar<int> CV_PRICE = new(
|
||||
"css_ttt_shop_healthshot_price", "Price of the Healthshot item", 40,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 10000));
|
||||
|
||||
public static readonly FakeConVar<int> CV_MAX_PURCHASES = new(
|
||||
"css_ttt_shop_healthshot_max_purchases",
|
||||
"Maximum number of times a player can purchase the Healthshot per round", 2,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(1, 100));
|
||||
|
||||
public static readonly FakeConVar<string> CV_WEAPON = new(
|
||||
"css_ttt_shop_healthshot_weapon", "Weapon entity name for the Healthshot",
|
||||
"weapon_healthshot");
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public void Start() { }
|
||||
|
||||
public void Start(BasePlugin? plugin) {
|
||||
ArgumentNullException.ThrowIfNull(plugin, nameof(plugin));
|
||||
plugin.RegisterFakeConVars(this);
|
||||
}
|
||||
|
||||
public Task<HealthshotConfig?> Load() {
|
||||
var cfg = new HealthshotConfig {
|
||||
Price = CV_PRICE.Value,
|
||||
MaxPurchases = CV_MAX_PURCHASES.Value,
|
||||
Weapon = CV_WEAPON.Value
|
||||
};
|
||||
|
||||
return Task.FromResult<HealthshotConfig?>(cfg);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ namespace TTT.CS2.Configs.ShopItems;
|
||||
|
||||
public class CS2StickersConfig : IStorage<StickersConfig>, IPluginModule {
|
||||
public static readonly FakeConVar<int> CV_PRICE = new(
|
||||
"css_ttt_shop_stickers_price", "Price of the Stickers item (Detective)", 35,
|
||||
"css_ttt_shop_stickers_price", "Price of the Stickers item (Detective)", 45,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 10000));
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
68
TTT/CS2/Configs/ShopItems/CS2TripwireConfig.cs
Normal file
68
TTT/CS2/Configs/ShopItems/CS2TripwireConfig.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
using ShopAPI.Configs.Traitor;
|
||||
using TTT.API;
|
||||
using TTT.API.Storage;
|
||||
|
||||
namespace TTT.CS2.Configs.ShopItems;
|
||||
|
||||
public class CS2TripwireConfig : IStorage<TripwireConfig>, IPluginModule {
|
||||
public static readonly FakeConVar<int> CV_PRICE = new(
|
||||
"css_ttt_shop_tripwire_price", "Price of the Tripwire item (Traitor)", 50,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 10000));
|
||||
|
||||
public static readonly FakeConVar<int> CV_EXPLOSION_POWER = new(
|
||||
"css_ttt_shop_tripwire_explosion_power",
|
||||
"Explosion power of the Tripwire in damage units", 1000,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(1, 10000));
|
||||
|
||||
public static readonly FakeConVar<float> CV_FALLOFF_DELAY = new(
|
||||
"css_ttt_shop_tripwire_falloff_delay",
|
||||
"Damage falloff, higher means faster falloff", 0.15f,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(0f, 1f));
|
||||
|
||||
public static readonly FakeConVar<float> CV_FF_MULTIPLIER = new(
|
||||
"css_ttt_shop_tripwire_friendlyfire_multiplier",
|
||||
"Damage multiplier applied to friendly fire from Tripwire", 0.5f,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(0f, 1f));
|
||||
|
||||
public static readonly FakeConVar<bool> CV_FF_TRIGGERS = new(
|
||||
"css_ttt_shop_tripwire_friendlyfire_triggers",
|
||||
"Whether Tripwires can be triggered by teammates", true);
|
||||
|
||||
public static readonly FakeConVar<float> CV_MAX_PLACEMENT_DISTANCE_SQUARED =
|
||||
new("css_ttt_shop_tripwire_max_placement_distance_squared",
|
||||
"Maximum distance squared from player to place Tripwire", 400f * 400f,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(100f, 100000f));
|
||||
|
||||
public static readonly FakeConVar<int> CV_INITIATION_TIME_MS = new(
|
||||
"css_ttt_shop_tripwire_initiation_time_ms",
|
||||
"Time in milliseconds to initiate Tripwire placement", 2000,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 10000));
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public void Start() { }
|
||||
|
||||
public void Start(BasePlugin? plugin) {
|
||||
ArgumentNullException.ThrowIfNull(plugin, nameof(plugin));
|
||||
plugin.RegisterFakeConVars(this);
|
||||
}
|
||||
|
||||
public Task<TripwireConfig?> Load() {
|
||||
var cfg = new TripwireConfig {
|
||||
Price = CV_PRICE.Value,
|
||||
ExplosionPower = CV_EXPLOSION_POWER.Value,
|
||||
FalloffDelay = CV_FALLOFF_DELAY.Value,
|
||||
FriendlyFireMultiplier = CV_FF_MULTIPLIER.Value,
|
||||
FriendlyFireTriggers = CV_FF_TRIGGERS.Value,
|
||||
MaxPlacementDistanceSquared = CV_MAX_PLACEMENT_DISTANCE_SQUARED.Value,
|
||||
TripwireInitiationTime =
|
||||
TimeSpan.FromMilliseconds(CV_INITIATION_TIME_MS.Value)
|
||||
};
|
||||
|
||||
return Task.FromResult<TripwireConfig?>(cfg);
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,10 @@ using TTT.API;
|
||||
using TTT.API.Events;
|
||||
using TTT.API.Game;
|
||||
using TTT.API.Player;
|
||||
using TTT.API.Role;
|
||||
using TTT.CS2.API;
|
||||
using TTT.Game.Events.Player;
|
||||
using TTT.Game.Roles;
|
||||
|
||||
namespace TTT.CS2.GameHandlers;
|
||||
|
||||
@@ -21,6 +23,9 @@ public class CombatHandler(IServiceProvider provider) : IPluginModule {
|
||||
private readonly IGameManager games =
|
||||
provider.GetRequiredService<IGameManager>();
|
||||
|
||||
private readonly IRoleAssigner roles =
|
||||
provider.GetRequiredService<IRoleAssigner>();
|
||||
|
||||
private readonly IAliveSpoofer spoofer =
|
||||
provider.GetRequiredService<IAliveSpoofer>();
|
||||
|
||||
@@ -45,7 +50,19 @@ public class CombatHandler(IServiceProvider provider) : IPluginModule {
|
||||
if (games.ActiveGame is not { State: State.IN_PROGRESS })
|
||||
return HookResult.Continue;
|
||||
|
||||
if (ev.Attacker != null) ev.FireEventToClient(ev.Attacker);
|
||||
if (ev.Attacker != null) {
|
||||
ev.FireEventToClient(ev.Attacker);
|
||||
var apiPlayer = converter.GetPlayer(ev.Attacker);
|
||||
var role = roles.GetRoles(apiPlayer);
|
||||
if (role.Any(r => r is TraitorRole))
|
||||
foreach (var p in Utilities.GetPlayers()) {
|
||||
var apiP = converter.GetPlayer(p);
|
||||
if (apiP.Id == apiPlayer.Id) continue;
|
||||
var r = roles.GetRoles(converter.GetPlayer(p));
|
||||
if (role.Intersect(r).Any()) ev.FireEventToClient(p);
|
||||
}
|
||||
}
|
||||
|
||||
info.DontBroadcast = true;
|
||||
spoofer.SpoofAlive(player);
|
||||
Server.NextWorldUpdateAsync(() => bus.Dispatch(deathEvent));
|
||||
|
||||
@@ -51,7 +51,9 @@ public class PropMover(IServiceProvider provider) : IPluginModule {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pressed.HasFlag(PlayerButtons.Use)) return;
|
||||
if (!pressed.HasFlag(PlayerButtons.Use)
|
||||
&& !pressed.HasFlag(PlayerButtons.Inspect))
|
||||
return;
|
||||
|
||||
onStartUse(player);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ShopAPI.Configs.Traitor;
|
||||
@@ -45,6 +46,13 @@ public class DamageStation(IServiceProvider provider)
|
||||
override protected void onInterval() {
|
||||
var players = finder.GetOnline();
|
||||
var toRemove = new List<CPhysicsPropMultiplayer>();
|
||||
var playerMapping = players
|
||||
.Select(p => (ApiPlayer: p, GamePlayer: converter.GetPlayer(p)))
|
||||
.Where(m
|
||||
=> m.GamePlayer != null
|
||||
&& !Roles.GetRoles(m.ApiPlayer).Any(r => r is TraitorRole))
|
||||
.ToList();
|
||||
|
||||
foreach (var (prop, info) in props) {
|
||||
if (_Config.TotalHealthGiven != 0 && Math.Abs(info.HealthGiven)
|
||||
> Math.Abs(_Config.TotalHealthGiven)) {
|
||||
@@ -59,10 +67,6 @@ public class DamageStation(IServiceProvider provider)
|
||||
|
||||
var propPos = prop.AbsOrigin;
|
||||
|
||||
var playerMapping = players.Select(p
|
||||
=> (ApiPlayer: p, GamePlayer: converter.GetPlayer(p)))
|
||||
.Where(m => m.GamePlayer != null);
|
||||
|
||||
var playerDists = playerMapping
|
||||
.Select(t => (t.ApiPlayer, Origin: t.GamePlayer!.Pawn.Value?.AbsOrigin,
|
||||
t.GamePlayer))
|
||||
@@ -73,19 +77,16 @@ public class DamageStation(IServiceProvider provider)
|
||||
.ToList();
|
||||
|
||||
foreach (var (player, dist, gamePlayer) in playerDists) {
|
||||
gamePlayer.EmitSound("Player.DamageFall", null, 0.2f);
|
||||
if (Roles.GetRoles(player).Any(r => r is TraitorRole)) continue;
|
||||
|
||||
var healthScale = 1.0 - dist / _Config.MaxRange;
|
||||
var damageAmount =
|
||||
(int)Math.Floor(_Config.HealthIncrements * healthScale);
|
||||
Math.Abs((int)Math.Floor(_Config.HealthIncrements * healthScale));
|
||||
|
||||
var dmgEvent = new PlayerDamagedEvent(player,
|
||||
info.Owner as IOnlinePlayer, damageAmount) { Weapon = $"[{Name}]" };
|
||||
|
||||
bus.Dispatch(dmgEvent);
|
||||
|
||||
damageAmount = -dmgEvent.DmgDealt;
|
||||
damageAmount = dmgEvent.DmgDealt;
|
||||
|
||||
if (player.Health + damageAmount <= 0) {
|
||||
killedWithStation[player.Id] = info;
|
||||
@@ -95,7 +96,8 @@ public class DamageStation(IServiceProvider provider)
|
||||
bus.Dispatch(playerDeath);
|
||||
}
|
||||
|
||||
player.Health += damageAmount;
|
||||
gamePlayer.EmitSound("Player.DamageFall", SELF(gamePlayer.Slot), 0.2f);
|
||||
player.Health -= damageAmount;
|
||||
info.HealthGiven += damageAmount;
|
||||
}
|
||||
}
|
||||
@@ -103,6 +105,10 @@ public class DamageStation(IServiceProvider provider)
|
||||
foreach (var prop in toRemove) props.Remove(prop);
|
||||
}
|
||||
|
||||
private static RecipientFilter SELF(int slot) {
|
||||
return new RecipientFilter(slot);
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
[EventHandler]
|
||||
public void OnGameEnd(GameStateUpdateEvent ev) {
|
||||
|
||||
@@ -20,7 +20,7 @@ public abstract class StationItem<T>(IServiceProvider provider,
|
||||
: RoleRestrictedItem<T>(provider), IPluginModule where T : IRole {
|
||||
protected readonly StationConfig _Config = config;
|
||||
|
||||
protected readonly IPlayerConverter<CCSPlayerController> Converter =
|
||||
protected readonly IPlayerConverter<CCSPlayerController> converter =
|
||||
provider.GetRequiredService<IPlayerConverter<CCSPlayerController>>();
|
||||
|
||||
private readonly long PROP_SIZE_SQUARED = 700;
|
||||
@@ -124,7 +124,7 @@ public abstract class StationItem<T>(IServiceProvider provider,
|
||||
prop.SetModel("models/props/cs_office/microwave.vmdl");
|
||||
prop.DispatchSpawn();
|
||||
|
||||
var gamePlayer = Converter.GetPlayer(player);
|
||||
var gamePlayer = converter.GetPlayer(player);
|
||||
if (gamePlayer == null || !gamePlayer.Pawn.IsValid
|
||||
|| gamePlayer.Pawn.Value == null)
|
||||
return;
|
||||
|
||||
155
TTT/CS2/Items/Tripwire/TripwireItem.cs
Normal file
155
TTT/CS2/Items/Tripwire/TripwireItem.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Drawing;
|
||||
using System.Reactive.Concurrency;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ShopAPI;
|
||||
using ShopAPI.Configs;
|
||||
using ShopAPI.Configs.Traitor;
|
||||
using TTT.API;
|
||||
using TTT.API.Extensions;
|
||||
using TTT.API.Player;
|
||||
using TTT.API.Storage;
|
||||
using TTT.CS2.Extensions;
|
||||
using TTT.CS2.RayTrace.Class;
|
||||
using TTT.CS2.RayTrace.Enum;
|
||||
using TTT.CS2.RayTrace.Struct;
|
||||
using TTT.Game.Roles;
|
||||
|
||||
namespace TTT.CS2.Items.Tripwire;
|
||||
|
||||
public static class TripwireServiceCollection {
|
||||
public static void AddTripwireServices(this IServiceCollection services) {
|
||||
services.AddModBehavior<TripwireItem>();
|
||||
services.AddModBehavior<TripwireMovementListener>();
|
||||
}
|
||||
}
|
||||
|
||||
public class TripwireItem(IServiceProvider provider)
|
||||
: RoleRestrictedItem<TraitorRole>(provider), IPluginModule {
|
||||
private TripwireConfig config
|
||||
=> Provider.GetService<IStorage<TripwireConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new TripwireConfig();
|
||||
|
||||
private readonly IPlayerConverter<CCSPlayerController> converter =
|
||||
provider.GetRequiredService<IPlayerConverter<CCSPlayerController>>();
|
||||
|
||||
private readonly IScheduler scheduler =
|
||||
provider.GetRequiredService<IScheduler>();
|
||||
|
||||
public readonly List<TripwireInstance> ActiveTripwires = [];
|
||||
public override string Name => Locale[TripwireMsgs.SHOP_ITEM_TRIPWIRE];
|
||||
|
||||
public override string Description
|
||||
=> Locale[TripwireMsgs.SHOP_ITEM_TRIPWIRE_DESC];
|
||||
|
||||
public override ShopItemConfig Config => config;
|
||||
|
||||
public void Start(BasePlugin? plugin) {
|
||||
Start();
|
||||
plugin
|
||||
?.RegisterListener<
|
||||
CounterStrikeSharp.API.Core.Listeners.OnServerPrecacheResources>(
|
||||
onPrecache);
|
||||
}
|
||||
|
||||
private void onPrecache(ResourceManifest manifest) {
|
||||
manifest.AddResource(
|
||||
"models/generic/conveyor_control_panel_01/conveyor_button_02.vmdl");
|
||||
}
|
||||
|
||||
public override void OnPurchase(IOnlinePlayer player) {
|
||||
Server.NextWorldUpdate(() => {
|
||||
if (!placeTripwire(player, out var originTrace, out var endTrace,
|
||||
out var tripwire))
|
||||
return;
|
||||
|
||||
scheduler.Schedule(config.TripwireInitiationTime,
|
||||
() => {
|
||||
Server.NextWorldUpdate(() => {
|
||||
createTripwireBeam(player, tripwire,
|
||||
originTrace.Value.EndPos.toVector(),
|
||||
endTrace.Value.EndPos.toVector());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private bool placeTripwire(IOnlinePlayer player,
|
||||
[NotNullWhen(true)] out CGameTrace? originTrace,
|
||||
[NotNullWhen(true)] out CGameTrace? endTrace,
|
||||
[NotNullWhen(true)] out CDynamicProp? tripwire) {
|
||||
tripwire = null;
|
||||
originTrace = null;
|
||||
endTrace = null;
|
||||
var gamePlayer = converter.GetPlayer(player);
|
||||
var playerPawn = gamePlayer?.PlayerPawn.Value;
|
||||
if (gamePlayer == null || playerPawn == null) return false;
|
||||
|
||||
originTrace = gamePlayer.GetGameTraceByEyePosition(TraceMask.MaskSolid,
|
||||
Contents.NoDraw, gamePlayer);
|
||||
var origin = gamePlayer.GetEyePosition();
|
||||
if (origin == null || originTrace == null) return false;
|
||||
|
||||
if (origin.DistanceSquared(originTrace.Value.EndPos.toVector())
|
||||
> config.MaxPlacementDistanceSquared) {
|
||||
Shop.AddBalance(player, config.Price, "Refund");
|
||||
Messenger.Message(player, Locale[TripwireMsgs.SHOP_ITEM_TRIPWIRE_TOOFAR]);
|
||||
return false;
|
||||
}
|
||||
|
||||
var angles = vectorToAngle(originTrace.Value.Normal.toVector());
|
||||
|
||||
endTrace = TraceRay.TraceShape(originTrace.Value.EndPos.toVector(), angles,
|
||||
TraceMask.MaskSolid, Contents.NoDraw, gamePlayer);
|
||||
|
||||
tripwire = Utilities.CreateEntityByName<CDynamicProp>("prop_dynamic");
|
||||
if (tripwire == null) return false;
|
||||
|
||||
tripwire.SetModel(
|
||||
"models/generic/conveyor_control_panel_01/conveyor_button_02.vmdl");
|
||||
tripwire.DispatchSpawn();
|
||||
|
||||
tripwire.Teleport(originTrace.Value.EndPos.toVector(),
|
||||
vectorToAngle(originTrace.Value.Normal.toVector()));
|
||||
tripwire.EmitSound("Weapon_ELITE.Clipout");
|
||||
return true;
|
||||
}
|
||||
|
||||
private void createTripwireBeam(IOnlinePlayer owner, CDynamicProp prop,
|
||||
Vector start, Vector end) {
|
||||
prop.EmitSound("C4.ExplodeTriggerTrip");
|
||||
var beam = createBeamEnt(start, end);
|
||||
if (beam == null) return;
|
||||
|
||||
var instance = new TripwireInstance(owner, beam, prop, start, end);
|
||||
ActiveTripwires.Add(instance);
|
||||
}
|
||||
|
||||
private QAngle vectorToAngle(Vector vec) {
|
||||
var pitch = (float)(Math.Atan2(-vec.Z,
|
||||
Math.Sqrt(vec.X * vec.X + vec.Y * vec.Y)) * (180.0 / Math.PI));
|
||||
var yaw = (float)(Math.Atan2(vec.Y, vec.X) * (180.0 / Math.PI));
|
||||
return new QAngle(pitch, yaw, 0);
|
||||
}
|
||||
|
||||
private CEnvBeam? createBeamEnt(Vector start, Vector end) {
|
||||
var beam = Utilities.CreateEntityByName<CEnvBeam>("env_beam");
|
||||
if (beam == null) return null;
|
||||
beam.RenderMode = RenderMode_t.kRenderTransAlpha;
|
||||
beam.Width = 0.5f;
|
||||
beam.Render = Color.FromArgb(128, Color.Red);
|
||||
beam.EndPos.X = end.X;
|
||||
beam.EndPos.Y = end.Y;
|
||||
beam.EndPos.Z = end.Z;
|
||||
beam.Teleport(start);
|
||||
return beam;
|
||||
}
|
||||
|
||||
public record TripwireInstance(IOnlinePlayer owner, CEnvBeam Beam,
|
||||
CDynamicProp TripwireProp, Vector StartPos, Vector EndPos);
|
||||
}
|
||||
137
TTT/CS2/Items/Tripwire/TripwireMovementListener.cs
Normal file
137
TTT/CS2/Items/Tripwire/TripwireMovementListener.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Timers;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ShopAPI.Configs.Traitor;
|
||||
using TTT.API;
|
||||
using TTT.API.Events;
|
||||
using TTT.API.Game;
|
||||
using TTT.API.Player;
|
||||
using TTT.API.Role;
|
||||
using TTT.API.Storage;
|
||||
using TTT.CS2.Extensions;
|
||||
using TTT.CS2.RayTrace.Class;
|
||||
using TTT.CS2.RayTrace.Enum;
|
||||
using TTT.Game.Events.Body;
|
||||
using TTT.Game.Events.Game;
|
||||
using TTT.Game.Events.Player;
|
||||
using TTT.Game.Roles;
|
||||
|
||||
namespace TTT.CS2.Items.Tripwire;
|
||||
|
||||
public class TripwireMovementListener(IServiceProvider provider)
|
||||
: IPluginModule, IListener {
|
||||
private readonly IEventBus bus = provider.GetRequiredService<IEventBus>();
|
||||
|
||||
private readonly IPlayerConverter<CCSPlayerController> converter =
|
||||
provider.GetRequiredService<IPlayerConverter<CCSPlayerController>>();
|
||||
|
||||
private readonly IPlayerFinder finder =
|
||||
provider.GetRequiredService<IPlayerFinder>();
|
||||
|
||||
private readonly TripwireItem? item = provider.GetService<TripwireItem>();
|
||||
|
||||
private readonly Dictionary<string, TripwireItem.TripwireInstance>
|
||||
killedWithTripwire = new();
|
||||
|
||||
private readonly IRoleAssigner roles =
|
||||
provider.GetRequiredService<IRoleAssigner>();
|
||||
|
||||
private TripwireConfig config
|
||||
=> provider.GetService<IStorage<TripwireConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new TripwireConfig();
|
||||
|
||||
public void Dispose() { }
|
||||
public void Start() { }
|
||||
|
||||
public void Start(BasePlugin? plugin) {
|
||||
if (item == null) return;
|
||||
plugin?.AddTimer(0.1f, checkTripwires, TimerFlags.REPEAT);
|
||||
}
|
||||
|
||||
private void checkTripwires() {
|
||||
if (item == null) return;
|
||||
var toRemove = new List<TripwireItem.TripwireInstance>();
|
||||
foreach (var wire in item.ActiveTripwires) {
|
||||
var ray = TraceRay.TraceShape(wire.StartPos, wire.EndPos, Contents.Player,
|
||||
wire.TripwireProp.Handle);
|
||||
if (!ray.DidHit() || !ray.HitPlayer(out var player)) continue;
|
||||
|
||||
if (!config.FriendlyFireTriggers && player != null) {
|
||||
var apiPlayer = converter.GetPlayer(player);
|
||||
var role = roles.GetRoles(apiPlayer);
|
||||
if (role.Any(r => r is TraitorRole)) continue;
|
||||
}
|
||||
|
||||
toRemove.Add(wire);
|
||||
|
||||
wire.TripwireProp.EmitSound("Flashbang.ExplodeDistant");
|
||||
doExplosion(wire);
|
||||
}
|
||||
|
||||
foreach (var wire in toRemove) {
|
||||
item.ActiveTripwires.Remove(wire);
|
||||
wire.Beam.Remove();
|
||||
wire.TripwireProp.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
private void doExplosion(TripwireItem.TripwireInstance instance) {
|
||||
foreach (var player in finder.GetOnline()) {
|
||||
if (!player.IsAlive) continue;
|
||||
|
||||
var gamePlayer = converter.GetPlayer(player);
|
||||
if (gamePlayer == null || gamePlayer.Pawn.Value == null) continue;
|
||||
if (gamePlayer.Pawn.Value.AbsOrigin == null) continue;
|
||||
|
||||
var distance =
|
||||
instance.StartPos.Distance(gamePlayer.Pawn.Value.AbsOrigin);
|
||||
var damage = (int)Math.Round(getDamage(distance));
|
||||
|
||||
if (roles.GetRoles(player).Any(r => r is TraitorRole))
|
||||
damage = (int)(damage * config.FriendlyFireMultiplier);
|
||||
|
||||
if (damage < 1) continue;
|
||||
|
||||
if (player.Health - damage <= 0) {
|
||||
killedWithTripwire[player.Id] = instance;
|
||||
var death = new PlayerDeathEvent(player).WithKiller(instance.owner)
|
||||
.WithWeapon("[Tripwire]");
|
||||
bus.Dispatch(death);
|
||||
} else {
|
||||
var damaged =
|
||||
new PlayerDamagedEvent(player, instance.owner, damage) {
|
||||
Weapon = "[Tripwire]"
|
||||
};
|
||||
bus.Dispatch(damaged);
|
||||
}
|
||||
|
||||
player.Health -= damage;
|
||||
gamePlayer.EmitSound("Player.BurnDamage");
|
||||
}
|
||||
}
|
||||
|
||||
private float getDamage(float distance) {
|
||||
return config.ExplosionPower
|
||||
* MathF.Pow(MathF.E, -distance * config.FalloffDelay);
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
[EventHandler]
|
||||
public void OnGameEnd(GameStateUpdateEvent ev) {
|
||||
if (ev.NewState != State.FINISHED) return;
|
||||
|
||||
killedWithTripwire.Clear();
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
[EventHandler]
|
||||
public void OnRagdollSpawn(BodyCreateEvent ev) {
|
||||
if (!killedWithTripwire.TryGetValue(ev.Body.Id, out var info)) return;
|
||||
if (ev.Body.Killer != null && ev.Body.Killer.Id != ev.Body.OfPlayer.Id)
|
||||
return;
|
||||
ev.Body.Killer = info.owner;
|
||||
}
|
||||
}
|
||||
14
TTT/CS2/Items/Tripwire/TripwireMsgs.cs
Normal file
14
TTT/CS2/Items/Tripwire/TripwireMsgs.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using TTT.Locale;
|
||||
|
||||
namespace TTT.CS2.Items.Tripwire;
|
||||
|
||||
public class TripwireMsgs {
|
||||
public static IMsg SHOP_ITEM_TRIPWIRE
|
||||
=> MsgFactory.Create(nameof(SHOP_ITEM_TRIPWIRE));
|
||||
|
||||
public static IMsg SHOP_ITEM_TRIPWIRE_DESC
|
||||
=> MsgFactory.Create(nameof(SHOP_ITEM_TRIPWIRE_DESC));
|
||||
|
||||
public static IMsg SHOP_ITEM_TRIPWIRE_TOOFAR
|
||||
=> MsgFactory.Create(nameof(SHOP_ITEM_TRIPWIRE_TOOFAR));
|
||||
}
|
||||
@@ -34,6 +34,8 @@ public class BodyPickupListener(IServiceProvider provider)
|
||||
if (ev.Player is not IOnlinePlayer online)
|
||||
throw new InvalidOperationException("Player is not an online player.");
|
||||
|
||||
if (ev.Player.Id == body.OfPlayer.Id) return;
|
||||
|
||||
var identifyEvent = new BodyIdentifyEvent(body, online);
|
||||
|
||||
Bus.Dispatch(identifyEvent);
|
||||
|
||||
54
TTT/CS2/Listeners/WardenTagAssigner.cs
Normal file
54
TTT/CS2/Listeners/WardenTagAssigner.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using TTT.API.Events;
|
||||
using TTT.API.Player;
|
||||
using TTT.CS2.ThirdParties.eGO;
|
||||
using TTT.Game.Events.Player;
|
||||
using TTT.Game.Listeners;
|
||||
using TTT.Game.Roles;
|
||||
|
||||
namespace TTT.CS2.Listeners;
|
||||
|
||||
public class WardenTagAssigner(IServiceProvider provider)
|
||||
: BaseListener(provider) {
|
||||
private readonly IPlayerConverter<CCSPlayerController> converter =
|
||||
provider.GetRequiredService<IPlayerConverter<CCSPlayerController>>();
|
||||
|
||||
private readonly Dictionary<string, (string, char)> oldTags = new();
|
||||
|
||||
[UsedImplicitly]
|
||||
[EventHandler]
|
||||
public void OnRoleAssign(PlayerRoleAssignEvent ev) {
|
||||
var maul = EgoApi.MAUL.Get();
|
||||
if (maul == null) return;
|
||||
Server.NextWorldUpdate(() => {
|
||||
var gamePlayer = converter.GetPlayer(ev.Player);
|
||||
if (gamePlayer == null) return;
|
||||
|
||||
Task.Run(async () => {
|
||||
if (ev.Role is DetectiveRole) {
|
||||
var oldTag = await maul.getTagService().GetTag(gamePlayer.SteamID);
|
||||
var oldTagColor =
|
||||
await maul.getTagService().GetTagColor(gamePlayer.SteamID);
|
||||
oldTags[ev.Player.Id] = (oldTag, oldTagColor);
|
||||
}
|
||||
|
||||
await Server.NextWorldUpdateAsync(() => {
|
||||
if (ev.Role is DetectiveRole) {
|
||||
maul.getTagService().SetTag(gamePlayer, "[DETECTIVE]", false);
|
||||
maul.getTagService()
|
||||
.SetTagColor(gamePlayer, ChatColors.DarkBlue, false);
|
||||
} else if (oldTags.TryGetValue(ev.Player.Id, out var oldTag)) {
|
||||
maul.getTagService().SetTag(gamePlayer, oldTag.Item1, false);
|
||||
maul.getTagService().SetTagColor(gamePlayer, oldTag.Item2, false);
|
||||
|
||||
oldTags.Remove(ev.Player.Id);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -52,4 +52,8 @@ SHOP_ITEM_CLUSTER_GRENADE: "Cluster Grenade"
|
||||
SHOP_ITEM_CLUSTER_GRENADE_DESC: "A grenade that splits into multiple smaller grenades."
|
||||
|
||||
SHOP_ITEM_TELEPORT_DECOY: "Teleport Decoy"
|
||||
SHOP_ITEM_TELEPORT_DECOY_DESC: "A decoy that teleports you to it upon explosion."
|
||||
SHOP_ITEM_TELEPORT_DECOY_DESC: "A decoy that teleports you to it upon explosion."
|
||||
|
||||
SHOP_ITEM_TRIPWIRE: "Tripwire"
|
||||
SHOP_ITEM_TRIPWIRE_DESC: "A tripwire that explodes when triggered."
|
||||
SHOP_ITEM_TRIPWIRE_TOOFAR: "%PREFIX%You are too far away to place the tripwire."
|
||||
@@ -8,7 +8,7 @@ namespace TTT.Game.Events.Player;
|
||||
public class PlayerDamagedEvent(IOnlinePlayer player, IOnlinePlayer? attacker,
|
||||
int originalHp, int hpLeft) : PlayerEvent(player), ICancelableEvent {
|
||||
public PlayerDamagedEvent(IOnlinePlayer player, IOnlinePlayer? attacker,
|
||||
int damageDealt) : this(player, attacker, player.Health - damageDealt,
|
||||
int damageDealt) : this(player, attacker, player.Health + damageDealt,
|
||||
player.Health) { }
|
||||
|
||||
public PlayerDamagedEvent(IPlayerConverter<CCSPlayerController> converter,
|
||||
|
||||
@@ -9,11 +9,12 @@ public record TTTConfig {
|
||||
public Func<int, int> TraitorCount { get; init; } =
|
||||
p => (int)Math.Ceiling((p - 1) / 5f);
|
||||
|
||||
public Func<int, int> DetectiveCount { get; init; } =
|
||||
p => (int)Math.Floor(p / 8f);
|
||||
public Func<int, int> DetectiveCount { get; init; } = p
|
||||
=> (int)Math.Ceiling(Math.Floor(p / 8f) / 1.5f);
|
||||
|
||||
public Func<int, int> InnocentCount { get; init; } = p
|
||||
=> p - (int)Math.Ceiling((p - 1) / 5f) - (int)Math.Floor(p / 8f);
|
||||
=> p - (int)Math.Ceiling((p - 1) / 5f)
|
||||
- (int)Math.Ceiling(Math.Floor(p / 8f) / 1.5f);
|
||||
}
|
||||
|
||||
public record RoleConfig {
|
||||
|
||||
@@ -74,6 +74,12 @@ public class ListCommand(IServiceProvider provider) : ICommand, IItemSorter {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void InvalidateOrder(IOnlinePlayer? player) {
|
||||
if (player == null) return;
|
||||
cache.Remove(player.Id);
|
||||
lastUpdate.Remove(player.Id);
|
||||
}
|
||||
|
||||
private List<IShopItem> calculateSortedItems(IOnlinePlayer? player) {
|
||||
var items = new List<IShopItem>(shop.Items).Where(item
|
||||
=> player == null
|
||||
|
||||
@@ -72,4 +72,8 @@ public class ShopCommand(IServiceProvider provider) : ICommand, IItemSorter {
|
||||
public DateTime? GetLastUpdate(IOnlinePlayer? player) {
|
||||
return listCmd.GetLastUpdate(player);
|
||||
}
|
||||
|
||||
public void InvalidateOrder(IOnlinePlayer? player) {
|
||||
listCmd.InvalidateOrder(player);
|
||||
}
|
||||
}
|
||||
@@ -54,5 +54,6 @@ public class DeagleDamageListener(IServiceProvider provider)
|
||||
}
|
||||
|
||||
ev.HpLeft = -100;
|
||||
Messenger.Message(victim, Locale[DeagleMsgs.SHOP_ITEM_DEAGLE_VICTIM]);
|
||||
}
|
||||
}
|
||||
@@ -11,4 +11,7 @@ public class DeagleMsgs {
|
||||
|
||||
public static IMsg SHOP_ITEM_DEAGLE_HIT_FF
|
||||
=> MsgFactory.Create(nameof(SHOP_ITEM_DEAGLE_HIT_FF));
|
||||
|
||||
public static IMsg SHOP_ITEM_DEAGLE_VICTIM
|
||||
=> MsgFactory.Create(nameof(SHOP_ITEM_DEAGLE_VICTIM));
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
xmlns:s="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xml:space="preserve">
|
||||
<s:Boolean
|
||||
x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=items_005Coneshotdeagle/@EntryIndexedValue">True</s:Boolean>
|
||||
x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=items_005Coneshotdeagle/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean
|
||||
x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=lang/@EntryIndexedValue">True</s:Boolean>
|
||||
x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=lang/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean
|
||||
x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=shop/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=shop/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -13,6 +13,7 @@ using TTT.CS2.Items.PoisonSmoke;
|
||||
using TTT.CS2.Items.SilentAWP;
|
||||
using TTT.CS2.Items.Station;
|
||||
using TTT.CS2.Items.TeleportDecoy;
|
||||
using TTT.CS2.Items.Tripwire;
|
||||
using TTT.Shop.Commands;
|
||||
using TTT.Shop.Items;
|
||||
using TTT.Shop.Items.Detective.Stickers;
|
||||
@@ -61,5 +62,6 @@ public static class ShopServiceCollection {
|
||||
collection.AddStickerServices();
|
||||
collection.AddTaserServices();
|
||||
collection.AddTeleportDecoyServices();
|
||||
collection.AddTripwireServices();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ SHOP_ITEM_NOT_FOUND: "%SHOP_PREFIX%Could not find an item named \"{default}{0}{g
|
||||
SHOP_ITEM_DEAGLE: "One-Hit Revolver"
|
||||
SHOP_ITEM_DEAGLE_DESC: "A one-hit kill revolver with a single bullet. Aim carefully!"
|
||||
SHOP_ITEM_DEAGLE_HIT_FF: "%PREFIX%You hit a teammate!"
|
||||
SHOP_ITEM_DEAGLE_VICTIM: "%PREFIX%You were hit by a {yellow}One-Hit Revolver{grey}."
|
||||
|
||||
SHOP_ITEM_STICKERS: "Stickers"
|
||||
SHOP_ITEM_STICKERS_DESC: "Reveal the roles of all players you taser to others."
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Drawing;
|
||||
namespace ShopAPI.Configs;
|
||||
|
||||
public record BodyPaintConfig : ShopItemConfig {
|
||||
public override int Price { get; init; } = 40;
|
||||
public override int Price { get; init; } = 30;
|
||||
public int MaxUses { get; init; } = 4;
|
||||
public Color ColorToApply { get; init; } = Color.GreenYellow;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ShopAPI.Configs.Traitor;
|
||||
|
||||
public record CompassConfig : ShopItemConfig {
|
||||
public override int Price { get; init; } = 70;
|
||||
public override int Price { get; init; } = 60;
|
||||
public float MaxRange { get; init; } = 10000;
|
||||
public float CompassFOV { get; init; } = 120;
|
||||
public int CompassLength { get; init; } = 64;
|
||||
|
||||
11
TTT/ShopAPI/Configs/Traitor/TripwireConfig.cs
Normal file
11
TTT/ShopAPI/Configs/Traitor/TripwireConfig.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace ShopAPI.Configs.Traitor;
|
||||
|
||||
public record TripwireConfig : ShopItemConfig {
|
||||
public override int Price { get; init; } = 60;
|
||||
public int ExplosionPower { get; init; } = 1000;
|
||||
public float FalloffDelay { get; init; } = 0.02f;
|
||||
public float FriendlyFireMultiplier { get; init; } = 0.5f;
|
||||
public bool FriendlyFireTriggers { get; init; } = true;
|
||||
public float MaxPlacementDistanceSquared { get; init; } = 400f * 400f;
|
||||
public TimeSpan TripwireInitiationTime { get; init; } = TimeSpan.FromSeconds(2);
|
||||
}
|
||||
@@ -5,4 +5,5 @@ namespace ShopAPI;
|
||||
public interface IItemSorter {
|
||||
List<IShopItem> GetSortedItems(IOnlinePlayer? player, bool refresh = false);
|
||||
DateTime? GetLastUpdate(IOnlinePlayer? player);
|
||||
void InvalidateOrder(IOnlinePlayer? player);
|
||||
}
|
||||
@@ -40,7 +40,7 @@ public enum PurchaseResult {
|
||||
/// <summary>
|
||||
/// The item cannot be purchased multiple times, and the player already owns it.
|
||||
/// </summary>
|
||||
ALREADY_OWNED
|
||||
ALREADY_OWNED,
|
||||
}
|
||||
|
||||
public static class PurchaseResultExtensions {
|
||||
@@ -58,7 +58,7 @@ public static class PurchaseResultExtensions {
|
||||
PurchaseResult.WRONG_ROLE =>
|
||||
"You do not have the required role to purchase this item",
|
||||
PurchaseResult.ALREADY_OWNED =>
|
||||
"You already own this item and cannot purchase it again",
|
||||
"You have purchased the maximum amount of this item",
|
||||
_ => "An unexpected error occurred"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SpecialRound.lang;
|
||||
using SpecialRoundAPI;
|
||||
using SpecialRoundAPI.Configs;
|
||||
using TTT.API.Game;
|
||||
using TTT.API.Storage;
|
||||
using TTT.Game.Events.Game;
|
||||
|
||||
66
TTT/SpecialRound/Rounds/PistolRound.cs
Normal file
66
TTT/SpecialRound/Rounds/PistolRound.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SpecialRound.lang;
|
||||
using SpecialRoundAPI;
|
||||
using SpecialRoundAPI.Configs;
|
||||
using TTT.API;
|
||||
using TTT.API.Game;
|
||||
using TTT.API.Player;
|
||||
using TTT.API.Storage;
|
||||
using TTT.Game.Events.Game;
|
||||
using TTT.Locale;
|
||||
|
||||
namespace SpecialRound.Rounds;
|
||||
|
||||
public class PistolRound(IServiceProvider provider)
|
||||
: AbstractSpecialRound(provider), IPluginModule {
|
||||
private readonly IInventoryManager inventory = provider
|
||||
.GetRequiredService<IInventoryManager>();
|
||||
|
||||
private BasePlugin? plugin;
|
||||
public override string Name => "Pistol";
|
||||
public override IMsg Description => RoundMsgs.SPECIAL_ROUND_PISTOL;
|
||||
|
||||
private PistolRoundConfig config
|
||||
=> Provider.GetService<IStorage<PistolRoundConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new PistolRoundConfig();
|
||||
|
||||
public override SpecialRoundConfig Config => config;
|
||||
|
||||
public void Start(BasePlugin? newPluing) { plugin = newPluing; }
|
||||
|
||||
public override void ApplyRoundEffects() {
|
||||
VirtualFunctions.CCSPlayer_ItemServices_CanAcquireFunc.Hook(canAcquire,
|
||||
HookMode.Pre);
|
||||
foreach (var player in Finder.GetOnline())
|
||||
inventory.RemoveWeaponInSlot(player, 0);
|
||||
}
|
||||
|
||||
private HookResult canAcquire(DynamicHook hook) {
|
||||
var player = hook.GetParam<CCSPlayer_ItemServices>(0)
|
||||
.Pawn.Value.Controller.Value?.As<CCSPlayerController>();
|
||||
var data = VirtualFunctions.GetCSWeaponDataFromKey.Invoke(-1,
|
||||
hook.GetParam<CEconItemView>(1).ItemDefinitionIndex.ToString());
|
||||
|
||||
if (player == null || !player.IsValid) return HookResult.Continue;
|
||||
|
||||
if (Tag.RIFLES.Contains(data.Name)) {
|
||||
hook.SetReturn(AcquireResult.NotAllowedByMode);
|
||||
return HookResult.Handled;
|
||||
}
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
public override void OnGameState(GameStateUpdateEvent ev) {
|
||||
if (ev.NewState != State.FINISHED) return;
|
||||
|
||||
VirtualFunctions.CCSPlayer_ItemServices_CanAcquireFunc.Unhook(canAcquire,
|
||||
HookMode.Pre);
|
||||
}
|
||||
}
|
||||
30
TTT/SpecialRound/Rounds/SilentRound.cs
Normal file
30
TTT/SpecialRound/Rounds/SilentRound.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SpecialRound.lang;
|
||||
using SpecialRoundAPI;
|
||||
using SpecialRoundAPI.Configs;
|
||||
using TTT.API.Storage;
|
||||
using TTT.Game.Events.Game;
|
||||
using TTT.Locale;
|
||||
|
||||
namespace SpecialRound.Rounds;
|
||||
|
||||
public class SilentRound(IServiceProvider provider)
|
||||
: AbstractSpecialRound(provider) {
|
||||
public override string Name => "Silent";
|
||||
public override IMsg Description => RoundMsgs.SPECIAL_ROUND_SILENT;
|
||||
public override SpecialRoundConfig Config => config;
|
||||
|
||||
private SilentRoundConfig config
|
||||
=> Provider.GetService<IStorage<SilentRoundConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new SilentRoundConfig();
|
||||
|
||||
public override void ApplyRoundEffects() {
|
||||
foreach (var player in Utilities.GetPlayers())
|
||||
player.VoiceFlags |= VoiceFlags.Muted;
|
||||
}
|
||||
|
||||
public override void OnGameState(GameStateUpdateEvent ev) { }
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SpecialRound.lang;
|
||||
using SpecialRoundAPI;
|
||||
using SpecialRoundAPI.Configs;
|
||||
using TTT.API.Events;
|
||||
using TTT.API.Game;
|
||||
using TTT.API.Role;
|
||||
@@ -58,6 +59,8 @@ public class SpeedRound(IServiceProvider provider)
|
||||
}
|
||||
|
||||
private void setTime(TimeSpan span) {
|
||||
span = TimeSpan.FromSeconds(Math.Min(span.TotalSeconds,
|
||||
config.MaxTimeEver.TotalSeconds));
|
||||
Server.NextWorldUpdate(() => {
|
||||
RoundUtil.SetTimeRemaining((int)span.TotalSeconds);
|
||||
});
|
||||
|
||||
60
TTT/SpecialRound/Rounds/SuppressedRound.cs
Normal file
60
TTT/SpecialRound/Rounds/SuppressedRound.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.UserMessages;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SpecialRound.lang;
|
||||
using SpecialRoundAPI;
|
||||
using SpecialRoundAPI.Configs;
|
||||
using TTT.API;
|
||||
using TTT.API.Game;
|
||||
using TTT.API.Storage;
|
||||
using TTT.Game.Events.Game;
|
||||
using TTT.Locale;
|
||||
|
||||
namespace SpecialRound.Rounds;
|
||||
|
||||
public class SuppressedRound(IServiceProvider provider)
|
||||
: AbstractSpecialRound(provider), IPluginModule {
|
||||
private static readonly HashSet<uint> silencedWeapons = new() {
|
||||
1, // deagle
|
||||
2, // dual berettas
|
||||
3, // five seven
|
||||
30, // tec9
|
||||
32, // p2000
|
||||
36, // p250
|
||||
4, // glock
|
||||
61, // usp-s
|
||||
63, // cz75 auto
|
||||
64 // r8 revolver
|
||||
};
|
||||
|
||||
private BasePlugin? plugin;
|
||||
public override string Name => "Suppressed";
|
||||
public override IMsg Description => RoundMsgs.SPECIAL_ROUND_SUPPRESSED;
|
||||
public override SpecialRoundConfig Config => config;
|
||||
|
||||
private SuppressedRoundConfig config
|
||||
=> Provider.GetService<IStorage<SuppressedRoundConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new SuppressedRoundConfig();
|
||||
|
||||
public void Start(BasePlugin? newPlugin) { plugin ??= newPlugin; }
|
||||
|
||||
public override void ApplyRoundEffects() {
|
||||
plugin?.HookUserMessage(452, onWeaponSound);
|
||||
}
|
||||
|
||||
private HookResult onWeaponSound(UserMessage msg) {
|
||||
var defIndex = msg.ReadUInt("item_def_index");
|
||||
|
||||
if (!silencedWeapons.Contains(defIndex)) return HookResult.Continue;
|
||||
|
||||
msg.Recipients.Clear();
|
||||
return HookResult.Handled;
|
||||
}
|
||||
|
||||
public override void OnGameState(GameStateUpdateEvent ev) {
|
||||
if (ev.NewState != State.FINISHED) return;
|
||||
plugin?.UnhookUserMessage(452, onWeaponSound);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using ShopAPI.Events;
|
||||
using SpecialRound.lang;
|
||||
using SpecialRoundAPI;
|
||||
using SpecialRoundAPI.Configs;
|
||||
using TTT.API.Events;
|
||||
using TTT.API.Messages;
|
||||
using TTT.API.Storage;
|
||||
|
||||
@@ -13,5 +13,8 @@ public static class SpecialRoundCollection {
|
||||
services.AddModBehavior<SpeedRound>();
|
||||
services.AddModBehavior<BhopRound>();
|
||||
services.AddModBehavior<VanillaRound>();
|
||||
services.AddModBehavior<SuppressedRound>();
|
||||
services.AddModBehavior<SilentRound>();
|
||||
services.AddModBehavior<PistolRound>();
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
public record SpecialRoundsConfig {
|
||||
public int MinRoundsBetweenSpecial { get; init; } = 3;
|
||||
public int MinPlayersForSpecial { get; init; } = 8;
|
||||
public int MinPlayersForSpecial { get; init; } = 5;
|
||||
public int MinRoundsAfterMapChange { get; init; } = 2;
|
||||
public float SpecialRoundChance { get; init; } = 0.2f;
|
||||
}
|
||||
@@ -16,6 +16,15 @@ public class RoundMsgs {
|
||||
public static IMsg VANILLA_ROUND_REMINDER
|
||||
=> MsgFactory.Create(nameof(VANILLA_ROUND_REMINDER));
|
||||
|
||||
public static IMsg SPECIAL_ROUND_SUPPRESSED
|
||||
=> MsgFactory.Create(nameof(SPECIAL_ROUND_SUPPRESSED));
|
||||
|
||||
public static IMsg SPECIAL_ROUND_SILENT
|
||||
=> MsgFactory.Create(nameof(SPECIAL_ROUND_SILENT));
|
||||
|
||||
public static IMsg SPECIAL_ROUND_PISTOL
|
||||
=> MsgFactory.Create(nameof(SPECIAL_ROUND_PISTOL));
|
||||
|
||||
public static IMsg SPECIAL_ROUND_STARTED(AbstractSpecialRound round) {
|
||||
return MsgFactory.Create(nameof(SPECIAL_ROUND_STARTED), round.Name);
|
||||
}
|
||||
|
||||
@@ -2,4 +2,7 @@
|
||||
SPECIAL_ROUND_SPEED: " {yellow}SPEED{grey}: The round is faster than usual! {red}Traitors{grey} must kill to gain more time."
|
||||
SPECIAL_ROUND_BHOP: " {Yellow}BHOP{grey}: Bunny hopping is enabled! Hold jump to move faster!"
|
||||
SPECIAL_ROUND_VANILLA: " {green}VANILLA{grey}: The shop has been disabled!"
|
||||
SPECIAL_ROUND_SUPPRESSED: " {grey}SUPPRESSED{grey}: All pistols are silent!"
|
||||
SPECIAL_ROUND_SILENT: " {grey}SILENT{grey}: All players are muted!"
|
||||
SPECIAL_ROUND_PISTOL: " {blue}PISTOL{grey}: You can only use pistols this round!"
|
||||
VANILLA_ROUND_REMINDER: "%SHOP_PREFIX%This is a {purple}Vanilla{grey} round. The shop is disabled."
|
||||
@@ -53,10 +53,10 @@ public class RoleAssignerTest(IServiceProvider provider) {
|
||||
[InlineData(9, 6, 2, 1)]
|
||||
[InlineData(10, 7, 2, 1)]
|
||||
[InlineData(20, 14, 4, 2)]
|
||||
[InlineData(30, 21, 6, 3)]
|
||||
[InlineData(32, 21, 7, 4)]
|
||||
[InlineData(60, 41, 12, 7)]
|
||||
[InlineData(64, 43, 13, 8)]
|
||||
[InlineData(30, 22, 6, 2)]
|
||||
[InlineData(32, 22, 7, 3)]
|
||||
[InlineData(60, 43, 12, 5)]
|
||||
[InlineData(64, 45, 13, 6)]
|
||||
public void AssignRole_AssignsBalanced_Roles(int players, int innos,
|
||||
int traitors, int detectives) {
|
||||
var playerList = new HashSet<IOnlinePlayer>();
|
||||
|
||||
1014
licenses/CounterStrikeSharp.API__1.0.342.html
Normal file
1014
licenses/CounterStrikeSharp.API__1.0.342.html
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user