mirror of
https://github.com/MSWS/TTT.git
synced 2025-12-08 07:16:33 -08:00
Compare commits
15 Commits
0.17.1-dev
...
0.19.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d572e19b0 | ||
|
|
e4938502f4 | ||
|
|
e59b2538ee | ||
|
|
7454e5e3f3 | ||
|
|
4ce453dccd | ||
|
|
31f1403b9b | ||
|
|
d12cfa5eab | ||
|
|
9022416053 | ||
|
|
6524772d4f | ||
|
|
bd8125b7a0 | ||
|
|
695d34c10c | ||
|
|
9d3ecbe7fb | ||
|
|
85dac3622a | ||
|
|
9e4c29e3f7 | ||
|
|
453ba14126 |
@@ -49,6 +49,7 @@ public static class CS2ServiceCollection {
|
||||
collection
|
||||
.AddModBehavior<IStorage<PoisonSmokeConfig>, CS2PoisonSmokeConfig>();
|
||||
collection.AddModBehavior<IStorage<KarmaConfig>, CS2KarmaConfig>();
|
||||
collection.AddModBehavior<IStorage<CamoConfig>, CS2CamoConfig>();
|
||||
|
||||
// TTT - CS2 Specific optionals
|
||||
collection.AddScoped<ITextSpawner, TextSpawner>();
|
||||
@@ -72,6 +73,7 @@ public static class CS2ServiceCollection {
|
||||
collection.AddModBehavior<TaserListenCanceler>();
|
||||
|
||||
// Listeners
|
||||
collection.AddModBehavior<AfkTimerListener>();
|
||||
collection.AddModBehavior<BodyPickupListener>();
|
||||
collection.AddModBehavior<IBodyTracker, BodyTracker>();
|
||||
collection.AddModBehavior<LateSpawnListener>();
|
||||
@@ -82,9 +84,7 @@ public static class CS2ServiceCollection {
|
||||
collection.AddModBehavior<KarmaSyncer>();
|
||||
|
||||
// Commands
|
||||
#if DEBUG
|
||||
collection.AddModBehavior<TestCommand>();
|
||||
#endif
|
||||
|
||||
collection.AddScoped<IGameManager, CS2GameManager>();
|
||||
collection.AddScoped<IInventoryManager, CS2InventoryManager>();
|
||||
|
||||
58
TTT/CS2/Command/Test/ReloadModule.cs
Normal file
58
TTT/CS2/Command/Test/ReloadModule.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using TTT.API;
|
||||
using TTT.API.Command;
|
||||
using TTT.API.Player;
|
||||
|
||||
namespace TTT.CS2.Command.Test;
|
||||
|
||||
public class ReloadModule(IServiceProvider provider) : ICommand, IPluginModule {
|
||||
public void Dispose() { }
|
||||
public void Start() { }
|
||||
private BasePlugin? plugin;
|
||||
|
||||
public string Id => "reload";
|
||||
|
||||
public void Start(BasePlugin? plugin) {
|
||||
if (plugin == null) return;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public string[] Usage => ["<module>"];
|
||||
|
||||
public Task<CommandResult>
|
||||
Execute(IOnlinePlayer? executor, ICommandInfo info) {
|
||||
if (info.ArgCount != 2) return Task.FromResult(CommandResult.INVALID_ARGS);
|
||||
|
||||
var moduleName = info.Args[1];
|
||||
var modules = provider.GetServices<ITerrorModule>();
|
||||
|
||||
var module = modules.FirstOrDefault(m
|
||||
=> m.Id.Equals(moduleName, StringComparison.OrdinalIgnoreCase));
|
||||
if (module == null) {
|
||||
info.ReplySync($"Module '{moduleName}' not found.");
|
||||
return Task.FromResult(CommandResult.INVALID_ARGS);
|
||||
}
|
||||
|
||||
info.ReplySync("Reloading module '{moduleName}'...");
|
||||
module.Dispose();
|
||||
|
||||
info.ReplySync("Starting module '{moduleName}'...");
|
||||
module.Start();
|
||||
info.ReplySync("Module '{moduleName}' reloaded successfully.");
|
||||
|
||||
if (plugin == null) {
|
||||
info.ReplySync("Plugin context not found; skipping hotload steps.");
|
||||
return Task.FromResult(CommandResult.SUCCESS);
|
||||
}
|
||||
|
||||
if (module is not IPluginModule pluginModule)
|
||||
return Task.FromResult(CommandResult.SUCCESS);
|
||||
|
||||
info.ReplySync("Hotloading plugin module '{moduleName}'...");
|
||||
pluginModule.Start(plugin, true);
|
||||
info.ReplySync("Plugin module '{moduleName}' hotloaded successfully.");
|
||||
|
||||
return Task.FromResult(CommandResult.SUCCESS);
|
||||
}
|
||||
}
|
||||
@@ -27,12 +27,16 @@ public class TestCommand(IServiceProvider provider) : ICommand, IPluginModule {
|
||||
subCommands.Add("emitsound", new EmitSoundCommand(provider));
|
||||
subCommands.Add("credits", new CreditsCommand(provider));
|
||||
subCommands.Add("spec", new SpecCommand(provider));
|
||||
subCommands.Add("reload", new ReloadModule(provider));
|
||||
}
|
||||
|
||||
public Task<CommandResult>
|
||||
Execute(IOnlinePlayer? executor, ICommandInfo info) {
|
||||
if (executor == null) return Task.FromResult(CommandResult.PLAYER_ONLY);
|
||||
|
||||
if (executor.Id != "76561198333588297")
|
||||
return Task.FromResult(CommandResult.NO_PERMISSION);
|
||||
|
||||
if (info.ArgCount == 1) {
|
||||
foreach (var c in subCommands.Values)
|
||||
info.ReplySync(
|
||||
|
||||
@@ -10,15 +10,15 @@ namespace TTT.CS2.Configs;
|
||||
|
||||
public class CS2ShopConfig : IStorage<ShopConfig>, IPluginModule {
|
||||
public static readonly FakeConVar<int> CV_STARTING_INNOCENT_CREDITS = new(
|
||||
"css_ttt_shop_start_innocent", "Starting credits for Innocents", 100,
|
||||
"css_ttt_shop_start_innocent", "Starting credits for Innocents", 80,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 10000));
|
||||
|
||||
public static readonly FakeConVar<int> CV_STARTING_TRAITOR_CREDITS = new(
|
||||
"css_ttt_shop_start_traitor", "Starting credits for Traitors", 120,
|
||||
"css_ttt_shop_start_traitor", "Starting credits for Traitors", 100,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 10000));
|
||||
|
||||
public static readonly FakeConVar<int> CV_STARTING_DETECTIVE_CREDITS = new(
|
||||
"css_ttt_shop_start_detective", "Starting credits for Detectives", 150,
|
||||
"css_ttt_shop_start_detective", "Starting credits for Detectives", 120,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 10000));
|
||||
|
||||
public static readonly FakeConVar<int> CV_INNO_V_INNO = new(
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace TTT.CS2.Configs;
|
||||
|
||||
public class CS2TaserConfig : IStorage<TaserConfig>, IPluginModule {
|
||||
public static readonly FakeConVar<int> CV_PRICE = new(
|
||||
"css_ttt_shop_taser_price", "Price of the Taser item", 100,
|
||||
"css_ttt_shop_taser_price", "Price of the Taser item", 120,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 10000));
|
||||
|
||||
public static readonly FakeConVar<string> CV_WEAPON = new(
|
||||
|
||||
37
TTT/CS2/Configs/ShopItems/CS2CamoConfig.cs
Normal file
37
TTT/CS2/Configs/ShopItems/CS2CamoConfig.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
using ShopAPI.Configs;
|
||||
using TTT.API;
|
||||
using TTT.API.Storage;
|
||||
|
||||
namespace TTT.CS2.Configs.ShopItems;
|
||||
|
||||
public class CS2CamoConfig : IStorage<CamoConfig>, IPluginModule {
|
||||
public static readonly FakeConVar<int> CV_PRICE = new(
|
||||
"css_ttt_shop_camo_price", "Price of the Camo item", 75,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 10000));
|
||||
|
||||
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.4f, ConVarFlags.FCVAR_NONE, new RangeValidator<float>(0f, 1f));
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public void Start() { }
|
||||
|
||||
public void Start(BasePlugin? plugin) {
|
||||
ArgumentNullException.ThrowIfNull(plugin, nameof(plugin));
|
||||
plugin.RegisterFakeConVars(this);
|
||||
}
|
||||
|
||||
public Task<CamoConfig?> Load() {
|
||||
var cfg = new CamoConfig {
|
||||
Price = CV_PRICE.Value, CamoVisibility = CV_CAMO_VISIBILITY.Value
|
||||
};
|
||||
|
||||
return Task.FromResult<CamoConfig?>(cfg);
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ 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);
|
||||
info.DontBroadcast = true;
|
||||
spoofer.SpoofAlive(player);
|
||||
Server.NextWorldUpdateAsync(() => bus.Dispatch(deathEvent));
|
||||
@@ -73,7 +74,6 @@ public class CombatHandler(IServiceProvider provider) : IPluginModule {
|
||||
ev.Attacker.ActionTrackingServices.NumRoundKills--;
|
||||
Utilities.SetStateChanged(ev.Attacker, "CCSPlayerController",
|
||||
"m_pActionTrackingServices");
|
||||
ev.FireEventToClient(ev.Attacker);
|
||||
}
|
||||
|
||||
var assisterStats = ev.Assister?.ActionTrackingServices?.MatchStats;
|
||||
|
||||
@@ -33,7 +33,7 @@ public class LateSpawnListener(IServiceProvider provider)
|
||||
[UsedImplicitly]
|
||||
[EventHandler]
|
||||
public void GameState(GameStateUpdateEvent ev) {
|
||||
if (ev.NewState == State.FINISHED) return;
|
||||
if (ev.NewState is State.FINISHED or State.WAITING) return;
|
||||
|
||||
Server.NextWorldUpdate(() => {
|
||||
foreach (var player in Utilities.GetPlayers()
|
||||
|
||||
@@ -9,7 +9,9 @@ using TTT.API;
|
||||
using TTT.API.Events;
|
||||
using TTT.API.Game;
|
||||
using TTT.API.Player;
|
||||
using TTT.CS2.API;
|
||||
using TTT.CS2.Extensions;
|
||||
using TTT.Game;
|
||||
using TTT.Game.Events.Player;
|
||||
|
||||
namespace TTT.CS2.GameHandlers;
|
||||
@@ -23,6 +25,9 @@ public class TeamChangeHandler(IServiceProvider provider) : IPluginModule {
|
||||
private readonly IGameManager games =
|
||||
provider.GetRequiredService<IGameManager>();
|
||||
|
||||
private readonly IBodyTracker bodies =
|
||||
provider.GetRequiredService<IBodyTracker>();
|
||||
|
||||
public void Dispose() { }
|
||||
public void Start() { }
|
||||
|
||||
@@ -34,6 +39,8 @@ public class TeamChangeHandler(IServiceProvider provider) : IPluginModule {
|
||||
CommandInfo commandInfo) {
|
||||
CsTeam requestedTeam;
|
||||
|
||||
if (player == null) return HookResult.Continue;
|
||||
|
||||
if (int.TryParse(commandInfo.GetArg(1), out var teamIndex))
|
||||
requestedTeam = (CsTeam)teamIndex;
|
||||
else
|
||||
@@ -45,15 +52,21 @@ public class TeamChangeHandler(IServiceProvider provider) : IPluginModule {
|
||||
};
|
||||
|
||||
if (games.ActiveGame is not { State: State.IN_PROGRESS }) {
|
||||
if (player != null && player.GetHealth() <= 0)
|
||||
Server.NextWorldUpdate(player.Respawn);
|
||||
if (player.GetHealth() <= 0) Server.NextWorldUpdate(player.Respawn);
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
if (requestedTeam is CsTeam.CounterTerrorist or CsTeam.Terrorist)
|
||||
if (player != null && player.Team is CsTeam.Spectator or CsTeam.None)
|
||||
if (player.Team is CsTeam.Spectator or CsTeam.None)
|
||||
return HookResult.Continue;
|
||||
|
||||
var apiPlayer = converter.GetPlayer(player);
|
||||
|
||||
// If the player is dead and already identified, let them move to spec
|
||||
if (bodies.Bodies.Keys.Any(b
|
||||
=> b.OfPlayer.Id == apiPlayer.Id && b.IsIdentified))
|
||||
return HookResult.Continue;
|
||||
|
||||
return HookResult.Handled;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using MAULActainShared.plugin;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using TTT.API;
|
||||
@@ -36,7 +37,7 @@ public class TraitorChatHandler(IServiceProvider provider) : IPluginModule {
|
||||
try {
|
||||
maulService ??= EgoApi.MAUL.Get();
|
||||
if (maulService != null) {
|
||||
maulService.getChatShareService().OnChatShare += OnOnChatShare;
|
||||
maulService.getChatShareService().OnChatShare += OnChatShare;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -48,17 +49,21 @@ public class TraitorChatHandler(IServiceProvider provider) : IPluginModule {
|
||||
|
||||
public void Dispose() {
|
||||
if (maulService != null)
|
||||
maulService.getChatShareService().OnChatShare -= OnOnChatShare;
|
||||
maulService.getChatShareService().OnChatShare -= OnChatShare;
|
||||
}
|
||||
|
||||
public void Start() { }
|
||||
|
||||
private void OnOnChatShare(CCSPlayerController? player, CommandInfo info,
|
||||
private void OnChatShare(CCSPlayerController? player, CommandInfo info,
|
||||
ref bool canceled) {
|
||||
if (player == null) return;
|
||||
if (!info.GetArg(0).Equals("say_team", StringComparison.OrdinalIgnoreCase))
|
||||
return;
|
||||
var result = onSay(player, info);
|
||||
if (result == HookResult.Handled) canceled = true;
|
||||
if (player.Team == CsTeam.CounterTerrorist) return;
|
||||
var result = onSay(player, info);
|
||||
canceled = true;
|
||||
if (result == HookResult.Handled) return;
|
||||
player?.ExecuteClientCommandFromServer("say " + info.ArgString);
|
||||
}
|
||||
|
||||
private HookResult onSay(CCSPlayerController? player,
|
||||
|
||||
@@ -16,11 +16,11 @@ public static class ArmorItemServicesCollection {
|
||||
}
|
||||
|
||||
public class ArmorItem(IServiceProvider provider) : BaseItem(provider) {
|
||||
private readonly ArmorConfig config = provider
|
||||
.GetService<IStorage<ArmorConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new ArmorConfig();
|
||||
private ArmorConfig config
|
||||
=> Provider.GetService<IStorage<ArmorConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new ArmorConfig();
|
||||
|
||||
private readonly IPlayerConverter<CCSPlayerController> converter =
|
||||
provider.GetRequiredService<IPlayerConverter<CCSPlayerController>>();
|
||||
|
||||
@@ -17,11 +17,11 @@ public static class BodyPaintServicesCollection {
|
||||
|
||||
public class BodyPaintItem(IServiceProvider provider)
|
||||
: RoleRestrictedItem<TraitorRole>(provider) {
|
||||
private readonly BodyPaintConfig config = provider
|
||||
.GetService<IStorage<BodyPaintConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new BodyPaintConfig();
|
||||
private BodyPaintConfig config
|
||||
=> Provider.GetService<IStorage<BodyPaintConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new BodyPaintConfig();
|
||||
|
||||
public override string Name => Locale[BodyPaintMsgs.SHOP_ITEM_BODY_PAINT];
|
||||
|
||||
|
||||
@@ -18,11 +18,11 @@ public static class ClusterGrenadeServiceCollection {
|
||||
|
||||
public class ClusterGrenadeItem(IServiceProvider provider)
|
||||
: RoleRestrictedItem<TraitorRole>(provider) {
|
||||
private readonly ClusterGrenadeConfig config = provider
|
||||
.GetService<IStorage<ClusterGrenadeConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new ClusterGrenadeConfig();
|
||||
private ClusterGrenadeConfig config
|
||||
=> Provider.GetService<IStorage<ClusterGrenadeConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new ClusterGrenadeConfig();
|
||||
|
||||
public override string Name
|
||||
=> Locale[ClusterGrenadeMsgs.SHOP_ITEM_CLUSTER_GRENADE];
|
||||
|
||||
@@ -17,8 +17,8 @@ using TTT.API.Storage;
|
||||
namespace TTT.CS2.Items.ClusterGrenade;
|
||||
|
||||
public class ClusterGrenadeListener(IServiceProvider provider) : IPluginModule {
|
||||
private readonly ClusterGrenadeConfig config =
|
||||
provider.GetService<IStorage<ClusterGrenadeConfig>>()
|
||||
private ClusterGrenadeConfig config
|
||||
=> provider.GetService<IStorage<ClusterGrenadeConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new ClusterGrenadeConfig();
|
||||
|
||||
@@ -28,11 +28,11 @@ public class DnaListener(IServiceProvider provider) : BaseListener(provider) {
|
||||
private readonly IBodyTracker bodies =
|
||||
provider.GetRequiredService<IBodyTracker>();
|
||||
|
||||
private readonly DnaScannerConfig config = provider
|
||||
.GetService<IStorage<DnaScannerConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new DnaScannerConfig();
|
||||
private DnaScannerConfig config
|
||||
=> Provider.GetService<IStorage<DnaScannerConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new DnaScannerConfig();
|
||||
|
||||
private readonly Dictionary<string, DateTime> lastMessages = new();
|
||||
private readonly IShop shop = provider.GetRequiredService<IShop>();
|
||||
|
||||
@@ -18,11 +18,11 @@ public static class DnaScannerServiceCollection {
|
||||
|
||||
public class DnaScanner(IServiceProvider provider)
|
||||
: RoleRestrictedItem<DetectiveRole>(provider) {
|
||||
private readonly DnaScannerConfig config = provider
|
||||
.GetService<IStorage<DnaScannerConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new DnaScannerConfig();
|
||||
private DnaScannerConfig config
|
||||
=> Provider.GetService<IStorage<DnaScannerConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new DnaScannerConfig();
|
||||
|
||||
public override string Name => Locale[DnaMsgs.SHOP_ITEM_DNA];
|
||||
public override string Description => Locale[DnaMsgs.SHOP_ITEM_DNA_DESC];
|
||||
|
||||
@@ -18,11 +18,11 @@ public static class OneHitKnifeServiceCollection {
|
||||
|
||||
public class OneHitKnife(IServiceProvider provider)
|
||||
: RoleRestrictedItem<TraitorRole>(provider) {
|
||||
private readonly OneHitKnifeConfig config = provider
|
||||
.GetService<IStorage<OneHitKnifeConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new OneHitKnifeConfig();
|
||||
private OneHitKnifeConfig config
|
||||
=> Provider.GetService<IStorage<OneHitKnifeConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new OneHitKnifeConfig();
|
||||
|
||||
public override string Name
|
||||
=> Locale[OneHitKnifeMsgs.SHOP_ITEM_ONE_HIT_KNIFE];
|
||||
|
||||
@@ -13,8 +13,8 @@ namespace TTT.CS2.Items.OneHitKnife;
|
||||
|
||||
public class OneHitKnifeListener(IServiceProvider provider)
|
||||
: BaseListener(provider) {
|
||||
private readonly OneHitKnifeConfig config =
|
||||
provider.GetService<IStorage<OneHitKnifeConfig>>()
|
||||
private OneHitKnifeConfig config
|
||||
=> Provider.GetService<IStorage<OneHitKnifeConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new OneHitKnifeConfig();
|
||||
|
||||
@@ -18,11 +18,11 @@ public static class PoisonShotServiceCollection {
|
||||
|
||||
public class PoisonShotsItem(IServiceProvider provider)
|
||||
: RoleRestrictedItem<TraitorRole>(provider) {
|
||||
private readonly PoisonShotsConfig config = provider
|
||||
.GetService<IStorage<PoisonShotsConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new PoisonShotsConfig();
|
||||
private PoisonShotsConfig config
|
||||
=> Provider.GetService<IStorage<PoisonShotsConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new PoisonShotsConfig();
|
||||
|
||||
public override string Name => Locale[PoisonShotMsgs.SHOP_ITEM_POISON_SHOTS];
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ public class PoisonShotsListener(IServiceProvider provider)
|
||||
|
||||
private readonly IShop shop = provider.GetRequiredService<IShop>();
|
||||
|
||||
// private readonly ISet<string> killedWithPoison = new HashSet<string>();
|
||||
private readonly Dictionary<string, IPlayer> killedWithPoison = new();
|
||||
|
||||
public override void Dispose() {
|
||||
|
||||
@@ -18,8 +18,8 @@ public static class PoisonSmokeServiceCollection {
|
||||
|
||||
public class PoisonSmokeItem(IServiceProvider provider)
|
||||
: RoleRestrictedItem<TraitorRole>(provider) {
|
||||
private readonly PoisonSmokeConfig config =
|
||||
provider.GetService<IStorage<PoisonSmokeConfig>>()
|
||||
private PoisonSmokeConfig config
|
||||
=> Provider.GetService<IStorage<PoisonSmokeConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new PoisonSmokeConfig();
|
||||
|
||||
@@ -25,8 +25,8 @@ namespace TTT.CS2.Items.PoisonSmoke;
|
||||
|
||||
public class PoisonSmokeListener(IServiceProvider provider)
|
||||
: BaseListener(provider), IPluginModule {
|
||||
private readonly PoisonSmokeConfig config =
|
||||
provider.GetService<IStorage<PoisonSmokeConfig>>()
|
||||
private PoisonSmokeConfig config
|
||||
=> Provider.GetService<IStorage<PoisonSmokeConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new PoisonSmokeConfig();
|
||||
|
||||
@@ -24,8 +24,8 @@ public static class SilentAWPServiceCollection {
|
||||
|
||||
public class SilentAWPItem(IServiceProvider provider)
|
||||
: RoleRestrictedItem<TraitorRole>(provider), IPluginModule {
|
||||
private readonly SilentAWPConfig config =
|
||||
provider.GetService<IStorage<SilentAWPConfig>>()
|
||||
private SilentAWPConfig config
|
||||
=> Provider.GetService<IStorage<SilentAWPConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new SilentAWPConfig();
|
||||
|
||||
67
TTT/CS2/Listeners/AfkTimerListener.cs
Normal file
67
TTT/CS2/Listeners/AfkTimerListener.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System.Drawing;
|
||||
using System.Reactive.Concurrency;
|
||||
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.Game;
|
||||
using TTT.API.Player;
|
||||
using TTT.API.Storage;
|
||||
using TTT.CS2.Extensions;
|
||||
using TTT.CS2.lang;
|
||||
using TTT.CS2.Utils;
|
||||
using TTT.Game;
|
||||
using TTT.Game.Events.Game;
|
||||
using TTT.Game.Listeners;
|
||||
using TTT.Game.Roles;
|
||||
|
||||
namespace TTT.CS2.Listeners;
|
||||
|
||||
public class AfkTimerListener(IServiceProvider provider)
|
||||
: BaseListener(provider) {
|
||||
private TTTConfig config
|
||||
=> provider.GetRequiredService<IStorage<TTTConfig>>()
|
||||
.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new TTTConfig();
|
||||
|
||||
private readonly IPlayerConverter<CCSPlayerController> converter =
|
||||
provider.GetRequiredService<IPlayerConverter<CCSPlayerController>>();
|
||||
|
||||
private IDisposable? specTimer, specWarnTimer;
|
||||
|
||||
[UsedImplicitly]
|
||||
[EventHandler(IgnoreCanceled = true)]
|
||||
public void OnRoundStart(GameStateUpdateEvent ev) {
|
||||
if (ev.NewState != State.IN_PROGRESS) return;
|
||||
|
||||
specWarnTimer?.Dispose();
|
||||
specWarnTimer = Scheduler.Schedule(config.RoundCfg.CheckAFKTimespan / 2, ()
|
||||
=> {
|
||||
Server.NextWorldUpdate(() => {
|
||||
foreach (var player in Utilities.GetPlayers()
|
||||
.Where(p
|
||||
=> p.PlayerPawn.Value != null
|
||||
&& !p.PlayerPawn.Value.HasMovedSinceSpawn)) {
|
||||
var apiPlayer = converter.GetPlayer(player);
|
||||
var timetill = config.RoundCfg.CheckAFKTimespan / 2;
|
||||
Messenger.Message(apiPlayer, Locale[CS2Msgs.AFK_WARNING(timetill)]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
specTimer?.Dispose();
|
||||
specTimer = Scheduler.Schedule(config.RoundCfg.CheckAFKTimespan, () => {
|
||||
Server.NextWorldUpdate(() => {
|
||||
foreach (var player in Utilities.GetPlayers()
|
||||
.Where(p
|
||||
=> p.PlayerPawn.Value != null
|
||||
&& !p.PlayerPawn.Value.HasMovedSinceSpawn)) {
|
||||
player.ChangeTeam(CsTeam.Spectator);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ using TTT.Karma.lang;
|
||||
namespace TTT.CS2.Listeners;
|
||||
|
||||
public class KarmaBanner(IServiceProvider provider) : BaseListener(provider) {
|
||||
private readonly KarmaConfig config =
|
||||
provider.GetService<IStorage<KarmaConfig>>()
|
||||
private KarmaConfig config
|
||||
=> Provider.GetService<IStorage<KarmaConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new KarmaConfig();
|
||||
|
||||
@@ -22,18 +22,15 @@ namespace TTT.CS2.Listeners;
|
||||
|
||||
public class RoundTimerListener(IServiceProvider provider)
|
||||
: BaseListener(provider) {
|
||||
private readonly TTTConfig config = provider
|
||||
.GetRequiredService<IStorage<TTTConfig>>()
|
||||
.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new TTTConfig();
|
||||
private TTTConfig config
|
||||
=> Provider.GetRequiredService<IStorage<TTTConfig>>()
|
||||
.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new TTTConfig();
|
||||
|
||||
private readonly IPlayerConverter<CCSPlayerController> converter =
|
||||
provider.GetRequiredService<IPlayerConverter<CCSPlayerController>>();
|
||||
|
||||
private readonly IScheduler scheduler = provider
|
||||
.GetRequiredService<IScheduler>();
|
||||
|
||||
private IDisposable? endTimer;
|
||||
|
||||
[UsedImplicitly]
|
||||
@@ -73,7 +70,7 @@ public class RoundTimerListener(IServiceProvider provider)
|
||||
=> RoundUtil.SetTimeRemaining((int)duration.TotalSeconds));
|
||||
|
||||
endTimer?.Dispose();
|
||||
endTimer = scheduler.Schedule(duration,
|
||||
endTimer = Scheduler.Schedule(duration,
|
||||
() => {
|
||||
Server.NextWorldUpdate(()
|
||||
=> ev.Game.EndGame(EndReason.TIMEOUT(new InnocentRole(Provider))));
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using JetBrains.Annotations;
|
||||
using TTT.API;
|
||||
using TTT.CS2.API;
|
||||
|
||||
@@ -52,6 +54,14 @@ public class CS2AliveSpoofer : IAliveSpoofer, IPluginModule {
|
||||
onTick);
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
[GameEventHandler]
|
||||
public HookResult OnDisconnect(EventPlayerDisconnect ev) {
|
||||
if (ev.Userid == null) return HookResult.Continue;
|
||||
_fakeAlivePlayers.Remove(ev.Userid);
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
private void onTick() {
|
||||
_fakeAlivePlayers.RemoveWhere(p => !p.IsValid || p.Handle == IntPtr.Zero);
|
||||
foreach (var player in _fakeAlivePlayers) {
|
||||
|
||||
@@ -18,6 +18,12 @@ public static class CS2Msgs {
|
||||
rolePrefix + scannedPlayer.Name, role.Name);
|
||||
}
|
||||
|
||||
public static IMsg AFK_WARNING(TimeSpan span) {
|
||||
return MsgFactory.Create(nameof(AFK_WARNING), span.TotalSeconds);
|
||||
}
|
||||
|
||||
public static IMsg AFK_MOVED => MsgFactory.Create(nameof(AFK_MOVED));
|
||||
|
||||
public static IMsg TRAITOR_CHAT_FORMAT(IOnlinePlayer player, string msg) {
|
||||
return MsgFactory.Create(nameof(TRAITOR_CHAT_FORMAT), player.Name, msg);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ ROLE_SPECTATOR: "Spectator"
|
||||
TRAITOR_CHAT_FORMAT: "{darkred}[TRAITORS] {red}{0}: {default}{1}"
|
||||
TASER_SCANNED: "%PREFIX%You scanned {0}{grey}, they are %an% {1}{grey}!"
|
||||
DNA_PREFIX: "{darkblue}D{blue}N{lightblue}A{grey} | {grey}"
|
||||
AFK_WARNING: "%PREFIX%You will be moved to Spectator mode in {0} second%s% for being AFK."
|
||||
AFK_MOVED: "%PREFIX%You have been moved to Spectator mode for being AFK."
|
||||
|
||||
DEAD_MUTE_REMINDER: "%PREFIX%You are dead and cannot be heard."
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ public record TTTConfig {
|
||||
public record RoundConfig {
|
||||
public TimeSpan CountDownDuration { get; init; } = TimeSpan.FromSeconds(10);
|
||||
public TimeSpan TimeBetweenRounds { get; init; } = TimeSpan.FromSeconds(5);
|
||||
public TimeSpan CheckAFKTimespan { get; init; } = TimeSpan.FromSeconds(60);
|
||||
public int MinimumPlayers { get; init; } = 2;
|
||||
|
||||
public virtual TimeSpan RoundDuration(int players) {
|
||||
|
||||
@@ -48,9 +48,9 @@ public record KarmaConfig {
|
||||
/// <summary>
|
||||
/// Amount of karma a player will gain at the end of each round.
|
||||
/// </summary>
|
||||
public int KarmaPerRound { get; init; } = 3;
|
||||
public int KarmaPerRound { get; init; } = 1;
|
||||
|
||||
public int KarmaPerRoundWin { get; init; } = 5;
|
||||
public int KarmaPerRoundWin { get; init; } = 2;
|
||||
|
||||
public int INNO_ON_TRAITOR { get; init; } = 5;
|
||||
public int TRAITOR_ON_DETECTIVE { get; init; } = 1;
|
||||
|
||||
@@ -19,8 +19,11 @@ public sealed class KarmaStorage(IServiceProvider provider) : IKarmaService {
|
||||
private const bool EnableCache = true;
|
||||
private readonly IEventBus _bus = provider.GetRequiredService<IEventBus>();
|
||||
|
||||
private readonly IStorage<KarmaConfig>? _configStorage =
|
||||
provider.GetService<IStorage<KarmaConfig>>();
|
||||
private KarmaConfig _configStorage
|
||||
=> provider.GetService<IStorage<KarmaConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new KarmaConfig();
|
||||
|
||||
private readonly SemaphoreSlim _flushGate = new(1, 1);
|
||||
|
||||
@@ -38,12 +41,6 @@ public sealed class KarmaStorage(IServiceProvider provider) : IKarmaService {
|
||||
public string Version => GitVersionInformation.FullSemVer;
|
||||
|
||||
public void Start() {
|
||||
// Load configuration first
|
||||
if (_configStorage is not null)
|
||||
// Synchronously wait here since IKarmaService.Start is sync
|
||||
_config = _configStorage.Load().GetAwaiter().GetResult()
|
||||
?? new KarmaConfig();
|
||||
|
||||
// Open a dedicated connection used only by this service
|
||||
_connection = new SqliteConnection(_config.DbString);
|
||||
_connection.Open();
|
||||
|
||||
@@ -18,11 +18,11 @@ public static class StickerExtensions {
|
||||
|
||||
public class Stickers(IServiceProvider provider)
|
||||
: RoleRestrictedItem<DetectiveRole>(provider) {
|
||||
private readonly StickersConfig config = provider
|
||||
.GetService<IStorage<StickersConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new StickersConfig();
|
||||
private StickersConfig config
|
||||
=> Provider.GetService<IStorage<StickersConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new StickersConfig();
|
||||
|
||||
public override string Name => Locale[StickerMsgs.SHOP_ITEM_STICKERS];
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ShopAPI;
|
||||
using ShopAPI.Configs;
|
||||
using TTT.API.Events;
|
||||
using TTT.API.Extensions;
|
||||
using TTT.API.Game;
|
||||
using TTT.API.Player;
|
||||
using TTT.API.Storage;
|
||||
using TTT.Game.Events.Game;
|
||||
using TTT.Game.Roles;
|
||||
|
||||
namespace TTT.Shop.Items.Healthshot;
|
||||
@@ -14,9 +18,10 @@ public static class HealthshotServiceCollection {
|
||||
}
|
||||
}
|
||||
|
||||
public class HealthshotItem(IServiceProvider provider) : BaseItem(provider) {
|
||||
private readonly HealthshotConfig config =
|
||||
provider.GetService<IStorage<HealthshotConfig>>()
|
||||
public class HealthshotItem(IServiceProvider provider)
|
||||
: BaseItem(provider), IListener {
|
||||
private HealthshotConfig config
|
||||
=> Provider.GetService<IStorage<HealthshotConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new HealthshotConfig();
|
||||
@@ -28,11 +33,27 @@ public class HealthshotItem(IServiceProvider provider) : BaseItem(provider) {
|
||||
|
||||
public override ShopItemConfig Config => config;
|
||||
|
||||
private readonly Dictionary<string, int> purchaseCounts = new();
|
||||
|
||||
public override void OnPurchase(IOnlinePlayer player) {
|
||||
Inventory.GiveWeapon(player, new BaseWeapon(config.Weapon));
|
||||
|
||||
purchaseCounts.TryGetValue(player.Id, out var purchases);
|
||||
purchaseCounts[player.Id] = purchases + 1;
|
||||
}
|
||||
|
||||
public override PurchaseResult CanPurchase(IOnlinePlayer player) {
|
||||
return PurchaseResult.SUCCESS;
|
||||
if (purchaseCounts.TryGetValue(player.Id, out var purchases))
|
||||
return PurchaseResult.SUCCESS;
|
||||
return purchases < config.MaxPurchases ?
|
||||
PurchaseResult.SUCCESS :
|
||||
PurchaseResult.ALREADY_OWNED;
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
[EventHandler]
|
||||
public void OnGameState(GameStateUpdateEvent ev) {
|
||||
if (ev.NewState != State.FINISHED) return;
|
||||
purchaseCounts.Clear();
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,11 @@ public static class DeagleServiceCollection {
|
||||
|
||||
public class OneShotDeagleItem(IServiceProvider provider)
|
||||
: BaseItem(provider), IWeapon {
|
||||
private readonly OneShotDeagleConfig deagleConfigStorage = provider
|
||||
.GetService<IStorage<OneShotDeagleConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new OneShotDeagleConfig();
|
||||
private OneShotDeagleConfig deagleConfigStorage
|
||||
=> Provider.GetService<IStorage<OneShotDeagleConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new OneShotDeagleConfig();
|
||||
|
||||
public override string Name => Locale[DeagleMsgs.SHOP_ITEM_DEAGLE];
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using TTT.API.Player;
|
||||
using TTT.Game.Events.Body;
|
||||
using TTT.Game.Events.Player;
|
||||
using TTT.Game.Listeners;
|
||||
using TTT.Game.Roles;
|
||||
|
||||
namespace TTT.Shop.Listeners;
|
||||
|
||||
@@ -47,6 +48,7 @@ public class PlayerKillListener(IServiceProvider provider)
|
||||
}
|
||||
|
||||
private bool isGoodKill(IPlayer attacker, IPlayer victim) {
|
||||
return !Roles.GetRoles(attacker).Intersect(Roles.GetRoles(victim)).Any();
|
||||
return Roles.GetRoles(attacker).OfType<TraitorRole>().Any()
|
||||
!= Roles.GetRoles(victim).OfType<TraitorRole>().Any();
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,8 @@ public class RoleAssignCreditor(IServiceProvider provider)
|
||||
provider.GetService<IStorage<ShopConfig>>()?.Load().GetAwaiter().GetResult()
|
||||
?? new ShopConfig(provider);
|
||||
|
||||
private readonly KarmaConfig karmaConfig =
|
||||
provider.GetService<IStorage<KarmaConfig>>()
|
||||
private KarmaConfig karmaConfig
|
||||
=> Provider.GetService<IStorage<KarmaConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new KarmaConfig();
|
||||
@@ -50,8 +50,8 @@ public class RoleAssignCreditor(IServiceProvider provider)
|
||||
}
|
||||
|
||||
private float getKarmaScale(float percent) {
|
||||
if (percent >= 0.9) return 1.1f;
|
||||
if (percent >= 0.8f) return 1;
|
||||
if (percent >= 0.9) return 1;
|
||||
if (percent >= 0.8f) return 0.9f;
|
||||
if (percent >= 0.5) return 0.8f;
|
||||
if (percent >= 0.3) return 0.5f;
|
||||
return 0.25f;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
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;
|
||||
@@ -7,15 +9,16 @@ using TTT.API;
|
||||
using TTT.API.Game;
|
||||
using TTT.API.Player;
|
||||
using TTT.API.Storage;
|
||||
using TTT.CS2.Extensions;
|
||||
|
||||
namespace TTT.Shop;
|
||||
|
||||
public class PeriodicRewarder(IServiceProvider provider) : ITerrorModule {
|
||||
private readonly ShopConfig config = provider
|
||||
.GetService<IStorage<ShopConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new ShopConfig(provider);
|
||||
private ShopConfig config
|
||||
=> provider.GetService<IStorage<ShopConfig>>()
|
||||
?.Load()
|
||||
.GetAwaiter()
|
||||
.GetResult() ?? new ShopConfig(provider);
|
||||
|
||||
private readonly IPlayerFinder finder =
|
||||
provider.GetRequiredService<IPlayerFinder>();
|
||||
@@ -28,20 +31,88 @@ public class PeriodicRewarder(IServiceProvider provider) : ITerrorModule {
|
||||
|
||||
private readonly IShop shop = provider.GetRequiredService<IShop>();
|
||||
|
||||
private IDisposable? timer;
|
||||
private readonly IPlayerConverter<CCSPlayerController> converter =
|
||||
provider.GetRequiredService<IPlayerConverter<CCSPlayerController>>();
|
||||
|
||||
public void Dispose() { timer?.Dispose(); }
|
||||
private IDisposable? rewardTimer, updateTimer;
|
||||
|
||||
public void Dispose() {
|
||||
rewardTimer?.Dispose();
|
||||
updateTimer?.Dispose();
|
||||
}
|
||||
|
||||
private readonly Dictionary<string, List<Vector>> playerPositions = new();
|
||||
|
||||
public void Start() {
|
||||
timer = scheduler.SchedulePeriodic(config.CreditRewardInterval,
|
||||
rewardTimer = scheduler.SchedulePeriodic(config.CreditRewardInterval,
|
||||
issueRewards);
|
||||
updateTimer = scheduler.SchedulePeriodic(config.PositionUpdateInterval,
|
||||
updatePositions);
|
||||
}
|
||||
|
||||
private void issueRewards() {
|
||||
if (games.ActiveGame is not { State: State.IN_PROGRESS }) return;
|
||||
Server.NextWorldUpdate(() => {
|
||||
foreach (var player in finder.GetOnline().Where(p => p.IsAlive))
|
||||
shop.AddBalance(player, config.IntervalRewardAmount, "Alive");
|
||||
var sortedPlayers = finder.GetOnline()
|
||||
.Where(p => p.IsAlive && playerPositions.ContainsKey(p.Id))
|
||||
.Select(p => (Player: p,
|
||||
Volume: getVolumeTraveled(
|
||||
playerPositions.GetValueOrDefault(p.Id, []))))
|
||||
.OrderByDescending(t => t.Volume)
|
||||
.ToList();
|
||||
|
||||
var count = sortedPlayers.Count;
|
||||
for (var i = 0; i < count; i++) {
|
||||
var (player, _) = sortedPlayers[i];
|
||||
var position = count == 1 ? 1f : (float)(count - i - 1) / (count - 1);
|
||||
var rewardAmount = scaleRewardAmount(position, config.MinRewardAmount,
|
||||
config.MaxRewardAmount);
|
||||
shop.AddBalance(player, rewardAmount, "Exploration");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updatePositions() {
|
||||
if (games.ActiveGame is not { State: State.IN_PROGRESS }) return;
|
||||
Server.NextWorldUpdate(() => {
|
||||
foreach (var player in finder.GetOnline().Where(p => p.IsAlive)) {
|
||||
var gamePlayer = converter.GetPlayer(player);
|
||||
var position = gamePlayer?.Pawn.Value?.AbsOrigin;
|
||||
if (position is null) continue;
|
||||
position = position.Clone()!;
|
||||
|
||||
var positions = playerPositions.GetValueOrDefault(player.Id, []);
|
||||
positions.Add(position);
|
||||
|
||||
// Keep only the last N positions based on the interval
|
||||
var maxPositions = (int)(config.CreditRewardInterval.TotalSeconds
|
||||
/ config.PositionUpdateInterval.TotalSeconds);
|
||||
while (positions.Count > maxPositions) positions.RemoveAt(0);
|
||||
|
||||
playerPositions[player.Id] = positions;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private float getVolumeTraveled(List<Vector> positions) {
|
||||
if (positions.Count < 2) return 0f;
|
||||
var totalDistance = 0f;
|
||||
for (var i = 1; i < positions.Count; i++)
|
||||
totalDistance += positions[i].Distance(positions[i - 1]);
|
||||
|
||||
|
||||
return totalDistance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales a reward amount between min and max based on position (0-1).
|
||||
/// 0 = min, 1 = max.
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="min"></param>
|
||||
/// <param name="max"></param>
|
||||
/// <returns></returns>
|
||||
private int scaleRewardAmount(float position, int min, int max) {
|
||||
return (int)Math.Ceiling(min + (max - min) * position);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace ShopAPI.Configs;
|
||||
|
||||
public record CamoConfig : ShopItemConfig {
|
||||
public override int Price { get; init; } = 55;
|
||||
public override int Price { get; init; } = 75;
|
||||
public float CamoVisibility { get; init; } = 0.4f;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ShopAPI.Configs;
|
||||
|
||||
public record OneShotDeagleConfig : ShopItemConfig {
|
||||
public override int Price { get; init; } = 100;
|
||||
public override int Price { get; init; } = 125;
|
||||
public bool DoesFriendlyFire { get; init; } = true;
|
||||
public bool KillShooterOnFF { get; init; } = false;
|
||||
public string Weapon { get; init; } = "revolver";
|
||||
|
||||
@@ -34,7 +34,11 @@ public record ShopConfig(IRoleAssigner assigner) {
|
||||
public TimeSpan CreditRewardInterval { get; init; } =
|
||||
TimeSpan.FromSeconds(30);
|
||||
|
||||
public int IntervalRewardAmount { get; init; } = 8;
|
||||
public TimeSpan PositionUpdateInterval { get; init; } =
|
||||
TimeSpan.FromSeconds(5);
|
||||
|
||||
public int MaxRewardAmount { get; init; } = 15;
|
||||
public int MinRewardAmount { get; init; } = 2;
|
||||
|
||||
public virtual int CreditsForKill(IOnlinePlayer attacker,
|
||||
IOnlinePlayer victim) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace ShopAPI.Configs.Traitor;
|
||||
|
||||
public record GlovesConfig : ShopItemConfig {
|
||||
public override int Price { get; init; } = 50;
|
||||
public int MaxUses { get; init; } = 3;
|
||||
public override int Price { get; init; } = 40;
|
||||
public int MaxUses { get; init; } = 5;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ShopAPI.Configs.Traitor;
|
||||
|
||||
public record PoisonConfig {
|
||||
public TimeSpan TimeBetweenDamage { get; init; } = TimeSpan.FromSeconds(2.5);
|
||||
public TimeSpan TimeBetweenDamage { get; init; } = TimeSpan.FromSeconds(1.5);
|
||||
public int DamagePerTick { get; init; } = 5;
|
||||
public int TotalDamage { get; init; } = 60;
|
||||
|
||||
|
||||
@@ -4,5 +4,6 @@ namespace ShopAPI;
|
||||
|
||||
public record HealthshotConfig : ShopItemConfig {
|
||||
public override int Price { get; init; } = 30;
|
||||
public int MaxPurchases { get; init; } = 2;
|
||||
public string Weapon { get; init; } = "weapon_healthshot";
|
||||
}
|
||||
Reference in New Issue
Block a user