From 9674c0f479bf9641cc4db4d9b8d26af84e263dec Mon Sep 17 00:00:00 2001 From: Yoni Lerner Date: Fri, 12 Jan 2024 01:33:18 -0500 Subject: [PATCH 01/14] User settings are now configured --- RetakesAllocatorCore/Db/Db.cs | 11 +++ RetakesAllocatorCore/Db/Queries.cs | 28 +++++-- RetakesAllocatorCore/Db/UserSetting.cs | 31 ++++++- ...0240112063019_SniperPreference.Designer.cs | 42 ++++++++++ .../20240112063019_SniperPreference.cs | 29 +++++++ .../Migrations/DbModelSnapshot.cs | 8 +- RetakesAllocatorCore/OnWeaponCommandHelper.cs | 45 +++++++--- RetakesAllocatorCore/WeaponHelpers.cs | 82 +++++++++++++++---- 8 files changed, 234 insertions(+), 42 deletions(-) create mode 100644 RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.Designer.cs create mode 100644 RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.cs diff --git a/RetakesAllocatorCore/Db/Db.cs b/RetakesAllocatorCore/Db/Db.cs index 0129bfb..cf53b67 100644 --- a/RetakesAllocatorCore/Db/Db.cs +++ b/RetakesAllocatorCore/Db/Db.cs @@ -48,10 +48,21 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) } } + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .Property(e => e.WeaponPreferences) + .IsRequired(false); + base.OnModelCreating(modelBuilder); + } + protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { configurationBuilder .Properties() .HaveConversion(); + configurationBuilder + .Properties() + .HaveConversion(); } } diff --git a/RetakesAllocatorCore/Db/Queries.cs b/RetakesAllocatorCore/Db/Queries.cs index 6e2d65f..0a016a2 100644 --- a/RetakesAllocatorCore/Db/Queries.cs +++ b/RetakesAllocatorCore/Db/Queries.cs @@ -11,22 +11,36 @@ public class Queries return Db.GetInstance().UserSettings.FirstOrDefault(u => u.UserId == userId); } - public static void SetWeaponPreferenceForUser(ulong userId, CsTeam team, RoundType roundType, CsItem? item) + private static UserSetting? UpsertUserSettings(ulong userId, Action mutation) { + var instance = Db.GetInstance(); var isNew = false; - var userSettings = Db.GetInstance().UserSettings.FirstOrDefault(u => u.UserId == userId); + var userSettings = instance.UserSettings.FirstOrDefault(u => u.UserId == userId); if (userSettings is null) { userSettings = new UserSetting {UserId = userId}; - Db.GetInstance().UserSettings.Add(userSettings); + instance.UserSettings.Add(userSettings); isNew = true; } - userSettings.SetWeaponPreference(team, roundType, item); - Db.GetInstance().Entry(userSettings).State = isNew ? EntityState.Added : EntityState.Modified; + instance.Entry(userSettings).State = isNew ? EntityState.Added : EntityState.Modified; + + mutation(userSettings); + + instance.SaveChanges(); + instance.Entry(userSettings).State = EntityState.Detached; + + return userSettings; + } + + public static void SetWeaponPreferenceForUser(ulong userId, CsTeam team, RoundType roundType, CsItem? item) + { + UpsertUserSettings(userId, userSetting => { userSetting.SetWeaponPreference(team, roundType, item); }); + } - Db.GetInstance().SaveChanges(); - Db.GetInstance().Entry(userSettings).State = EntityState.Detached; + public static void SetSniperPreference(ulong userId, CsItem? sniper) + { + UpsertUserSettings(userId, userSetting => { userSetting.SniperPreference = sniper; }); } public static IDictionary GetUsersSettings(ICollection userIds) diff --git a/RetakesAllocatorCore/Db/UserSetting.cs b/RetakesAllocatorCore/Db/UserSetting.cs index 7e10358..57489e1 100644 --- a/RetakesAllocatorCore/Db/UserSetting.cs +++ b/RetakesAllocatorCore/Db/UserSetting.cs @@ -5,7 +5,6 @@ using CounterStrikeSharp.API.Modules.Utils; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using RetakesAllocatorCore.Config; namespace RetakesAllocatorCore.Db; @@ -22,7 +21,10 @@ public class UserSetting public ulong UserId { get; set; } [Column(TypeName = "TEXT"), MaxLength(10000)] - public WeaponPreferencesType WeaponPreferences { get; set; } = new(); + public WeaponPreferencesType? WeaponPreferences { get; set; } = new(); + + [Column(TypeName = "TEXT"), MaxLength(100)] + public CsItem? SniperPreference { get; set; } = null; public void SetWeaponPreference(CsTeam team, RoundType roundType, CsItem? weapon) { @@ -57,6 +59,31 @@ public void SetWeaponPreference(CsTeam team, RoundType roundType, CsItem? weapon } } +public class CsItemConverter : ValueConverter +{ + public CsItemConverter() : base( + v => CsItemSerializer(v), + s => CsItemDeserializer(s) + ) + { + } + + public static string CsItemSerializer(CsItem? item) + { + return JsonSerializer.Serialize(item); + } + + public static CsItem? CsItemDeserializer(string? str) + { + if (str is null) + { + return null; + } + + return JsonSerializer.Deserialize(str); + } +} + public class WeaponPreferencesConverter : ValueConverter { public WeaponPreferencesConverter() : base( diff --git a/RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.Designer.cs b/RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.Designer.cs new file mode 100644 index 0000000..30c93d4 --- /dev/null +++ b/RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.Designer.cs @@ -0,0 +1,42 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RetakesAllocatorCore.Db; + +#nullable disable + +namespace RetakesAllocator.Migrations +{ + [DbContext(typeof(Db))] + [Migration("20240112063019_SniperPreference")] + partial class SniperPreference + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.14"); + + modelBuilder.Entity("RetakesAllocatorCore.Db.UserSetting", b => + { + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.Property("SniperPreference") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("WeaponPreferences") + .HasMaxLength(10000) + .HasColumnType("TEXT"); + + b.HasKey("UserId"); + + b.ToTable("UserSettings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.cs b/RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.cs new file mode 100644 index 0000000..cfa5a8a --- /dev/null +++ b/RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace RetakesAllocator.Migrations +{ + /// + public partial class SniperPreference : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SniperPreference", + table: "UserSettings", + type: "TEXT", + maxLength: 100, + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SniperPreference", + table: "UserSettings"); + } + } +} diff --git a/RetakesAllocatorCore/Migrations/DbModelSnapshot.cs b/RetakesAllocatorCore/Migrations/DbModelSnapshot.cs index 0b92c77..9aaaa98 100644 --- a/RetakesAllocatorCore/Migrations/DbModelSnapshot.cs +++ b/RetakesAllocatorCore/Migrations/DbModelSnapshot.cs @@ -16,11 +16,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "7.0.14"); - modelBuilder.Entity("RetakesAllocator.db.UserSetting", b => + modelBuilder.Entity("RetakesAllocatorCore.Db.UserSetting", b => { - b.Property("UserId") + b.Property("UserId") .HasColumnType("INTEGER"); + b.Property("SniperPreference") + .HasMaxLength(100) + .HasColumnType("TEXT"); + b.Property("WeaponPreferences") .HasMaxLength(10000) .HasColumnType("TEXT"); diff --git a/RetakesAllocatorCore/OnWeaponCommandHelper.cs b/RetakesAllocatorCore/OnWeaponCommandHelper.cs index 75b1414..8122c75 100644 --- a/RetakesAllocatorCore/OnWeaponCommandHelper.cs +++ b/RetakesAllocatorCore/OnWeaponCommandHelper.cs @@ -46,38 +46,57 @@ public class OnWeaponCommandHelper var weapon = foundWeapons.First(); + if (!WeaponHelpers.IsUsableWeapon(weapon)) + { + return $"Weapon '{weapon}' is not allowed to be selected."; + } + var roundType = WeaponHelpers.GetRoundTypeForWeapon(weapon); if (roundType is null) { return $"Invalid weapon '{weapon}'"; } - if (!WeaponHelpers.IsValidWeapon(roundType.Value, team, weapon)) + if (!WeaponHelpers.IsValidWeaponForRound(roundType.Value, team, weapon)) { return $"Weapon '{weapon}' is not valid for {roundType} rounds on {team}"; } - if (!WeaponHelpers.IsUsableWeapon(weapon)) + var isSniper = WeaponHelpers.IsSniper(team, weapon); + + if (remove) { - return $"Weapon '{weapon}' is not allowed to be selected."; + if (isSniper) + { + Queries.SetSniperPreference(userId, null); + return $"You will no longer receive '{weapon}'."; + } + else + { + Queries.SetWeaponPreferenceForUser(userId, team, roundType.Value, null); + return $"Weapon '{weapon}' is no longer your {roundType} preference for {team}."; + } } - if (remove) + string message; + if (isSniper) { - Queries.SetWeaponPreferenceForUser(userId, team, roundType.Value, null); - return $"Weapon '{weapon}' is no longer your {roundType} preference for {team}."; + Queries.SetSniperPreference(userId, weapon); + message = $"You will now get a '{weapon}' when its your turn."; } else { Queries.SetWeaponPreferenceForUser(userId, team, roundType.Value, weapon); - - if (currentTeam == team) - { - // Only set the outWeapon if the user is setting the preference for their current team - outWeapon = weapon; - } + message = $"Weapon '{weapon}' is now your {roundType} preference for {team}."; + } - return $"Weapon '{weapon}' is now your {roundType} preference for {team}."; + // TODO dont set sniper immediately if its not a valid time to allocate it + if (currentTeam == team) + { + // Only set the outWeapon if the user is setting the preference for their current team + outWeapon = weapon; } + + return message; } } diff --git a/RetakesAllocatorCore/WeaponHelpers.cs b/RetakesAllocatorCore/WeaponHelpers.cs index b5f9375..0ed870e 100644 --- a/RetakesAllocatorCore/WeaponHelpers.cs +++ b/RetakesAllocatorCore/WeaponHelpers.cs @@ -23,6 +23,7 @@ public static class WeaponHelpers CsItem.Tec9, }; + private static readonly ICollection _ctPistols = new HashSet { CsItem.USP, @@ -30,6 +31,12 @@ public static class WeaponHelpers CsItem.FiveSeven, }; + private static readonly ICollection _pistolsForT = + _sharedPistols.Concat(_tPistols).ToHashSet(); + + private static readonly ICollection _pistolsForCt = + _sharedPistols.Concat(_ctPistols).ToHashSet(); + private static readonly ICollection _sharedMidRange = new HashSet { // SMG @@ -50,14 +57,24 @@ public static class WeaponHelpers CsItem.SawedOff, }; + private static readonly ICollection _ctMidRange = new HashSet { CsItem.MP9, CsItem.MAG7, }; + private static readonly ICollection _midRangeForCt = _sharedMidRange.Concat(_ctMidRange).ToHashSet(); + private static readonly ICollection _midRangeForT = _sharedMidRange.Concat(_tMidRange).ToHashSet(); + private static readonly int _maxSmgItemValue = (int) CsItem.UMP; + private static readonly ICollection _smgsForT = + _sharedMidRange.Concat(_tMidRange).Where(i => (int) i <= _maxSmgItemValue).ToHashSet(); + + private static readonly ICollection _smgsForCt = + _sharedMidRange.Concat(_ctMidRange).Where(i => (int) i <= _maxSmgItemValue).ToHashSet(); + private static readonly ICollection _tRifles = new HashSet { CsItem.AK47, @@ -89,8 +106,11 @@ public static class WeaponHelpers CsItem.AutoSniperCT, }; + private static readonly ICollection _snipersForT = _sharedSnipers.Concat(_tSnipers).ToHashSet(); + private static readonly ICollection _snipersForCt = _sharedSnipers.Concat(_ctSnipers).ToHashSet(); + private static readonly ICollection _allSnipers = - _sharedSnipers.Concat(_ctSnipers).Concat(_tSnipers).ToHashSet(); + _snipersForT.Concat(_snipersForCt).ToHashSet(); private static readonly ICollection _heavys = new HashSet { @@ -98,10 +118,25 @@ public static class WeaponHelpers CsItem.Negev, }; + private static readonly ICollection _fullBuyForT = + _tRifles.Concat(_snipersForT).Concat(_heavys).ToHashSet(); + + private static readonly ICollection _fullBuyForCt = + _ctRifles.Concat(_snipersForCt).Concat(_heavys).ToHashSet(); + private static readonly ICollection _allWeapons = Enum.GetValues() .Where(item => (int) item >= 200 && (int) item < 500) .ToHashSet(); + private static readonly ICollection _fullBuyRound = + _allSnipers.Concat(_heavys).Concat(_tRifles).Concat(_ctRifles).ToHashSet(); + + private static readonly ICollection _halfBuyRound = + _midRangeForT.Concat(_midRangeForCt).ToHashSet(); + + private static readonly ICollection _pistolRound = + _pistolsForT.Concat(_pistolsForCt).ToHashSet(); + private static readonly Dictionary< CsTeam, Dictionary> @@ -110,17 +145,17 @@ private static readonly Dictionary< { CsTeam.Terrorist, new() { - {RoundType.Pistol, new HashSet(_sharedPistols.Concat(_tPistols))}, - {RoundType.HalfBuy, new HashSet(_sharedMidRange.Concat(_tMidRange))}, - {RoundType.FullBuy, _tRifles.Concat(_sharedSnipers).Concat(_tSnipers).Concat(_heavys).ToHashSet()}, + {RoundType.Pistol, _pistolsForT}, + {RoundType.HalfBuy, _midRangeForT}, + {RoundType.FullBuy, _fullBuyForT}, } }, { CsTeam.CounterTerrorist, new() { - {RoundType.Pistol, new HashSet(_sharedPistols.Concat(_ctPistols))}, - {RoundType.HalfBuy, new HashSet(_sharedMidRange.Concat(_ctMidRange))}, - {RoundType.FullBuy, _ctRifles.Concat(_sharedSnipers).Concat(_ctSnipers).Concat(_heavys).ToHashSet()}, + {RoundType.Pistol, _pistolsForCt}, + {RoundType.HalfBuy, _midRangeForCt}, + {RoundType.FullBuy, _fullBuyForCt}, } } }; @@ -169,7 +204,7 @@ public static ICollection GetPossibleWeaponsForRoundType(RoundType round return _validWeaponsByTeamAndRoundType[team][roundType].Where(IsUsableWeapon).ToList(); } - public static bool IsValidWeapon(RoundType roundType, CsTeam team, CsItem weapon) + public static bool IsValidWeaponForRound(RoundType roundType, CsTeam team, CsItem weapon) { if (team != CsTeam.Terrorist && team != CsTeam.CounterTerrorist) { @@ -184,6 +219,16 @@ public static bool IsValidWeapon(RoundType roundType, CsTeam team, CsItem weapon return _validWeaponsByTeamAndRoundType[team][roundType].Contains(weapon); } + public static bool IsSniper(CsTeam team, CsItem weapon) + { + return team switch + { + CsTeam.Terrorist => _snipersForT.Contains(weapon), + CsTeam.CounterTerrorist => _snipersForCt.Contains(weapon), + _ => false, + }; + } + public static bool IsUsableWeapon(CsItem weapon) { return Configs.GetConfigData().UsableWeapons.Contains(weapon); @@ -191,17 +236,17 @@ public static bool IsUsableWeapon(CsItem weapon) public static RoundType? GetRoundTypeForWeapon(CsItem weapon) { - if (_allSnipers.Concat(_heavys).Concat(_ctRifles).Concat(_tRifles).Contains(weapon)) + if (_fullBuyRound.Contains(weapon)) { return RoundType.FullBuy; } - if (_sharedMidRange.Concat(_ctMidRange).Concat(_tMidRange).Contains(weapon)) + if (_halfBuyRound.Contains(weapon)) { return RoundType.HalfBuy; } - if (_sharedPistols.Concat(_ctPistols).Concat(_tPistols).Contains(weapon)) + if (_pistolRound.Contains(weapon)) { return RoundType.Pistol; } @@ -260,14 +305,15 @@ private static CsItem GetDefaultWeaponForRoundType(RoundType roundType, CsTeam t private static CsItem GetRandomWeaponForRoundType(RoundType roundType, CsTeam team) { + if (team != CsTeam.Terrorist && team != CsTeam.CounterTerrorist) + { + return CsItem.Deagle; + } + var collectionToCheck = roundType switch { - RoundType.Pistol => _sharedPistols.Concat(team == CsTeam.Terrorist ? _tPistols : _ctPistols).ToHashSet(), - RoundType.HalfBuy => - _sharedMidRange - .Concat(team == CsTeam.Terrorist ? _tMidRange : _ctMidRange) - .Where(item => (int) item <= _maxSmgItemValue) - .ToHashSet(), + RoundType.Pistol => team == CsTeam.Terrorist ? _pistolsForT : _pistolsForCt, + RoundType.HalfBuy => team == CsTeam.Terrorist ? _smgsForT : _smgsForCt, RoundType.FullBuy => team == CsTeam.Terrorist ? _tRifles : _ctRifles, _ => _sharedPistols, }; @@ -298,7 +344,7 @@ private static CsItem GetRandomWeaponForRoundType(RoundType roundType, CsTeam te return weapon; } - + public static bool IsWeaponAllocationAllowed(bool isFreezePeriod) { return Configs.GetConfigData().AllowAllocationAfterFreezeTime || isFreezePeriod; From 0bc18719d60d93f68c71ebba81065b9883874415 Mon Sep 17 00:00:00 2001 From: Yoni Lerner Date: Fri, 12 Jan 2024 01:45:07 -0500 Subject: [PATCH 02/14] handle buy menu --- RetakesAllocator/RetakesAllocator.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/RetakesAllocator/RetakesAllocator.cs b/RetakesAllocator/RetakesAllocator.cs index 06abb74..1a4ca25 100644 --- a/RetakesAllocator/RetakesAllocator.cs +++ b/RetakesAllocator/RetakesAllocator.cs @@ -227,12 +227,16 @@ public HookResult OnPostItemPurchase(EventItemPurchase @event, GameEventInfo inf var team = (CsTeam) player.TeamNum; var playerId = Helpers.GetSteamId(player); var weaponRoundType = WeaponHelpers.GetRoundTypeForWeapon(item); + var isSniper = WeaponHelpers.IsSniper(team, item); + // Log.Write($"item {item} team {team} player {playerId}"); // Log.Write($"curRound {_currentRoundType} weapon Round {weaponRoundType}"); if ( Helpers.IsWeaponAllocationAllowed() && + // Snipers are treated like un-buy-able weapons, but at the end we'll set the user preference + !isSniper && weaponRoundType is not null && (weaponRoundType == _currentRoundType || weaponRoundType == RoundType.Pistol) ) @@ -318,6 +322,22 @@ p.AbsOrigin is null } } + if (isSniper) + { + var itemName = Enum.GetName(item); + if (itemName is not null) + { + var message = OnWeaponCommandHelper.Handle( + new List {itemName}, + Helpers.GetSteamId(player), + team, + false, + out _ + ); + player.PrintToChat(message); + } + } + return HookResult.Continue; } From c9bd0789713bea5f8bc303313f5e471beaed6a2b Mon Sep 17 00:00:00 2001 From: Yoni Lerner Date: Fri, 12 Jan 2024 01:59:37 -0500 Subject: [PATCH 03/14] naive impl --- .../OnRoundPostStartHelper.cs | 22 +++++++++++++++++-- RetakesAllocatorCore/WeaponHelpers.cs | 11 ++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/RetakesAllocatorCore/OnRoundPostStartHelper.cs b/RetakesAllocatorCore/OnRoundPostStartHelper.cs index 082326e..6ca1373 100644 --- a/RetakesAllocatorCore/OnRoundPostStartHelper.cs +++ b/RetakesAllocatorCore/OnRoundPostStartHelper.cs @@ -1,4 +1,5 @@ -using CounterStrikeSharp.API.Modules.Entities.Constants; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Entities.Constants; using CounterStrikeSharp.API.Modules.Utils; using RetakesAllocatorCore.Config; using RetakesAllocatorCore.Db; @@ -49,6 +50,14 @@ out RoundType currentRoundType var defusingPlayer = Utils.Choice(ctPlayers); + ICollection tSnipers = new HashSet(); + ICollection ctSnipers = new HashSet(); + if (roundType == RoundType.FullBuy) + { + tSnipers = WeaponHelpers.SelectSnipers(tPlayers); + ctSnipers = WeaponHelpers.SelectSnipers(ctPlayers); + } + foreach (var player in allPlayers) { var team = getTeam(player); @@ -60,8 +69,17 @@ out RoundType currentRoundType team == CsTeam.Terrorist ? CsItem.DefaultKnifeT : CsItem.DefaultKnifeCT, }; + var playerWeaponsRoundType = roundType; + switch (team) + { + case CsTeam.Terrorist when tSnipers.Contains(player): + case CsTeam.CounterTerrorist when ctSnipers.Contains(player): + playerWeaponsRoundType = RoundType.Pistol; + break; + } + items.AddRange( - WeaponHelpers.GetWeaponsForRoundType(roundType, team, userSettings) + WeaponHelpers.GetWeaponsForRoundType(playerWeaponsRoundType, team, userSettings) ); if (team == CsTeam.CounterTerrorist) diff --git a/RetakesAllocatorCore/WeaponHelpers.cs b/RetakesAllocatorCore/WeaponHelpers.cs index 0ed870e..3439a48 100644 --- a/RetakesAllocatorCore/WeaponHelpers.cs +++ b/RetakesAllocatorCore/WeaponHelpers.cs @@ -229,6 +229,17 @@ public static bool IsSniper(CsTeam team, CsItem weapon) }; } + public static ICollection SelectSnipers(ICollection players) + { + var player = Utils.Choice(players); + if (player is null) + { + return new HashSet(); + } + + return new HashSet {player}; + } + public static bool IsUsableWeapon(CsItem weapon) { return Configs.GetConfigData().UsableWeapons.Contains(weapon); From b44d6948153242facd71713aecd268be0d316662 Mon Sep 17 00:00:00 2001 From: Yoni Lerner Date: Fri, 12 Jan 2024 22:07:27 -0500 Subject: [PATCH 04/14] fix --- RetakesAllocatorCore/Db/UserSetting.cs | 2 +- RetakesAllocatorCore/Migrations/DbModelSnapshot.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RetakesAllocatorCore/Db/UserSetting.cs b/RetakesAllocatorCore/Db/UserSetting.cs index 57489e1..fcea567 100644 --- a/RetakesAllocatorCore/Db/UserSetting.cs +++ b/RetakesAllocatorCore/Db/UserSetting.cs @@ -21,7 +21,7 @@ public class UserSetting public ulong UserId { get; set; } [Column(TypeName = "TEXT"), MaxLength(10000)] - public WeaponPreferencesType? WeaponPreferences { get; set; } = new(); + public WeaponPreferencesType WeaponPreferences { get; set; } = new(); [Column(TypeName = "TEXT"), MaxLength(100)] public CsItem? SniperPreference { get; set; } = null; diff --git a/RetakesAllocatorCore/Migrations/DbModelSnapshot.cs b/RetakesAllocatorCore/Migrations/DbModelSnapshot.cs index 9aaaa98..d8a1953 100644 --- a/RetakesAllocatorCore/Migrations/DbModelSnapshot.cs +++ b/RetakesAllocatorCore/Migrations/DbModelSnapshot.cs @@ -31,7 +31,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("UserId"); - b.ToTable("UserSettings"); + b.ToTable("UserSettings", (string)null); }); #pragma warning restore 612, 618 } From f2ca537801de9c4f3a7ee600ef67102043c86edc Mon Sep 17 00:00:00 2001 From: Yoni Lerner Date: Sat, 13 Jan 2024 00:14:32 -0500 Subject: [PATCH 05/14] Huge progress in refactoring to WeaponAllocationType --- RetakesAllocator/Menus/WeaponsMenu.cs | 8 +- RetakesAllocator/RetakesAllocator.cs | 30 ++- RetakesAllocatorCore/Config/Configs.cs | 5 + RetakesAllocatorCore/Db/Db.cs | 17 +- RetakesAllocatorCore/Db/Queries.cs | 14 +- RetakesAllocatorCore/Db/UserSetting.cs | 31 ++- ...0240112063019_SniperPreference.Designer.cs | 42 ---- .../20240112063019_SniperPreference.cs | 29 --- .../Migrations/DbModelSnapshot.cs | 74 +++--- .../OnRoundPostStartHelper.cs | 1 + RetakesAllocatorCore/OnWeaponCommandHelper.cs | 12 +- RetakesAllocatorCore/WeaponHelpers.cs | 196 +++++++++++---- RetakesAllocatorTest/DbTests.cs | 21 +- RetakesAllocatorTest/WeaponSelectionTests.cs | 223 +++++++++--------- 14 files changed, 378 insertions(+), 325 deletions(-) delete mode 100644 RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.Designer.cs delete mode 100644 RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.cs diff --git a/RetakesAllocator/Menus/WeaponsMenu.cs b/RetakesAllocator/Menus/WeaponsMenu.cs index 6dbfe31..4e23b8c 100644 --- a/RetakesAllocator/Menus/WeaponsMenu.cs +++ b/RetakesAllocator/Menus/WeaponsMenu.cs @@ -62,7 +62,7 @@ private void OpenTPrimaryMenu(CCSPlayerController player) { var menu = new ChatMenu($"{MessagePrefix} Select a T Primary Weapon"); - foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForRoundType(RoundType.FullBuy, CsTeam.Terrorist)) + foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForAllocationType(RoundType.FullBuy, CsTeam.Terrorist)) { menu.AddMenuOption(weapon.ToString(), OnTPrimarySelect); } @@ -92,7 +92,7 @@ private void OpenTSecondaryMenu(CCSPlayerController player) { var menu = new ChatMenu($"{MessagePrefix} Select a T Secondary Weapon"); - foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForRoundType(RoundType.Pistol, CsTeam.Terrorist)) + foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForAllocationType(RoundType.Pistol, CsTeam.Terrorist)) { menu.AddMenuOption(weapon.ToString(), OnTSecondarySelect); } @@ -123,7 +123,7 @@ private void OpenCtPrimaryMenu(CCSPlayerController player) { var menu = new ChatMenu($"{MessagePrefix} Select a CT Primary Weapon"); - foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForRoundType(RoundType.FullBuy, CsTeam.CounterTerrorist)) + foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForAllocationType(RoundType.FullBuy, CsTeam.CounterTerrorist)) { menu.AddMenuOption(weapon.ToString(), OnCTPrimarySelect); } @@ -153,7 +153,7 @@ private void OpenCtSecondaryMenu(CCSPlayerController player) { var menu = new ChatMenu($"{MessagePrefix} Select a CT Secondary Weapon"); - foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForRoundType(RoundType.Pistol, CsTeam.Terrorist)) + foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForAllocationType(RoundType.Pistol, CsTeam.Terrorist)) { menu.AddMenuOption(weapon.ToString(), OnCTSecondarySelect); } diff --git a/RetakesAllocator/RetakesAllocator.cs b/RetakesAllocator/RetakesAllocator.cs index 1a4ca25..c25b4d1 100644 --- a/RetakesAllocator/RetakesAllocator.cs +++ b/RetakesAllocator/RetakesAllocator.cs @@ -36,7 +36,7 @@ public override void Load(bool hotReload) RegisterListener(mapName => { ResetState(); }); AddCommandListener("say", OnPlayerChat, HookMode.Post); - + if (Configs.GetConfigData().MigrateOnStartup) { Queries.Migrate(); @@ -71,6 +71,7 @@ public override void Unload(bool hotReload) #endregion #region Commands + private HookResult OnPlayerChat(CCSPlayerController? player, CommandInfo info) { if (!Helpers.PlayerIsValid(player)) @@ -89,14 +90,14 @@ private HookResult OnPlayerChat(CCSPlayerController? player, CommandInfo info) return HookResult.Continue; } - + [ConsoleCommand("css_guns")] [CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)] public void OnGunsCommand(CCSPlayerController? player, CommandInfo commandInfo) { HandleGunsCommand(player, commandInfo); } - + private void HandleGunsCommand(CCSPlayerController? player, CommandInfo commandInfo) { if (!Helpers.PlayerIsValid(player)) @@ -229,7 +230,6 @@ public HookResult OnPostItemPurchase(EventItemPurchase @event, GameEventInfo inf var weaponRoundType = WeaponHelpers.GetRoundTypeForWeapon(item); var isSniper = WeaponHelpers.IsSniper(team, item); - // Log.Write($"item {item} team {team} player {playerId}"); // Log.Write($"curRound {_currentRoundType} weapon Round {weaponRoundType}"); @@ -267,16 +267,22 @@ weaponRoundType is not null && var slotToSelect = _currentRoundType == RoundType.Pistol ? "slot2" : "slot1"; if (removedAnyWeapons && _currentRoundType is not null && WeaponHelpers.IsWeapon(item)) { - var replacementItem = WeaponHelpers.GetWeaponForRoundType(_currentRoundType.Value, team, - Queries.GetUserSettings(playerId)); - // Log.Write($"Replacement item: {replacementItem}"); - if (replacementItem is not null) + var replacementAllocationType = + WeaponHelpers.GetWeaponAllocationTypeForWeapon(item, _currentRoundType.Value); + if (replacementAllocationType is not null) { - replacedWeapon = true; - AllocateItemsForPlayer(player, new List + var replacementItem = WeaponHelpers.GetWeaponForAllocationType(replacementAllocationType.Value, + team, + Queries.GetUserSettings(playerId)); + // Log.Write($"Replacement item: {replacementItem}"); + if (replacementItem is not null) { - replacementItem.Value - }, slotToSelect); + replacedWeapon = true; + AllocateItemsForPlayer(player, new List + { + replacementItem.Value + }, slotToSelect); + } } } diff --git a/RetakesAllocatorCore/Config/Configs.cs b/RetakesAllocatorCore/Config/Configs.cs index 205ce1e..04f5367 100644 --- a/RetakesAllocatorCore/Config/Configs.cs +++ b/RetakesAllocatorCore/Config/Configs.cs @@ -23,6 +23,11 @@ public static class Configs ReadCommentHandling = JsonCommentHandling.Skip, }; + public static bool IsLoaded() + { + return _configData is not null; + } + public static ConfigData GetConfigData() { if (_configData is null) diff --git a/RetakesAllocatorCore/Db/Db.cs b/RetakesAllocatorCore/Db/Db.cs index cf53b67..6060692 100644 --- a/RetakesAllocatorCore/Db/Db.cs +++ b/RetakesAllocatorCore/Db/Db.cs @@ -5,11 +5,6 @@ namespace RetakesAllocatorCore.Db; -using WeaponPreferencesType = Dictionary< - CsTeam, - Dictionary ->; - public class Db : DbContext { public DbSet UserSettings { get; set; } @@ -32,9 +27,11 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) optionsBuilder .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); - var databaseConnectionString = Configs.GetConfigData().DatabaseConnectionString; - - switch (Configs.GetConfigData().DatabaseProvider) + // TODO This whole thing needs to be fixed per + // https://jasonwatmore.com/post/2020/01/03/aspnet-core-ef-core-migrations-for-multiple-databases-sqlite-and-sql-server + var configData = Configs.IsLoaded() ? Configs.GetConfigData() : new ConfigData(); + var databaseConnectionString = configData.DatabaseConnectionString; + switch (configData.DatabaseProvider) { case DatabaseProvider.Sqlite: optionsBuilder.UseSqlite(databaseConnectionString); @@ -58,9 +55,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { - configurationBuilder - .Properties() - .HaveConversion(); + UserSetting.Configure(configurationBuilder); configurationBuilder .Properties() .HaveConversion(); diff --git a/RetakesAllocatorCore/Db/Queries.cs b/RetakesAllocatorCore/Db/Queries.cs index 0a016a2..956929e 100644 --- a/RetakesAllocatorCore/Db/Queries.cs +++ b/RetakesAllocatorCore/Db/Queries.cs @@ -33,14 +33,22 @@ public class Queries return userSettings; } - public static void SetWeaponPreferenceForUser(ulong userId, CsTeam team, RoundType roundType, CsItem? item) + public static void SetWeaponPreferenceForUser(ulong userId, CsTeam team, WeaponAllocationType weaponAllocationType, + CsItem? item) { - UpsertUserSettings(userId, userSetting => { userSetting.SetWeaponPreference(team, roundType, item); }); + UpsertUserSettings(userId, + userSetting => { userSetting.SetWeaponPreference(team, weaponAllocationType, item); }); } public static void SetSniperPreference(ulong userId, CsItem? sniper) { - UpsertUserSettings(userId, userSetting => { userSetting.SniperPreference = sniper; }); + UpsertUserSettings(userId, userSetting => + { + userSetting.SetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.Sniper, + WeaponHelpers.CoerceSniperTeam(sniper, CsTeam.Terrorist)); + userSetting.SetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.Sniper, + WeaponHelpers.CoerceSniperTeam(sniper, CsTeam.CounterTerrorist)); + }); } public static IDictionary GetUsersSettings(ICollection userIds) diff --git a/RetakesAllocatorCore/Db/UserSetting.cs b/RetakesAllocatorCore/Db/UserSetting.cs index fcea567..732ed01 100644 --- a/RetakesAllocatorCore/Db/UserSetting.cs +++ b/RetakesAllocatorCore/Db/UserSetting.cs @@ -3,6 +3,7 @@ using System.Text.Json; using CounterStrikeSharp.API.Modules.Entities.Constants; using CounterStrikeSharp.API.Modules.Utils; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; @@ -10,7 +11,7 @@ namespace RetakesAllocatorCore.Db; using WeaponPreferencesType = Dictionary< CsTeam, - Dictionary + Dictionary >; public class UserSetting @@ -23,33 +24,37 @@ public class UserSetting [Column(TypeName = "TEXT"), MaxLength(10000)] public WeaponPreferencesType WeaponPreferences { get; set; } = new(); - [Column(TypeName = "TEXT"), MaxLength(100)] - public CsItem? SniperPreference { get; set; } = null; + public static void Configure(ModelConfigurationBuilder configurationBuilder) + { + configurationBuilder + .Properties() + .HaveConversion(); + } - public void SetWeaponPreference(CsTeam team, RoundType roundType, CsItem? weapon) + public void SetWeaponPreference(CsTeam team, WeaponAllocationType weaponAllocationType, CsItem? weapon) { - // Log.Write($"Setting preference for {UserId} {team} {roundType} {weapon}"); - if (!WeaponPreferences.TryGetValue(team, out var roundPreferences)) + // Log.Write($"Setting preference for {UserId} {team} {weaponAllocationType} {weapon}"); + if (!WeaponPreferences.TryGetValue(team, out var allocationPreference)) { - roundPreferences = new(); - WeaponPreferences.Add(team, roundPreferences); + allocationPreference = new(); + WeaponPreferences.Add(team, allocationPreference); } if (weapon is not null) { - roundPreferences[roundType] = (CsItem) weapon; + allocationPreference[weaponAllocationType] = (CsItem) weapon; } else { - roundPreferences.Remove(roundType); + allocationPreference.Remove(weaponAllocationType); } } - public CsItem? GetWeaponPreference(CsTeam team, RoundType roundType) + public CsItem? GetWeaponPreference(CsTeam team, WeaponAllocationType weaponAllocationType) { - if (WeaponPreferences.TryGetValue(team, out var roundPreferences)) + if (WeaponPreferences.TryGetValue(team, out var allocationPreference)) { - if (roundPreferences.TryGetValue(roundType, out var weapon)) + if (allocationPreference.TryGetValue(weaponAllocationType, out var weapon)) { return weapon; } diff --git a/RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.Designer.cs b/RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.Designer.cs deleted file mode 100644 index 30c93d4..0000000 --- a/RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.Designer.cs +++ /dev/null @@ -1,42 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using RetakesAllocatorCore.Db; - -#nullable disable - -namespace RetakesAllocator.Migrations -{ - [DbContext(typeof(Db))] - [Migration("20240112063019_SniperPreference")] - partial class SniperPreference - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "7.0.14"); - - modelBuilder.Entity("RetakesAllocatorCore.Db.UserSetting", b => - { - b.Property("UserId") - .HasColumnType("INTEGER"); - - b.Property("SniperPreference") - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("WeaponPreferences") - .HasMaxLength(10000) - .HasColumnType("TEXT"); - - b.HasKey("UserId"); - - b.ToTable("UserSettings"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.cs b/RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.cs deleted file mode 100644 index cfa5a8a..0000000 --- a/RetakesAllocatorCore/Migrations/20240112063019_SniperPreference.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace RetakesAllocator.Migrations -{ - /// - public partial class SniperPreference : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "SniperPreference", - table: "UserSettings", - type: "TEXT", - maxLength: 100, - nullable: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "SniperPreference", - table: "UserSettings"); - } - } -} diff --git a/RetakesAllocatorCore/Migrations/DbModelSnapshot.cs b/RetakesAllocatorCore/Migrations/DbModelSnapshot.cs index d8a1953..bbb9ef6 100644 --- a/RetakesAllocatorCore/Migrations/DbModelSnapshot.cs +++ b/RetakesAllocatorCore/Migrations/DbModelSnapshot.cs @@ -1,39 +1,35 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using RetakesAllocatorCore.Db; - -#nullable disable - -namespace RetakesAllocator.Migrations -{ - [DbContext(typeof(Db))] - partial class DbModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "7.0.14"); - - modelBuilder.Entity("RetakesAllocatorCore.Db.UserSetting", b => - { - b.Property("UserId") - .HasColumnType("INTEGER"); - - b.Property("SniperPreference") - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("WeaponPreferences") - .HasMaxLength(10000) - .HasColumnType("TEXT"); - - b.HasKey("UserId"); - - b.ToTable("UserSettings", (string)null); - }); -#pragma warning restore 612, 618 - } - } -} +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RetakesAllocatorCore.Db; + +#nullable disable + +namespace RetakesAllocator.Migrations +{ + [DbContext(typeof(Db))] + partial class DbModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.14"); + + modelBuilder.Entity("RetakesAllocator.db.UserSetting", b => + { + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.Property("WeaponPreferences") + .HasMaxLength(10000) + .HasColumnType("TEXT"); + + b.HasKey("UserId"); + + b.ToTable("UserSettings", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RetakesAllocatorCore/OnRoundPostStartHelper.cs b/RetakesAllocatorCore/OnRoundPostStartHelper.cs index 6ca1373..675ea47 100644 --- a/RetakesAllocatorCore/OnRoundPostStartHelper.cs +++ b/RetakesAllocatorCore/OnRoundPostStartHelper.cs @@ -54,6 +54,7 @@ out RoundType currentRoundType ICollection ctSnipers = new HashSet(); if (roundType == RoundType.FullBuy) { + // Filter by players that actually want a sniper tSnipers = WeaponHelpers.SelectSnipers(tPlayers); ctSnipers = WeaponHelpers.SelectSnipers(ctPlayers); } diff --git a/RetakesAllocatorCore/OnWeaponCommandHelper.cs b/RetakesAllocatorCore/OnWeaponCommandHelper.cs index 8122c75..d1d8b63 100644 --- a/RetakesAllocatorCore/OnWeaponCommandHelper.cs +++ b/RetakesAllocatorCore/OnWeaponCommandHelper.cs @@ -57,7 +57,11 @@ public class OnWeaponCommandHelper return $"Invalid weapon '{weapon}'"; } - if (!WeaponHelpers.IsValidWeaponForRound(roundType.Value, team, weapon)) + var allocationType = WeaponHelpers.WeaponAllocationTypeForWeaponAndRound( + roundType.Value, team, weapon + ); + + if (allocationType is null) { return $"Weapon '{weapon}' is not valid for {roundType} rounds on {team}"; } @@ -73,7 +77,8 @@ public class OnWeaponCommandHelper } else { - Queries.SetWeaponPreferenceForUser(userId, team, roundType.Value, null); + // TODO double check this + Queries.SetWeaponPreferenceForUser(userId, team, allocationType.Value, null); return $"Weapon '{weapon}' is no longer your {roundType} preference for {team}."; } } @@ -86,7 +91,8 @@ public class OnWeaponCommandHelper } else { - Queries.SetWeaponPreferenceForUser(userId, team, roundType.Value, weapon); + // TODO double check this + Queries.SetWeaponPreferenceForUser(userId, team, allocationType.Value, weapon); message = $"Weapon '{weapon}' is now your {roundType} preference for {team}."; } diff --git a/RetakesAllocatorCore/WeaponHelpers.cs b/RetakesAllocatorCore/WeaponHelpers.cs index 3439a48..7a1f636 100644 --- a/RetakesAllocatorCore/WeaponHelpers.cs +++ b/RetakesAllocatorCore/WeaponHelpers.cs @@ -1,4 +1,3 @@ -using System.Collections; using CounterStrikeSharp.API.Modules.Entities.Constants; using CounterStrikeSharp.API.Modules.Utils; using RetakesAllocatorCore.Config; @@ -6,6 +5,15 @@ namespace RetakesAllocatorCore; +public enum WeaponAllocationType +{ + FullBuyPrimary, + HalfBuyPrimary, + Secondary, + PistolRound, + Sniper, +} + public static class WeaponHelpers { private static readonly ICollection _sharedPistols = new HashSet @@ -128,57 +136,65 @@ public static class WeaponHelpers .Where(item => (int) item >= 200 && (int) item < 500) .ToHashSet(); - private static readonly ICollection _fullBuyRound = + private static readonly ICollection _allFullBuy = _allSnipers.Concat(_heavys).Concat(_tRifles).Concat(_ctRifles).ToHashSet(); - private static readonly ICollection _halfBuyRound = + private static readonly ICollection _allHalfBuy = _midRangeForT.Concat(_midRangeForCt).ToHashSet(); - private static readonly ICollection _pistolRound = + private static readonly ICollection _allPistols = _pistolsForT.Concat(_pistolsForCt).ToHashSet(); private static readonly Dictionary< CsTeam, - Dictionary> - > _validWeaponsByTeamAndRoundType = new() + Dictionary> + > _validWeaponsByTeamAndAllocationType = new() { { CsTeam.Terrorist, new() { - {RoundType.Pistol, _pistolsForT}, - {RoundType.HalfBuy, _midRangeForT}, - {RoundType.FullBuy, _fullBuyForT}, + {WeaponAllocationType.PistolRound, _pistolsForT}, + {WeaponAllocationType.Secondary, _pistolsForT}, + {WeaponAllocationType.HalfBuyPrimary, _midRangeForT}, + {WeaponAllocationType.FullBuyPrimary, _fullBuyForT}, + {WeaponAllocationType.Sniper, _snipersForT}, } }, { CsTeam.CounterTerrorist, new() { - {RoundType.Pistol, _pistolsForCt}, - {RoundType.HalfBuy, _midRangeForCt}, - {RoundType.FullBuy, _fullBuyForCt}, + {WeaponAllocationType.PistolRound, _pistolsForCt}, + {WeaponAllocationType.Secondary, _pistolsForCt}, + {WeaponAllocationType.HalfBuyPrimary, _midRangeForCt}, + {WeaponAllocationType.FullBuyPrimary, _fullBuyForCt}, + {WeaponAllocationType.Sniper, _snipersForCt}, } } }; private static readonly Dictionary< CsTeam, - Dictionary - > _defaultWeaponsByTeamAndRoundType = new() + Dictionary + > _defaultWeaponsByTeamAndAllocationType = new() { { CsTeam.Terrorist, new() { - {RoundType.Pistol, CsItem.Glock}, - {RoundType.HalfBuy, CsItem.Mac10}, - {RoundType.FullBuy, CsItem.AK47}, + {WeaponAllocationType.FullBuyPrimary, CsItem.AK47}, + {WeaponAllocationType.HalfBuyPrimary, CsItem.Mac10}, + {WeaponAllocationType.Secondary, CsItem.Deagle}, + {WeaponAllocationType.PistolRound, CsItem.Glock}, + {WeaponAllocationType.Sniper, CsItem.AWP}, } }, { CsTeam.CounterTerrorist, new() { - {RoundType.Pistol, CsItem.USPS}, - {RoundType.HalfBuy, CsItem.MP9}, - {RoundType.FullBuy, CsItem.M4A4}, + {WeaponAllocationType.FullBuyPrimary, CsItem.M4A1S}, + {WeaponAllocationType.HalfBuyPrimary, CsItem.MP9}, + {WeaponAllocationType.Secondary, CsItem.Deagle}, + {WeaponAllocationType.PistolRound, CsItem.USPS}, + {WeaponAllocationType.Sniper, CsItem.AWP}, } } }; @@ -199,24 +215,46 @@ public static bool IsWeapon(CsItem item) return _allWeapons.Contains(item); } - public static ICollection GetPossibleWeaponsForRoundType(RoundType roundType, CsTeam team) + public static ICollection GetPossibleWeaponsForAllocationType(WeaponAllocationType allocationType, CsTeam team) + { + return _validWeaponsByTeamAndAllocationType[team][allocationType].Where(IsUsableWeapon).ToList(); + } + + // TODO Im not convinced this is reasonable + public static bool IsAllocationTypeValidForRound(WeaponAllocationType allocationType, RoundType roundType) { - return _validWeaponsByTeamAndRoundType[team][roundType].Where(IsUsableWeapon).ToList(); + return roundType switch + { + RoundType.Pistol => allocationType is WeaponAllocationType.PistolRound or WeaponAllocationType.Secondary, + RoundType.HalfBuy => allocationType == WeaponAllocationType.HalfBuyPrimary, + RoundType.FullBuy => allocationType is WeaponAllocationType.FullBuyPrimary or WeaponAllocationType.Sniper, + _ => false + }; } - public static bool IsValidWeaponForRound(RoundType roundType, CsTeam team, CsItem weapon) + public static WeaponAllocationType? WeaponAllocationTypeForWeaponAndRound(RoundType roundType, CsTeam team, + CsItem weapon) { if (team != CsTeam.Terrorist && team != CsTeam.CounterTerrorist) { - return false; + return null; + } + + if (_allSnipers.Contains(weapon)) + { + return WeaponAllocationType.Sniper; } - if (_allSnipers.Contains(weapon) || _heavys.Contains(weapon)) + var allocationsByTeam = _validWeaponsByTeamAndAllocationType[team]; + foreach (var (allocationType, items) in allocationsByTeam) { - return true; + if (items.Contains(weapon) && IsAllocationTypeValidForRound(allocationType, roundType)) + { + return allocationType; + } } - return _validWeaponsByTeamAndRoundType[team][roundType].Contains(weapon); + return null; } public static bool IsSniper(CsTeam team, CsItem weapon) @@ -245,19 +283,40 @@ public static bool IsUsableWeapon(CsItem weapon) return Configs.GetConfigData().UsableWeapons.Contains(weapon); } + public static CsItem? CoerceSniperTeam(CsItem? sniper, CsTeam team) + { + if (sniper == null || !_allSnipers.Contains(sniper.Value)) + { + return null; + } + + if (team != CsTeam.Terrorist && team != CsTeam.CounterTerrorist) + { + return null; + } + + if (sniper is CsItem.AWP or CsItem.Scout) + { + return sniper; + } + + return team == CsTeam.Terrorist ? CsItem.AutoSniperT : CsItem.AutoSniperCT; + } + + // TODO Change all usages of this public static RoundType? GetRoundTypeForWeapon(CsItem weapon) { - if (_fullBuyRound.Contains(weapon)) + if (_allFullBuy.Contains(weapon)) { return RoundType.FullBuy; } - if (_halfBuyRound.Contains(weapon)) + if (_allHalfBuy.Contains(weapon)) { return RoundType.HalfBuy; } - if (_pistolRound.Contains(weapon)) + if (_allPistols.Contains(weapon)) { return RoundType.Pistol; } @@ -272,24 +331,61 @@ public static ICollection FindValidWeaponsByName(string needle) .ToList(); } + public static WeaponAllocationType? GetWeaponAllocationTypeForWeapon(CsItem weapon, RoundType roundType) + { + if (_allPistols.Contains(weapon)) + { + return roundType switch + { + RoundType.Pistol => WeaponAllocationType.PistolRound, + _ => WeaponAllocationType.Secondary, + }; + } + + if (_allHalfBuy.Contains(weapon) && roundType == RoundType.HalfBuy) + { + return WeaponAllocationType.HalfBuyPrimary; + } + + if (_allFullBuy.Contains(weapon) && roundType == RoundType.FullBuy) + { + return WeaponAllocationType.FullBuyPrimary; + } + + return null; + } + public static ICollection GetWeaponsForRoundType(RoundType roundType, CsTeam team, UserSetting? userSetting) { + WeaponAllocationType? primaryWeaponAllocation = roundType switch + { + RoundType.HalfBuy => WeaponAllocationType.HalfBuyPrimary, + RoundType.FullBuy => WeaponAllocationType.FullBuyPrimary, + _ => null, + }; + + var secondaryWeaponAllocation = roundType switch + { + RoundType.Pistol => WeaponAllocationType.PistolRound, + _ => WeaponAllocationType.Secondary, + }; + var weapons = new List(); - var weapon = GetWeaponForRoundType(RoundType.Pistol, team, userSetting); - if (weapon is not null) + var secondary = GetWeaponForAllocationType(secondaryWeaponAllocation, team, userSetting); + if (secondary is not null) { - weapons.Add(weapon.Value); + weapons.Add(secondary.Value); } - if (roundType == RoundType.Pistol) + if (primaryWeaponAllocation is null) { return weapons; } - weapon = GetWeaponForRoundType(roundType, team, userSetting); - if (weapon is not null) + var primary = GetWeaponForAllocationType(primaryWeaponAllocation.Value, team, userSetting); + if (primary is not null) { - weapons.Add(weapon.Value); + weapons.Add(primary.Value); } return weapons; @@ -309,34 +405,38 @@ private static ICollection FindItemsByName(string needle) .ToList(); } - private static CsItem GetDefaultWeaponForRoundType(RoundType roundType, CsTeam team) + private static CsItem GetDefaultWeaponForAllocationType(WeaponAllocationType allocationType, CsTeam team) { - return _defaultWeaponsByTeamAndRoundType[team][roundType]; + return _defaultWeaponsByTeamAndAllocationType[team][allocationType]; } - private static CsItem GetRandomWeaponForRoundType(RoundType roundType, CsTeam team) + private static CsItem GetRandomWeaponForAllocationType(WeaponAllocationType allocationType, CsTeam team) { if (team != CsTeam.Terrorist && team != CsTeam.CounterTerrorist) { return CsItem.Deagle; } - var collectionToCheck = roundType switch + var collectionToCheck = allocationType switch { - RoundType.Pistol => team == CsTeam.Terrorist ? _pistolsForT : _pistolsForCt, - RoundType.HalfBuy => team == CsTeam.Terrorist ? _smgsForT : _smgsForCt, - RoundType.FullBuy => team == CsTeam.Terrorist ? _tRifles : _ctRifles, + WeaponAllocationType.PistolRound => team == CsTeam.Terrorist ? _pistolsForT : _pistolsForCt, + WeaponAllocationType.Secondary => team == CsTeam.Terrorist ? _pistolsForT : _pistolsForCt, + WeaponAllocationType.HalfBuyPrimary => team == CsTeam.Terrorist ? _smgsForT : _smgsForCt, + WeaponAllocationType.FullBuyPrimary => team == CsTeam.Terrorist ? _tRifles : _ctRifles, + WeaponAllocationType.Sniper => team == CsTeam.Terrorist ? _snipersForT : _snipersForCt, _ => _sharedPistols, }; return Utils.Choice(collectionToCheck.Where(IsUsableWeapon).ToList()); } - public static CsItem? GetWeaponForRoundType(RoundType roundType, CsTeam team, UserSetting? userSetting) + public static CsItem? GetWeaponForAllocationType(WeaponAllocationType allocationType, CsTeam team, + UserSetting? userSetting) { CsItem? weapon = null; + if (Configs.GetConfigData().CanPlayersSelectWeapons() && userSetting is not null) { - var weaponPreference = userSetting.GetWeaponPreference(team, roundType); + var weaponPreference = userSetting.GetWeaponPreference(team, allocationType); if (weaponPreference is not null && IsUsableWeapon(weaponPreference.Value)) { weapon = weaponPreference; @@ -345,12 +445,12 @@ private static CsItem GetRandomWeaponForRoundType(RoundType roundType, CsTeam te if (weapon is null && Configs.GetConfigData().CanAssignRandomWeapons()) { - weapon = GetRandomWeaponForRoundType(roundType, team); + weapon = GetRandomWeaponForAllocationType(allocationType, team); } if (weapon is null && Configs.GetConfigData().CanAssignDefaultWeapons()) { - weapon = GetDefaultWeaponForRoundType(roundType, team); + weapon = GetDefaultWeaponForAllocationType(allocationType, team); } return weapon; diff --git a/RetakesAllocatorTest/DbTests.cs b/RetakesAllocatorTest/DbTests.cs index 123aafe..37f93a8 100644 --- a/RetakesAllocatorTest/DbTests.cs +++ b/RetakesAllocatorTest/DbTests.cs @@ -13,11 +13,12 @@ public void TestGetUsersSettings() var usersSettings = Queries.GetUsersSettings(new List()); Assert.That(usersSettings, Is.EqualTo(new Dictionary())); - Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, RoundType.HalfBuy, CsItem.Bizon); - Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, RoundType.Pistol, null); - Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, RoundType.HalfBuy, CsItem.MP5); - Queries.SetWeaponPreferenceForUser(1, CsTeam.CounterTerrorist, RoundType.FullBuy, CsItem.AWP); - Queries.SetWeaponPreferenceForUser(2, CsTeam.Terrorist, RoundType.FullBuy, CsItem.AK47); + // TODO Add secondary and sniper allocation types + Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, WeaponAllocationType.HalfBuyPrimary, CsItem.Bizon); + Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, WeaponAllocationType.PistolRound, null); + Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, WeaponAllocationType.HalfBuyPrimary, CsItem.MP5); + Queries.SetWeaponPreferenceForUser(1, CsTeam.CounterTerrorist, WeaponAllocationType.FullBuyPrimary, CsItem.AWP); + Queries.SetWeaponPreferenceForUser(2, CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary, CsItem.AK47); usersSettings = Queries.GetUsersSettings(new List {1}); Assert.Multiple(() => @@ -37,16 +38,16 @@ public void TestGetUsersSettings() Assert.That(usersSettings.Keys, Is.EquivalentTo(new List {1, 2})); Assert.That(usersSettings.Values.Select(v => v.UserId), Is.EquivalentTo(new List {1, 2})); - Assert.That(usersSettings[1].WeaponPreferences[CsTeam.Terrorist][RoundType.HalfBuy], + Assert.That(usersSettings[1].WeaponPreferences[CsTeam.Terrorist][WeaponAllocationType.HalfBuyPrimary], Is.EqualTo(CsItem.MP5)); - Assert.That(usersSettings[1].WeaponPreferences[CsTeam.Terrorist].TryGetValue(RoundType.Pistol, out _), + Assert.That(usersSettings[1].WeaponPreferences[CsTeam.Terrorist].TryGetValue(WeaponAllocationType.PistolRound, out _), Is.EqualTo(false)); - Assert.That(usersSettings[1].WeaponPreferences[CsTeam.CounterTerrorist][RoundType.FullBuy], + Assert.That(usersSettings[1].WeaponPreferences[CsTeam.CounterTerrorist][WeaponAllocationType.FullBuyPrimary], Is.EqualTo(CsItem.AWP)); Assert.That( - usersSettings[1].WeaponPreferences[CsTeam.CounterTerrorist].TryGetValue(RoundType.HalfBuy, out _), + usersSettings[1].WeaponPreferences[CsTeam.CounterTerrorist].TryGetValue(WeaponAllocationType.HalfBuyPrimary, out _), Is.EqualTo(false)); - Assert.That(usersSettings[2].WeaponPreferences[CsTeam.Terrorist][RoundType.FullBuy], + Assert.That(usersSettings[2].WeaponPreferences[CsTeam.Terrorist][WeaponAllocationType.FullBuyPrimary], Is.EqualTo(CsItem.AK47)); }); } diff --git a/RetakesAllocatorTest/WeaponSelectionTests.cs b/RetakesAllocatorTest/WeaponSelectionTests.cs index ee126e8..b7670a2 100644 --- a/RetakesAllocatorTest/WeaponSelectionTests.cs +++ b/RetakesAllocatorTest/WeaponSelectionTests.cs @@ -11,129 +11,130 @@ public class WeaponSelectionTests : BaseTestFixture [Test] public void SetWeaponPreferenceDirectly() { - Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.Terrorist, RoundType.FullBuy), + Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary), Is.EqualTo(null)); - Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, RoundType.FullBuy, CsItem.Galil); - Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.Terrorist, RoundType.FullBuy), + Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary, CsItem.Galil); + Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary), Is.EqualTo(CsItem.Galil)); - Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, RoundType.FullBuy, CsItem.AWP); - Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.Terrorist, RoundType.FullBuy), + Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary, CsItem.AWP); + Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary), Is.EqualTo(CsItem.AWP)); - Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, RoundType.Pistol, CsItem.Deagle); - Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.Terrorist, RoundType.Pistol), + Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, WeaponAllocationType.PistolRound, CsItem.Deagle); + Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.PistolRound), Is.EqualTo(CsItem.Deagle)); - Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.CounterTerrorist, RoundType.HalfBuy), + Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.HalfBuyPrimary), Is.EqualTo(null)); - Queries.SetWeaponPreferenceForUser(1, CsTeam.CounterTerrorist, RoundType.HalfBuy, CsItem.MP9); - Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.CounterTerrorist, RoundType.HalfBuy), + Queries.SetWeaponPreferenceForUser(1, CsTeam.CounterTerrorist, WeaponAllocationType.HalfBuyPrimary, CsItem.MP9); + Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.HalfBuyPrimary), Is.EqualTo(CsItem.MP9)); } - [Test] - [TestCase(CsTeam.Terrorist, "galil", CsItem.Galil, "Galil' is now", "Galil' is no longer")] - [TestCase(CsTeam.Terrorist, "krieg", CsItem.Krieg, "SG553' is now", "SG553' is no longer")] - [TestCase(CsTeam.Terrorist, "mac10", CsItem.Mac10, "Mac10' is now", "Mac10' is no longer")] - [TestCase(CsTeam.CounterTerrorist, "deag", CsItem.Deagle, "Deagle' is now", "Deagle' is no longer")] - [TestCase(CsTeam.CounterTerrorist, "galil", null, "Galil' is not valid", null)] - [TestCase(CsTeam.CounterTerrorist, "tec9", null, "Tec9' is not valid", null)] - [TestCase(CsTeam.Terrorist, "poop", null, "not found", null)] - public void SetWeaponPreferenceCommandSingleArg( - CsTeam team, string itemInput, - CsItem? expectedItem, - string message, - string? removeMessage - ) - { - var args = new List {itemInput}; - - var result = OnWeaponCommandHelper.Handle(args, 1, team, false, out var selectedItem); - - Assert.That(result, Does.Contain(message)); - Assert.That(selectedItem, Is.EqualTo(expectedItem)); - - var roundType = expectedItem is not null - ? WeaponHelpers.GetRoundTypeForWeapon(expectedItem.Value) ?? RoundType.Pistol - : RoundType.Pistol; - - var setWeapon = Queries.GetUserSettings(1)? - .GetWeaponPreference(team, roundType); - Assert.That(setWeapon, Is.EqualTo(expectedItem)); - - if (removeMessage is not null) - { - result = OnWeaponCommandHelper.Handle(args, 1, team, true, out _); - Assert.That(result, Does.Contain(removeMessage)); - - setWeapon = Queries.GetUserSettings(1)?.GetWeaponPreference(team, roundType); - Assert.That(setWeapon, Is.EqualTo(null)); - } - } - - [Test] - [TestCase("T", CsTeam.Terrorist, "galil", CsItem.Galil, "Galil' is now")] - [TestCase("T", CsTeam.Terrorist, "krieg", CsItem.Krieg, "SG553' is now")] - [TestCase("T", CsTeam.Terrorist, "mac10", CsItem.Mac10, "Mac10' is now")] - [TestCase("T", CsTeam.None, "mac10", null, "Mac10' is now")] - [TestCase("CT", CsTeam.CounterTerrorist, "deag", CsItem.Deagle, "Deagle' is now")] - [TestCase("CT", CsTeam.CounterTerrorist, "galil", null, "Galil' is not valid")] - [TestCase("CT", CsTeam.CounterTerrorist, "tec9", null, "Tec9' is not valid")] - [TestCase("T", CsTeam.Terrorist, "poop", null, "not found")] - public void SetWeaponPreferenceCommandMultiArg( - string teamInput, - CsTeam currentTeam, - string itemInput, - CsItem? expectedItem, - string message - ) - { - var args = new List {itemInput, teamInput}; - - var result = OnWeaponCommandHelper.Handle(args, 1, currentTeam, false, out var selectedItem); - - Assert.That(result, Does.Contain(message)); - Assert.That(selectedItem, Is.EqualTo(expectedItem)); - - var roundType = expectedItem is not null - ? WeaponHelpers.GetRoundTypeForWeapon(expectedItem.Value) ?? RoundType.Pistol - : RoundType.Pistol; - - var setWeapon = Queries.GetUserSettings(1)?.GetWeaponPreference(Utils.ParseTeam(teamInput), roundType); - Assert.That(setWeapon, Is.EqualTo(expectedItem)); - } - - [Test] - [TestCase("ak", CsItem.AK47, WeaponSelectionType.PlayerChoice, CsItem.AK47, "AK47' is now")] - [TestCase("ak", CsItem.Galil, WeaponSelectionType.PlayerChoice, null, "not allowed")] - [TestCase("ak", CsItem.AK47, WeaponSelectionType.Default, null, "cannot choose")] - public void SetWeaponPreferencesConfig( - string itemName, - CsItem? allowedItem, - WeaponSelectionType weaponSelectionType, - CsItem? expectedItem, - string message - ) - { - var team = CsTeam.Terrorist; - Configs.GetConfigData().AllowedWeaponSelectionTypes = new List {weaponSelectionType}; - Configs.GetConfigData().UsableWeapons = new List { }; - if (allowedItem is not null) - { - Configs.GetConfigData().UsableWeapons.Add(allowedItem.Value); - } - - var args = new List {itemName}; - var result = OnWeaponCommandHelper.Handle(args, 1, team, false, out var selectedItem); - - Assert.That(result, Does.Contain(message)); - Assert.That(selectedItem, Is.EqualTo(expectedItem)); - - var setWeapon = Queries.GetUserSettings(1)?.GetWeaponPreference(team, RoundType.FullBuy); - Assert.That(setWeapon, Is.EqualTo(expectedItem)); - } + // TODO Fix these tests + // [Test] + // [TestCase(CsTeam.Terrorist, "galil", CsItem.Galil, "Galil' is now", "Galil' is no longer")] + // [TestCase(CsTeam.Terrorist, "krieg", CsItem.Krieg, "SG553' is now", "SG553' is no longer")] + // [TestCase(CsTeam.Terrorist, "mac10", CsItem.Mac10, "Mac10' is now", "Mac10' is no longer")] + // [TestCase(CsTeam.CounterTerrorist, "deag", CsItem.Deagle, "Deagle' is now", "Deagle' is no longer")] + // [TestCase(CsTeam.CounterTerrorist, "galil", null, "Galil' is not valid", null)] + // [TestCase(CsTeam.CounterTerrorist, "tec9", null, "Tec9' is not valid", null)] + // [TestCase(CsTeam.Terrorist, "poop", null, "not found", null)] + // public void SetWeaponPreferenceCommandSingleArg( + // CsTeam team, string itemInput, + // CsItem? expectedItem, + // string message, + // string? removeMessage + // ) + // { + // var args = new List {itemInput}; + // + // var result = OnWeaponCommandHelper.Handle(args, 1, team, false, out var selectedItem); + // + // Assert.That(result, Does.Contain(message)); + // Assert.That(selectedItem, Is.EqualTo(expectedItem)); + // + // var roundType = expectedItem is not null + // ? WeaponHelpers.GetRoundTypeForWeapon(expectedItem.Value) ?? RoundType.Pistol + // : RoundType.Pistol; + // + // var setWeapon = Queries.GetUserSettings(1)? + // .GetWeaponPreference(team, roundType); + // Assert.That(setWeapon, Is.EqualTo(expectedItem)); + // + // if (removeMessage is not null) + // { + // result = OnWeaponCommandHelper.Handle(args, 1, team, true, out _); + // Assert.That(result, Does.Contain(removeMessage)); + // + // setWeapon = Queries.GetUserSettings(1)?.GetWeaponPreference(team, roundType); + // Assert.That(setWeapon, Is.EqualTo(null)); + // } + // } + // + // [Test] + // [TestCase("T", CsTeam.Terrorist, "galil", CsItem.Galil, "Galil' is now")] + // [TestCase("T", CsTeam.Terrorist, "krieg", CsItem.Krieg, "SG553' is now")] + // [TestCase("T", CsTeam.Terrorist, "mac10", CsItem.Mac10, "Mac10' is now")] + // [TestCase("T", CsTeam.None, "mac10", null, "Mac10' is now")] + // [TestCase("CT", CsTeam.CounterTerrorist, "deag", CsItem.Deagle, "Deagle' is now")] + // [TestCase("CT", CsTeam.CounterTerrorist, "galil", null, "Galil' is not valid")] + // [TestCase("CT", CsTeam.CounterTerrorist, "tec9", null, "Tec9' is not valid")] + // [TestCase("T", CsTeam.Terrorist, "poop", null, "not found")] + // public void SetWeaponPreferenceCommandMultiArg( + // string teamInput, + // CsTeam currentTeam, + // string itemInput, + // CsItem? expectedItem, + // string message + // ) + // { + // var args = new List {itemInput, teamInput}; + // + // var result = OnWeaponCommandHelper.Handle(args, 1, currentTeam, false, out var selectedItem); + // + // Assert.That(result, Does.Contain(message)); + // Assert.That(selectedItem, Is.EqualTo(expectedItem)); + // + // var roundType = expectedItem is not null + // ? WeaponHelpers.GetRoundTypeForWeapon(expectedItem.Value) ?? RoundType.Pistol + // : RoundType.Pistol; + // + // var setWeapon = Queries.GetUserSettings(1)?.GetWeaponPreference(Utils.ParseTeam(teamInput), roundType); + // Assert.That(setWeapon, Is.EqualTo(expectedItem)); + // } + // + // [Test] + // [TestCase("ak", CsItem.AK47, WeaponSelectionType.PlayerChoice, CsItem.AK47, "AK47' is now")] + // [TestCase("ak", CsItem.Galil, WeaponSelectionType.PlayerChoice, null, "not allowed")] + // [TestCase("ak", CsItem.AK47, WeaponSelectionType.Default, null, "cannot choose")] + // public void SetWeaponPreferencesConfig( + // string itemName, + // CsItem? allowedItem, + // WeaponSelectionType weaponSelectionType, + // CsItem? expectedItem, + // string message + // ) + // { + // var team = CsTeam.Terrorist; + // Configs.GetConfigData().AllowedWeaponSelectionTypes = new List {weaponSelectionType}; + // Configs.GetConfigData().UsableWeapons = new List { }; + // if (allowedItem is not null) + // { + // Configs.GetConfigData().UsableWeapons.Add(allowedItem.Value); + // } + // + // var args = new List {itemName}; + // var result = OnWeaponCommandHelper.Handle(args, 1, team, false, out var selectedItem); + // + // Assert.That(result, Does.Contain(message)); + // Assert.That(selectedItem, Is.EqualTo(expectedItem)); + // + // var setWeapon = Queries.GetUserSettings(1)?.GetWeaponPreference(team, RoundType.FullBuy); + // Assert.That(setWeapon, Is.EqualTo(expectedItem)); + // } [Test] public void RandomWeaponSelection() From 9718ac0880f2b1f20bc40aedf222e4714b47ca60 Mon Sep 17 00:00:00 2001 From: Yoni Lerner Date: Sat, 13 Jan 2024 21:57:53 -0500 Subject: [PATCH 06/14] filtered by sniper preference --- .../OnRoundPostStartHelper.cs | 32 ++++++++++++------- RetakesAllocatorCore/WeaponHelpers.cs | 28 ++++++++++------ 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/RetakesAllocatorCore/OnRoundPostStartHelper.cs b/RetakesAllocatorCore/OnRoundPostStartHelper.cs index 675ea47..23bded0 100644 --- a/RetakesAllocatorCore/OnRoundPostStartHelper.cs +++ b/RetakesAllocatorCore/OnRoundPostStartHelper.cs @@ -50,37 +50,45 @@ out RoundType currentRoundType var defusingPlayer = Utils.Choice(ctPlayers); + HashSet FilterBySniperPreference(IEnumerable ps) => + ps.Where(p => + userSettingsByPlayerId.TryGetValue(getSteamId(p), out var userSetting) && + userSetting.GetWeaponPreference(getTeam(p), WeaponAllocationType.Sniper) is not null) + .ToHashSet(); + ICollection tSnipers = new HashSet(); ICollection ctSnipers = new HashSet(); if (roundType == RoundType.FullBuy) { - // Filter by players that actually want a sniper - tSnipers = WeaponHelpers.SelectSnipers(tPlayers); - ctSnipers = WeaponHelpers.SelectSnipers(ctPlayers); + tSnipers = WeaponHelpers.SelectSnipers(FilterBySniperPreference(tPlayers)); + ctSnipers = WeaponHelpers.SelectSnipers(FilterBySniperPreference(ctPlayers)); } foreach (var player in allPlayers) { var team = getTeam(player); var playerSteamId = getSteamId(player); - userSettingsByPlayerId.TryGetValue(playerSteamId, out var userSettings); + userSettingsByPlayerId.TryGetValue(playerSteamId, out var userSetting); var items = new List { RoundTypeHelpers.GetArmorForRoundType(roundType), team == CsTeam.Terrorist ? CsItem.DefaultKnifeT : CsItem.DefaultKnifeCT, }; - var playerWeaponsRoundType = roundType; - switch (team) + var giveSniper = team switch { - case CsTeam.Terrorist when tSnipers.Contains(player): - case CsTeam.CounterTerrorist when ctSnipers.Contains(player): - playerWeaponsRoundType = RoundType.Pistol; - break; - } + CsTeam.Terrorist => tSnipers.Contains(player), + CsTeam.CounterTerrorist => ctSnipers.Contains(player), + _ => false, + }; items.AddRange( - WeaponHelpers.GetWeaponsForRoundType(playerWeaponsRoundType, team, userSettings) + WeaponHelpers.GetWeaponsForRoundType( + roundType, + team, + userSetting, + giveSniper + ) ); if (team == CsTeam.CounterTerrorist) diff --git a/RetakesAllocatorCore/WeaponHelpers.cs b/RetakesAllocatorCore/WeaponHelpers.cs index 7a1f636..14ba2c0 100644 --- a/RetakesAllocatorCore/WeaponHelpers.cs +++ b/RetakesAllocatorCore/WeaponHelpers.cs @@ -215,7 +215,8 @@ public static bool IsWeapon(CsItem item) return _allWeapons.Contains(item); } - public static ICollection GetPossibleWeaponsForAllocationType(WeaponAllocationType allocationType, CsTeam team) + public static ICollection GetPossibleWeaponsForAllocationType(WeaponAllocationType allocationType, + CsTeam team) { return _validWeaponsByTeamAndAllocationType[team][allocationType].Where(IsUsableWeapon).ToList(); } @@ -267,6 +268,7 @@ public static bool IsSniper(CsTeam team, CsItem weapon) }; } + // TODO In the future this will be more complex based on sniper config public static ICollection SelectSnipers(ICollection players) { var player = Utils.Choice(players); @@ -355,14 +357,22 @@ public static ICollection FindValidWeaponsByName(string needle) return null; } - public static ICollection GetWeaponsForRoundType(RoundType roundType, CsTeam team, UserSetting? userSetting) - { - WeaponAllocationType? primaryWeaponAllocation = roundType switch - { - RoundType.HalfBuy => WeaponAllocationType.HalfBuyPrimary, - RoundType.FullBuy => WeaponAllocationType.FullBuyPrimary, - _ => null, - }; + public static ICollection GetWeaponsForRoundType( + RoundType roundType, + CsTeam team, + UserSetting? userSetting, + bool giveSniper + ) + { + WeaponAllocationType? primaryWeaponAllocation = + giveSniper + ? WeaponAllocationType.Sniper + : roundType switch + { + RoundType.HalfBuy => WeaponAllocationType.HalfBuyPrimary, + RoundType.FullBuy => WeaponAllocationType.FullBuyPrimary, + _ => null, + }; var secondaryWeaponAllocation = roundType switch { From e1ff05f85b93fc099991601aa84fd689e7af6df4 Mon Sep 17 00:00:00 2001 From: Yoni Lerner Date: Sun, 14 Jan 2024 00:56:56 -0500 Subject: [PATCH 07/14] saving progress; tests complete, weap command seems good --- RetakesAllocatorCore/OnWeaponCommandHelper.cs | 35 ++- RetakesAllocatorCore/WeaponHelpers.cs | 75 ++++- RetakesAllocatorTest/DbTests.cs | 39 ++- RetakesAllocatorTest/WeaponSelectionTests.cs | 263 +++++++++--------- 4 files changed, 244 insertions(+), 168 deletions(-) diff --git a/RetakesAllocatorCore/OnWeaponCommandHelper.cs b/RetakesAllocatorCore/OnWeaponCommandHelper.cs index d1d8b63..73cd58d 100644 --- a/RetakesAllocatorCore/OnWeaponCommandHelper.cs +++ b/RetakesAllocatorCore/OnWeaponCommandHelper.cs @@ -7,7 +7,8 @@ namespace RetakesAllocatorCore; public class OnWeaponCommandHelper { - public static string? Handle(ICollection args, ulong userId, CsTeam currentTeam, bool remove, out CsItem? outWeapon) + public static string? Handle(ICollection args, ulong userId, RoundType roundType, CsTeam currentTeam, + bool remove, out CsItem? outWeapon) { outWeapon = null; if (!Configs.GetConfigData().CanPlayersSelectWeapons()) @@ -51,22 +52,32 @@ public class OnWeaponCommandHelper return $"Weapon '{weapon}' is not allowed to be selected."; } - var roundType = WeaponHelpers.GetRoundTypeForWeapon(weapon); - if (roundType is null) + var weaponRoundTypes = WeaponHelpers.GetRoundTypesForWeapon(weapon); + if (weaponRoundTypes.Count == 0) { return $"Invalid weapon '{weapon}'"; } var allocationType = WeaponHelpers.WeaponAllocationTypeForWeaponAndRound( - roundType.Value, team, weapon + roundType, team, weapon + ); + var isSniper = allocationType == WeaponAllocationType.Sniper; + + var allocateImmediately = ( + // Always true for pistols + weaponRoundTypes.Contains(roundType) && + // Only set the outWeapon if the user is setting the preference for their current team + currentTeam == team && + // TODO Allow immediate allocation of sniper if the config permits it (eg. unlimited snipers) + // Could be tricky for max # per team config, since this function doesnt know # of players on the team + !isSniper ); if (allocationType is null) { - return $"Weapon '{weapon}' is not valid for {roundType} rounds on {team}"; + return $"Weapon '{weapon}' is not valid for {team}"; } - var isSniper = WeaponHelpers.IsSniper(team, weapon); if (remove) { @@ -77,7 +88,6 @@ public class OnWeaponCommandHelper } else { - // TODO double check this Queries.SetWeaponPreferenceForUser(userId, team, allocationType.Value, null); return $"Weapon '{weapon}' is no longer your {roundType} preference for {team}."; } @@ -87,21 +97,22 @@ public class OnWeaponCommandHelper if (isSniper) { Queries.SetSniperPreference(userId, weapon); - message = $"You will now get a '{weapon}' when its your turn."; + message = $"You will now get a '{weapon}' when its your turn for a sniper."; } else { - // TODO double check this Queries.SetWeaponPreferenceForUser(userId, team, allocationType.Value, weapon); message = $"Weapon '{weapon}' is now your {roundType} preference for {team}."; } - // TODO dont set sniper immediately if its not a valid time to allocate it - if (currentTeam == team) + if (allocateImmediately) { - // Only set the outWeapon if the user is setting the preference for their current team outWeapon = weapon; } + else if (!isSniper) + { + message += $" You will get it at the next {weaponRoundTypes.First()} round."; + } return message; } diff --git a/RetakesAllocatorCore/WeaponHelpers.cs b/RetakesAllocatorCore/WeaponHelpers.cs index 14ba2c0..f827a26 100644 --- a/RetakesAllocatorCore/WeaponHelpers.cs +++ b/RetakesAllocatorCore/WeaponHelpers.cs @@ -1,3 +1,4 @@ +using System.Collections; using CounterStrikeSharp.API.Modules.Entities.Constants; using CounterStrikeSharp.API.Modules.Utils; using RetakesAllocatorCore.Config; @@ -100,8 +101,9 @@ public static class WeaponHelpers private static readonly ICollection _sharedSnipers = new HashSet { - CsItem.AWP, + // TODO Make Scout a half buy weapon CsItem.Scout, + CsItem.AWP, }; private static readonly ICollection _tSnipers = new HashSet @@ -145,6 +147,21 @@ public static class WeaponHelpers private static readonly ICollection _allPistols = _pistolsForT.Concat(_pistolsForCt).ToHashSet(); + private static readonly Dictionary> + _validAllocationTypesForRound = new() + { + {RoundType.Pistol, new HashSet {WeaponAllocationType.PistolRound}}, + { + RoundType.HalfBuy, + new HashSet {WeaponAllocationType.Secondary, WeaponAllocationType.HalfBuyPrimary} + }, + { + RoundType.FullBuy, + new HashSet + {WeaponAllocationType.Secondary, WeaponAllocationType.FullBuyPrimary, WeaponAllocationType.Sniper} + }, + }; + private static readonly Dictionary< CsTeam, Dictionary> @@ -221,16 +238,9 @@ public static ICollection GetPossibleWeaponsForAllocationType(WeaponAllo return _validWeaponsByTeamAndAllocationType[team][allocationType].Where(IsUsableWeapon).ToList(); } - // TODO Im not convinced this is reasonable public static bool IsAllocationTypeValidForRound(WeaponAllocationType allocationType, RoundType roundType) { - return roundType switch - { - RoundType.Pistol => allocationType is WeaponAllocationType.PistolRound or WeaponAllocationType.Secondary, - RoundType.HalfBuy => allocationType == WeaponAllocationType.HalfBuyPrimary, - RoundType.FullBuy => allocationType is WeaponAllocationType.FullBuyPrimary or WeaponAllocationType.Sniper, - _ => false - }; + return _validAllocationTypesForRound[roundType].Contains(allocationType); } public static WeaponAllocationType? WeaponAllocationTypeForWeaponAndRound(RoundType roundType, CsTeam team, @@ -241,15 +251,32 @@ public static bool IsAllocationTypeValidForRound(WeaponAllocationType allocation return null; } - if (_allSnipers.Contains(weapon)) + // First populate all allocation types that could match + // For a pistol this could be multiple allocation types, for any other weapon type only one can match + var potentialAllocationTypes = new HashSet(); + foreach (var (allocationType, items) in _validWeaponsByTeamAndAllocationType[team]) + { + if (items.Contains(weapon)) + { + potentialAllocationTypes.Add(allocationType); + } + } + + // If theres only 1 to choose from, return that, or return null if there are none + if (potentialAllocationTypes.Count == 1) + { + return potentialAllocationTypes.First(); + } + if (potentialAllocationTypes.Count == 0) { - return WeaponAllocationType.Sniper; + return null; } - var allocationsByTeam = _validWeaponsByTeamAndAllocationType[team]; - foreach (var (allocationType, items) in allocationsByTeam) + // For a pistol, the set will be {PistolRound, Secondary} + // We need to find which of those matches the current round type + foreach (var allocationType in potentialAllocationTypes) { - if (items.Contains(weapon) && IsAllocationTypeValidForRound(allocationType, roundType)) + if (IsAllocationTypeValidForRound(allocationType, roundType)) { return allocationType; } @@ -305,6 +332,26 @@ public static bool IsUsableWeapon(CsItem weapon) return team == CsTeam.Terrorist ? CsItem.AutoSniperT : CsItem.AutoSniperCT; } + public static ICollection GetRoundTypesForWeapon(CsItem weapon) + { + if (_allPistols.Contains(weapon)) + { + return new HashSet {RoundType.Pistol, RoundType.HalfBuy, RoundType.FullBuy}; + } + + if (_allHalfBuy.Contains(weapon)) + { + return new HashSet {RoundType.HalfBuy}; + } + + if (_allFullBuy.Contains(weapon)) + { + return new HashSet {RoundType.FullBuy}; + } + + return new HashSet(); + } + // TODO Change all usages of this public static RoundType? GetRoundTypeForWeapon(CsItem weapon) { diff --git a/RetakesAllocatorTest/DbTests.cs b/RetakesAllocatorTest/DbTests.cs index 37f93a8..17648b2 100644 --- a/RetakesAllocatorTest/DbTests.cs +++ b/RetakesAllocatorTest/DbTests.cs @@ -13,12 +13,18 @@ public void TestGetUsersSettings() var usersSettings = Queries.GetUsersSettings(new List()); Assert.That(usersSettings, Is.EqualTo(new Dictionary())); - // TODO Add secondary and sniper allocation types Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, WeaponAllocationType.HalfBuyPrimary, CsItem.Bizon); Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, WeaponAllocationType.PistolRound, null); Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, WeaponAllocationType.HalfBuyPrimary, CsItem.MP5); - Queries.SetWeaponPreferenceForUser(1, CsTeam.CounterTerrorist, WeaponAllocationType.FullBuyPrimary, CsItem.AWP); + Queries.SetWeaponPreferenceForUser(1, CsTeam.CounterTerrorist, WeaponAllocationType.FullBuyPrimary, CsItem.AK47); + // Should set for both T and CT + Queries.SetSniperPreference(1, CsItem.AWP); + Queries.SetWeaponPreferenceForUser(2, CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary, CsItem.AK47); + Queries.SetWeaponPreferenceForUser(2, CsTeam.Terrorist, WeaponAllocationType.Secondary, CsItem.Deagle); + Queries.SetWeaponPreferenceForUser(2, CsTeam.CounterTerrorist, WeaponAllocationType.Secondary, CsItem.FiveSeven); + // Will get different snipers for different teams + Queries.SetSniperPreference(2, CsItem.SCAR20); usersSettings = Queries.GetUsersSettings(new List {1}); Assert.Multiple(() => @@ -38,17 +44,30 @@ public void TestGetUsersSettings() Assert.That(usersSettings.Keys, Is.EquivalentTo(new List {1, 2})); Assert.That(usersSettings.Values.Select(v => v.UserId), Is.EquivalentTo(new List {1, 2})); - Assert.That(usersSettings[1].WeaponPreferences[CsTeam.Terrorist][WeaponAllocationType.HalfBuyPrimary], + Assert.That(usersSettings[1].GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.HalfBuyPrimary), Is.EqualTo(CsItem.MP5)); - Assert.That(usersSettings[1].WeaponPreferences[CsTeam.Terrorist].TryGetValue(WeaponAllocationType.PistolRound, out _), - Is.EqualTo(false)); - Assert.That(usersSettings[1].WeaponPreferences[CsTeam.CounterTerrorist][WeaponAllocationType.FullBuyPrimary], + Assert.That(usersSettings[1].GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.PistolRound), + Is.EqualTo(null)); + Assert.That(usersSettings[1].GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.FullBuyPrimary), + Is.EqualTo(CsItem.AK47)); + Assert.That(usersSettings[1].GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.HalfBuyPrimary), + Is.EqualTo(null)); + Assert.That(usersSettings[1].GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.Sniper), + Is.EqualTo(CsItem.AWP)); + Assert.That(usersSettings[1].GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.Sniper), Is.EqualTo(CsItem.AWP)); - Assert.That( - usersSettings[1].WeaponPreferences[CsTeam.CounterTerrorist].TryGetValue(WeaponAllocationType.HalfBuyPrimary, out _), - Is.EqualTo(false)); - Assert.That(usersSettings[2].WeaponPreferences[CsTeam.Terrorist][WeaponAllocationType.FullBuyPrimary], + + Assert.That(usersSettings[2].GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary), Is.EqualTo(CsItem.AK47)); + Assert.That(usersSettings[2].GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.Secondary), + Is.EqualTo(CsItem.Deagle)); + Assert.That(usersSettings[2].GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.Secondary), + Is.EqualTo(CsItem.FiveSeven)); + Assert.That(usersSettings[2].GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.Sniper), + Is.EqualTo(CsItem.AutoSniperT)); + Assert.That(usersSettings[2].GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.Sniper), + Is.EqualTo(CsItem.AutoSniperCT)); + }); } } diff --git a/RetakesAllocatorTest/WeaponSelectionTests.cs b/RetakesAllocatorTest/WeaponSelectionTests.cs index b7670a2..d35c3aa 100644 --- a/RetakesAllocatorTest/WeaponSelectionTests.cs +++ b/RetakesAllocatorTest/WeaponSelectionTests.cs @@ -11,137 +11,137 @@ public class WeaponSelectionTests : BaseTestFixture [Test] public void SetWeaponPreferenceDirectly() { - Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary), + Assert.That( + Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary), Is.EqualTo(null)); Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary, CsItem.Galil); - Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary), + Assert.That( + Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary), Is.EqualTo(CsItem.Galil)); Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary, CsItem.AWP); - Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary), + Assert.That( + Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary), Is.EqualTo(CsItem.AWP)); Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, WeaponAllocationType.PistolRound, CsItem.Deagle); Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.PistolRound), Is.EqualTo(CsItem.Deagle)); - Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.HalfBuyPrimary), + Assert.That( + Queries.GetUserSettings(1) + ?.GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.HalfBuyPrimary), Is.EqualTo(null)); Queries.SetWeaponPreferenceForUser(1, CsTeam.CounterTerrorist, WeaponAllocationType.HalfBuyPrimary, CsItem.MP9); - Assert.That(Queries.GetUserSettings(1)?.GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.HalfBuyPrimary), + Assert.That( + Queries.GetUserSettings(1) + ?.GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.HalfBuyPrimary), Is.EqualTo(CsItem.MP9)); } - // TODO Fix these tests - // [Test] - // [TestCase(CsTeam.Terrorist, "galil", CsItem.Galil, "Galil' is now", "Galil' is no longer")] - // [TestCase(CsTeam.Terrorist, "krieg", CsItem.Krieg, "SG553' is now", "SG553' is no longer")] - // [TestCase(CsTeam.Terrorist, "mac10", CsItem.Mac10, "Mac10' is now", "Mac10' is no longer")] - // [TestCase(CsTeam.CounterTerrorist, "deag", CsItem.Deagle, "Deagle' is now", "Deagle' is no longer")] - // [TestCase(CsTeam.CounterTerrorist, "galil", null, "Galil' is not valid", null)] - // [TestCase(CsTeam.CounterTerrorist, "tec9", null, "Tec9' is not valid", null)] - // [TestCase(CsTeam.Terrorist, "poop", null, "not found", null)] - // public void SetWeaponPreferenceCommandSingleArg( - // CsTeam team, string itemInput, - // CsItem? expectedItem, - // string message, - // string? removeMessage - // ) - // { - // var args = new List {itemInput}; - // - // var result = OnWeaponCommandHelper.Handle(args, 1, team, false, out var selectedItem); - // - // Assert.That(result, Does.Contain(message)); - // Assert.That(selectedItem, Is.EqualTo(expectedItem)); - // - // var roundType = expectedItem is not null - // ? WeaponHelpers.GetRoundTypeForWeapon(expectedItem.Value) ?? RoundType.Pistol - // : RoundType.Pistol; - // - // var setWeapon = Queries.GetUserSettings(1)? - // .GetWeaponPreference(team, roundType); - // Assert.That(setWeapon, Is.EqualTo(expectedItem)); - // - // if (removeMessage is not null) - // { - // result = OnWeaponCommandHelper.Handle(args, 1, team, true, out _); - // Assert.That(result, Does.Contain(removeMessage)); - // - // setWeapon = Queries.GetUserSettings(1)?.GetWeaponPreference(team, roundType); - // Assert.That(setWeapon, Is.EqualTo(null)); - // } - // } - // - // [Test] - // [TestCase("T", CsTeam.Terrorist, "galil", CsItem.Galil, "Galil' is now")] - // [TestCase("T", CsTeam.Terrorist, "krieg", CsItem.Krieg, "SG553' is now")] - // [TestCase("T", CsTeam.Terrorist, "mac10", CsItem.Mac10, "Mac10' is now")] - // [TestCase("T", CsTeam.None, "mac10", null, "Mac10' is now")] - // [TestCase("CT", CsTeam.CounterTerrorist, "deag", CsItem.Deagle, "Deagle' is now")] - // [TestCase("CT", CsTeam.CounterTerrorist, "galil", null, "Galil' is not valid")] - // [TestCase("CT", CsTeam.CounterTerrorist, "tec9", null, "Tec9' is not valid")] - // [TestCase("T", CsTeam.Terrorist, "poop", null, "not found")] - // public void SetWeaponPreferenceCommandMultiArg( - // string teamInput, - // CsTeam currentTeam, - // string itemInput, - // CsItem? expectedItem, - // string message - // ) - // { - // var args = new List {itemInput, teamInput}; - // - // var result = OnWeaponCommandHelper.Handle(args, 1, currentTeam, false, out var selectedItem); - // - // Assert.That(result, Does.Contain(message)); - // Assert.That(selectedItem, Is.EqualTo(expectedItem)); - // - // var roundType = expectedItem is not null - // ? WeaponHelpers.GetRoundTypeForWeapon(expectedItem.Value) ?? RoundType.Pistol - // : RoundType.Pistol; - // - // var setWeapon = Queries.GetUserSettings(1)?.GetWeaponPreference(Utils.ParseTeam(teamInput), roundType); - // Assert.That(setWeapon, Is.EqualTo(expectedItem)); - // } - // - // [Test] - // [TestCase("ak", CsItem.AK47, WeaponSelectionType.PlayerChoice, CsItem.AK47, "AK47' is now")] - // [TestCase("ak", CsItem.Galil, WeaponSelectionType.PlayerChoice, null, "not allowed")] - // [TestCase("ak", CsItem.AK47, WeaponSelectionType.Default, null, "cannot choose")] - // public void SetWeaponPreferencesConfig( - // string itemName, - // CsItem? allowedItem, - // WeaponSelectionType weaponSelectionType, - // CsItem? expectedItem, - // string message - // ) - // { - // var team = CsTeam.Terrorist; - // Configs.GetConfigData().AllowedWeaponSelectionTypes = new List {weaponSelectionType}; - // Configs.GetConfigData().UsableWeapons = new List { }; - // if (allowedItem is not null) - // { - // Configs.GetConfigData().UsableWeapons.Add(allowedItem.Value); - // } - // - // var args = new List {itemName}; - // var result = OnWeaponCommandHelper.Handle(args, 1, team, false, out var selectedItem); - // - // Assert.That(result, Does.Contain(message)); - // Assert.That(selectedItem, Is.EqualTo(expectedItem)); - // - // var setWeapon = Queries.GetUserSettings(1)?.GetWeaponPreference(team, RoundType.FullBuy); - // Assert.That(setWeapon, Is.EqualTo(expectedItem)); - // } + [Test] + [TestCase(RoundType.FullBuy, CsTeam.Terrorist, "galil", CsItem.Galil, "Galil' is now", "Galil' is no longer")] + [TestCase(RoundType.HalfBuy, CsTeam.Terrorist, "galil", null, "Galil' is now;;;at the next FullBuy", "Galil' is no longer")] + [TestCase(RoundType.FullBuy, CsTeam.Terrorist, "krieg", CsItem.Krieg, "SG553' is now", "SG553' is no longer")] + [TestCase(RoundType.HalfBuy, CsTeam.Terrorist, "mac10", CsItem.Mac10, "Mac10' is now", "Mac10' is no longer")] + [TestCase(RoundType.FullBuy, CsTeam.Terrorist, "mac10", null, "Mac10' is now;;;at the next HalfBuy", "Mac10' is no longer")] + [TestCase(RoundType.Pistol, CsTeam.CounterTerrorist, "deag", CsItem.Deagle, "Deagle' is now", + "Deagle' is no longer")] + [TestCase(RoundType.FullBuy, CsTeam.CounterTerrorist, "deag", CsItem.Deagle, "Deagle' is now", + "Deagle' is no longer")] + [TestCase(RoundType.HalfBuy, CsTeam.CounterTerrorist, "deag", CsItem.Deagle, "Deagle' is now", + "Deagle' is no longer")] + [TestCase(RoundType.FullBuy, CsTeam.CounterTerrorist, "galil", null, "Galil' is not valid", null)] + [TestCase(RoundType.Pistol, CsTeam.CounterTerrorist, "tec9", null, "Tec9' is not valid", null)] + [TestCase(RoundType.FullBuy, CsTeam.Terrorist, "poop", null, "not found", null)] + [TestCase(RoundType.FullBuy, CsTeam.Terrorist, "galil,T", CsItem.Galil, "Galil' is now", null)] + [TestCase(RoundType.FullBuy, CsTeam.Terrorist, "krieg,T", CsItem.Krieg, "SG553' is now", null)] + [TestCase(RoundType.HalfBuy, CsTeam.Terrorist, "mac10,T", CsItem.Mac10, "Mac10' is now", null)] + [TestCase(RoundType.HalfBuy, CsTeam.None, "mac10,T", null, "Mac10' is now", null)] + [TestCase(RoundType.Pistol, CsTeam.CounterTerrorist, "deag,CT", CsItem.Deagle, "Deagle' is now", null)] + [TestCase(RoundType.FullBuy, CsTeam.CounterTerrorist, "galil,CT", null, "Galil' is not valid", null)] + [TestCase(RoundType.Pistol, CsTeam.CounterTerrorist, "tec9,CT", null, "Tec9' is not valid", null)] + [TestCase(RoundType.FullBuy, CsTeam.Terrorist, "poop,T", null, "not found", null)] + public void SetWeaponPreferenceCommandSingleArg( + RoundType roundType, + CsTeam team, + string strArgs, + CsItem? expectedItem, + string message, + string? removeMessage + ) + { + var args = strArgs.Split(","); + + var result = OnWeaponCommandHelper.Handle(args, 1, roundType, team, false, out var selectedItem); + + var messages = message.Split(";;;"); + foreach (var m in messages) + { + Assert.That(result, Does.Contain(m)); + } + + Assert.That(selectedItem, Is.EqualTo(expectedItem)); + + var allocationType = + selectedItem is not null + ? WeaponHelpers.GetWeaponAllocationTypeForWeapon(selectedItem.Value, roundType) + : null; + + var setWeapon = allocationType is not null + ? Queries.GetUserSettings(1)? + .GetWeaponPreference(team, allocationType.Value) + : null; + Assert.That(setWeapon, Is.EqualTo(expectedItem)); + + if (removeMessage is not null) + { + result = OnWeaponCommandHelper.Handle(args, 1, roundType, team, true, out _); + Assert.That(result, Does.Contain(removeMessage)); + + setWeapon = allocationType is not null + ? Queries.GetUserSettings(1)?.GetWeaponPreference(team, allocationType.Value) + : null; + Assert.That(setWeapon, Is.EqualTo(null)); + } + } [Test] - public void RandomWeaponSelection() + [TestCase("ak", CsItem.AK47, WeaponSelectionType.PlayerChoice, CsItem.AK47, "AK47' is now")] + [TestCase("ak", CsItem.Galil, WeaponSelectionType.PlayerChoice, null, "not allowed")] + [TestCase("ak", CsItem.AK47, WeaponSelectionType.Default, null, "cannot choose")] + public void SetWeaponPreferencesConfig( + string itemName, + CsItem? allowedItem, + WeaponSelectionType weaponSelectionType, + CsItem? expectedItem, + string message + ) { - for (var j = 0; j < 1000; j++) + var team = CsTeam.Terrorist; + Configs.GetConfigData().AllowedWeaponSelectionTypes = new List {weaponSelectionType}; + Configs.GetConfigData().UsableWeapons = new List { }; + if (allowedItem is not null) { - Configs.OverrideConfigDataForTests(new ConfigData + Configs.GetConfigData().UsableWeapons.Add(allowedItem.Value); + } + + var args = new List {itemName}; + var result = OnWeaponCommandHelper.Handle(args, 1, RoundType.FullBuy, team, false, out var selectedItem); + + Assert.That(result, Does.Contain(message)); + Assert.That(selectedItem, Is.EqualTo(expectedItem)); + + var setWeapon = Queries.GetUserSettings(1)?.GetWeaponPreference(team, WeaponAllocationType.FullBuyPrimary); + Assert.That(setWeapon, Is.EqualTo(expectedItem)); + } + + [Test] + [Retry(3)] + public void RandomWeaponSelection() + { + Configs.OverrideConfigDataForTests(new ConfigData { RoundTypePercentages = new() { @@ -150,30 +150,29 @@ public void RandomWeaponSelection() {RoundType.FullBuy, 90}, } }); - var numPistol = 0; - var numHalfBuy = 0; - var numFullBuy = 0; - for (var i = 0; i < 1000; i++) + var numPistol = 0; + var numHalfBuy = 0; + var numFullBuy = 0; + for (var i = 0; i < 1000; i++) + { + var randomRoundType = RoundTypeHelpers.GetRandomRoundType(); + switch (randomRoundType) { - var randomRoundType = RoundTypeHelpers.GetRandomRoundType(); - switch (randomRoundType) - { - case RoundType.Pistol: - numPistol++; - break; - case RoundType.HalfBuy: - numHalfBuy++; - break; - case RoundType.FullBuy: - numFullBuy++; - break; - } + case RoundType.Pistol: + numPistol++; + break; + case RoundType.HalfBuy: + numHalfBuy++; + break; + case RoundType.FullBuy: + numFullBuy++; + break; } - - // Ranges are very permissive to avoid flakes - Assert.That(numPistol, Is.InRange(20, 80)); - Assert.That(numHalfBuy, Is.InRange(20, 80)); - Assert.That(numFullBuy, Is.InRange(850, 950)); } + + // Ranges are very permissive to avoid flakes + Assert.That(numPistol, Is.InRange(20, 80)); + Assert.That(numHalfBuy, Is.InRange(20, 80)); + Assert.That(numFullBuy, Is.InRange(850, 950)); } } From 9ce38d8d9da9c3c11ea09938919c17b23fad0d34 Mon Sep 17 00:00:00 2001 From: Yoni Lerner Date: Sun, 14 Jan 2024 01:08:13 -0500 Subject: [PATCH 08/14] sniper -> preferred. also scout is now midrange --- RetakesAllocator/RetakesAllocator.cs | 8 +- RetakesAllocatorCore/Db/Queries.cs | 10 +-- .../OnRoundPostStartHelper.cs | 20 ++--- RetakesAllocatorCore/OnWeaponCommandHelper.cs | 17 ++-- RetakesAllocatorCore/WeaponHelpers.cs | 77 ++++++++++--------- RetakesAllocatorTest/DbTests.cs | 12 +-- 6 files changed, 76 insertions(+), 68 deletions(-) diff --git a/RetakesAllocator/RetakesAllocator.cs b/RetakesAllocator/RetakesAllocator.cs index c25b4d1..9fd527b 100644 --- a/RetakesAllocator/RetakesAllocator.cs +++ b/RetakesAllocator/RetakesAllocator.cs @@ -228,15 +228,15 @@ public HookResult OnPostItemPurchase(EventItemPurchase @event, GameEventInfo inf var team = (CsTeam) player.TeamNum; var playerId = Helpers.GetSteamId(player); var weaponRoundType = WeaponHelpers.GetRoundTypeForWeapon(item); - var isSniper = WeaponHelpers.IsSniper(team, item); + var isPreferred = WeaponHelpers.IsPreferred(team, item); // Log.Write($"item {item} team {team} player {playerId}"); // Log.Write($"curRound {_currentRoundType} weapon Round {weaponRoundType}"); if ( Helpers.IsWeaponAllocationAllowed() && - // Snipers are treated like un-buy-able weapons, but at the end we'll set the user preference - !isSniper && + // Preferred weapons are treated like un-buy-able weapons, but at the end we'll set the user preference + !isPreferred && weaponRoundType is not null && (weaponRoundType == _currentRoundType || weaponRoundType == RoundType.Pistol) ) @@ -328,7 +328,7 @@ p.AbsOrigin is null } } - if (isSniper) + if (isPreferred) { var itemName = Enum.GetName(item); if (itemName is not null) diff --git a/RetakesAllocatorCore/Db/Queries.cs b/RetakesAllocatorCore/Db/Queries.cs index 956929e..81ab0cf 100644 --- a/RetakesAllocatorCore/Db/Queries.cs +++ b/RetakesAllocatorCore/Db/Queries.cs @@ -40,14 +40,14 @@ public static void SetWeaponPreferenceForUser(ulong userId, CsTeam team, WeaponA userSetting => { userSetting.SetWeaponPreference(team, weaponAllocationType, item); }); } - public static void SetSniperPreference(ulong userId, CsItem? sniper) + public static void SetPreferredWeaponPreference(ulong userId, CsItem? item) { UpsertUserSettings(userId, userSetting => { - userSetting.SetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.Sniper, - WeaponHelpers.CoerceSniperTeam(sniper, CsTeam.Terrorist)); - userSetting.SetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.Sniper, - WeaponHelpers.CoerceSniperTeam(sniper, CsTeam.CounterTerrorist)); + userSetting.SetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.Preferred, + WeaponHelpers.CoercePreferredTeam(item, CsTeam.Terrorist)); + userSetting.SetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.Preferred, + WeaponHelpers.CoercePreferredTeam(item, CsTeam.CounterTerrorist)); }); } diff --git a/RetakesAllocatorCore/OnRoundPostStartHelper.cs b/RetakesAllocatorCore/OnRoundPostStartHelper.cs index 23bded0..b2a21b4 100644 --- a/RetakesAllocatorCore/OnRoundPostStartHelper.cs +++ b/RetakesAllocatorCore/OnRoundPostStartHelper.cs @@ -50,18 +50,18 @@ out RoundType currentRoundType var defusingPlayer = Utils.Choice(ctPlayers); - HashSet FilterBySniperPreference(IEnumerable ps) => + HashSet FilterByPreferredWeaponPreference(IEnumerable ps) => ps.Where(p => userSettingsByPlayerId.TryGetValue(getSteamId(p), out var userSetting) && - userSetting.GetWeaponPreference(getTeam(p), WeaponAllocationType.Sniper) is not null) + userSetting.GetWeaponPreference(getTeam(p), WeaponAllocationType.Preferred) is not null) .ToHashSet(); - ICollection tSnipers = new HashSet(); - ICollection ctSnipers = new HashSet(); + ICollection tPreferredPlayers = new HashSet(); + ICollection ctPreferredPlayers = new HashSet(); if (roundType == RoundType.FullBuy) { - tSnipers = WeaponHelpers.SelectSnipers(FilterBySniperPreference(tPlayers)); - ctSnipers = WeaponHelpers.SelectSnipers(FilterBySniperPreference(ctPlayers)); + tPreferredPlayers = WeaponHelpers.SelectPreferredPlayers(FilterByPreferredWeaponPreference(tPlayers)); + ctPreferredPlayers = WeaponHelpers.SelectPreferredPlayers(FilterByPreferredWeaponPreference(ctPlayers)); } foreach (var player in allPlayers) @@ -75,10 +75,10 @@ HashSet FilterBySniperPreference(IEnumerable ps) => team == CsTeam.Terrorist ? CsItem.DefaultKnifeT : CsItem.DefaultKnifeCT, }; - var giveSniper = team switch + var givePreferred = team switch { - CsTeam.Terrorist => tSnipers.Contains(player), - CsTeam.CounterTerrorist => ctSnipers.Contains(player), + CsTeam.Terrorist => tPreferredPlayers.Contains(player), + CsTeam.CounterTerrorist => ctPreferredPlayers.Contains(player), _ => false, }; @@ -87,7 +87,7 @@ HashSet FilterBySniperPreference(IEnumerable ps) => roundType, team, userSetting, - giveSniper + givePreferred ) ); diff --git a/RetakesAllocatorCore/OnWeaponCommandHelper.cs b/RetakesAllocatorCore/OnWeaponCommandHelper.cs index 73cd58d..382cb60 100644 --- a/RetakesAllocatorCore/OnWeaponCommandHelper.cs +++ b/RetakesAllocatorCore/OnWeaponCommandHelper.cs @@ -61,16 +61,16 @@ public class OnWeaponCommandHelper var allocationType = WeaponHelpers.WeaponAllocationTypeForWeaponAndRound( roundType, team, weapon ); - var isSniper = allocationType == WeaponAllocationType.Sniper; + var isPreferred = allocationType == WeaponAllocationType.Preferred; var allocateImmediately = ( // Always true for pistols weaponRoundTypes.Contains(roundType) && // Only set the outWeapon if the user is setting the preference for their current team currentTeam == team && - // TODO Allow immediate allocation of sniper if the config permits it (eg. unlimited snipers) + // TODO Allow immediate allocation of preferred if the config permits it (eg. unlimited preferred) // Could be tricky for max # per team config, since this function doesnt know # of players on the team - !isSniper + !isPreferred ); if (allocationType is null) @@ -81,9 +81,9 @@ public class OnWeaponCommandHelper if (remove) { - if (isSniper) + if (isPreferred) { - Queries.SetSniperPreference(userId, null); + Queries.SetPreferredWeaponPreference(userId, null); return $"You will no longer receive '{weapon}'."; } else @@ -94,9 +94,10 @@ public class OnWeaponCommandHelper } string message; - if (isSniper) + if (isPreferred) { - Queries.SetSniperPreference(userId, weapon); + Queries.SetPreferredWeaponPreference(userId, weapon); + // If we ever add more preferred weapons, we need to change the wording of "sniper" here message = $"You will now get a '{weapon}' when its your turn for a sniper."; } else @@ -109,7 +110,7 @@ public class OnWeaponCommandHelper { outWeapon = weapon; } - else if (!isSniper) + else if (!isPreferred) { message += $" You will get it at the next {weaponRoundTypes.First()} round."; } diff --git a/RetakesAllocatorCore/WeaponHelpers.cs b/RetakesAllocatorCore/WeaponHelpers.cs index f827a26..a2d7e8c 100644 --- a/RetakesAllocatorCore/WeaponHelpers.cs +++ b/RetakesAllocatorCore/WeaponHelpers.cs @@ -12,7 +12,11 @@ public enum WeaponAllocationType HalfBuyPrimary, Secondary, PistolRound, - Sniper, + // eg. AWP is a preferred gun - you cant always get it even if its your preference + // Right now its only snipers, but if we make this configurable, we need to change: + // - CoercePreferredTeam + // - "your turn" wording in the weapon command handler + Preferred, } public static class WeaponHelpers @@ -58,6 +62,9 @@ public static class WeaponHelpers // Shotgun CsItem.XM1014, CsItem.Nova, + + // Sniper + CsItem.Scout, }; private static readonly ICollection _tMidRange = new HashSet @@ -99,28 +106,26 @@ public static class WeaponHelpers CsItem.AUG, }; - private static readonly ICollection _sharedSnipers = new HashSet + private static readonly ICollection _sharedPreferred = new HashSet { - // TODO Make Scout a half buy weapon - CsItem.Scout, CsItem.AWP, }; - private static readonly ICollection _tSnipers = new HashSet + private static readonly ICollection _tPreferred = new HashSet { CsItem.AutoSniperT, }; - private static readonly ICollection _ctSnipers = new HashSet + private static readonly ICollection _ctPreferred = new HashSet { CsItem.AutoSniperCT, }; - private static readonly ICollection _snipersForT = _sharedSnipers.Concat(_tSnipers).ToHashSet(); - private static readonly ICollection _snipersForCt = _sharedSnipers.Concat(_ctSnipers).ToHashSet(); + private static readonly ICollection _preferredForT = _sharedPreferred.Concat(_tPreferred).ToHashSet(); + private static readonly ICollection _preferredForCt = _sharedPreferred.Concat(_ctPreferred).ToHashSet(); - private static readonly ICollection _allSnipers = - _snipersForT.Concat(_snipersForCt).ToHashSet(); + private static readonly ICollection _allPreferred = + _preferredForT.Concat(_preferredForCt).ToHashSet(); private static readonly ICollection _heavys = new HashSet { @@ -128,18 +133,18 @@ public static class WeaponHelpers CsItem.Negev, }; - private static readonly ICollection _fullBuyForT = - _tRifles.Concat(_snipersForT).Concat(_heavys).ToHashSet(); + private static readonly ICollection _fullBuyPrimaryForT = + _tRifles.Concat(_heavys).ToHashSet(); - private static readonly ICollection _fullBuyForCt = - _ctRifles.Concat(_snipersForCt).Concat(_heavys).ToHashSet(); + private static readonly ICollection _fullBuyPrimaryForCt = + _ctRifles.Concat(_heavys).ToHashSet(); private static readonly ICollection _allWeapons = Enum.GetValues() .Where(item => (int) item >= 200 && (int) item < 500) .ToHashSet(); private static readonly ICollection _allFullBuy = - _allSnipers.Concat(_heavys).Concat(_tRifles).Concat(_ctRifles).ToHashSet(); + _allPreferred.Concat(_heavys).Concat(_tRifles).Concat(_ctRifles).ToHashSet(); private static readonly ICollection _allHalfBuy = _midRangeForT.Concat(_midRangeForCt).ToHashSet(); @@ -158,7 +163,7 @@ private static readonly Dictionary> { RoundType.FullBuy, new HashSet - {WeaponAllocationType.Secondary, WeaponAllocationType.FullBuyPrimary, WeaponAllocationType.Sniper} + {WeaponAllocationType.Secondary, WeaponAllocationType.FullBuyPrimary, WeaponAllocationType.Preferred} }, }; @@ -173,8 +178,8 @@ private static readonly Dictionary< {WeaponAllocationType.PistolRound, _pistolsForT}, {WeaponAllocationType.Secondary, _pistolsForT}, {WeaponAllocationType.HalfBuyPrimary, _midRangeForT}, - {WeaponAllocationType.FullBuyPrimary, _fullBuyForT}, - {WeaponAllocationType.Sniper, _snipersForT}, + {WeaponAllocationType.FullBuyPrimary, _fullBuyPrimaryForT}, + {WeaponAllocationType.Preferred, _preferredForT}, } }, { @@ -183,8 +188,8 @@ private static readonly Dictionary< {WeaponAllocationType.PistolRound, _pistolsForCt}, {WeaponAllocationType.Secondary, _pistolsForCt}, {WeaponAllocationType.HalfBuyPrimary, _midRangeForCt}, - {WeaponAllocationType.FullBuyPrimary, _fullBuyForCt}, - {WeaponAllocationType.Sniper, _snipersForCt}, + {WeaponAllocationType.FullBuyPrimary, _fullBuyPrimaryForCt}, + {WeaponAllocationType.Preferred, _preferredForCt}, } } }; @@ -201,7 +206,7 @@ private static readonly Dictionary< {WeaponAllocationType.HalfBuyPrimary, CsItem.Mac10}, {WeaponAllocationType.Secondary, CsItem.Deagle}, {WeaponAllocationType.PistolRound, CsItem.Glock}, - {WeaponAllocationType.Sniper, CsItem.AWP}, + {WeaponAllocationType.Preferred, CsItem.AWP}, } }, { @@ -211,7 +216,7 @@ private static readonly Dictionary< {WeaponAllocationType.HalfBuyPrimary, CsItem.MP9}, {WeaponAllocationType.Secondary, CsItem.Deagle}, {WeaponAllocationType.PistolRound, CsItem.USPS}, - {WeaponAllocationType.Sniper, CsItem.AWP}, + {WeaponAllocationType.Preferred, CsItem.AWP}, } } }; @@ -285,18 +290,18 @@ public static bool IsAllocationTypeValidForRound(WeaponAllocationType allocation return null; } - public static bool IsSniper(CsTeam team, CsItem weapon) + public static bool IsPreferred(CsTeam team, CsItem weapon) { return team switch { - CsTeam.Terrorist => _snipersForT.Contains(weapon), - CsTeam.CounterTerrorist => _snipersForCt.Contains(weapon), + CsTeam.Terrorist => _preferredForT.Contains(weapon), + CsTeam.CounterTerrorist => _preferredForCt.Contains(weapon), _ => false, }; } - // TODO In the future this will be more complex based on sniper config - public static ICollection SelectSnipers(ICollection players) + // TODO In the future this will be more complex based on sniper/preferred config and VIP status + public static ICollection SelectPreferredPlayers(ICollection players) { var player = Utils.Choice(players); if (player is null) @@ -312,9 +317,9 @@ public static bool IsUsableWeapon(CsItem weapon) return Configs.GetConfigData().UsableWeapons.Contains(weapon); } - public static CsItem? CoerceSniperTeam(CsItem? sniper, CsTeam team) + public static CsItem? CoercePreferredTeam(CsItem? item, CsTeam team) { - if (sniper == null || !_allSnipers.Contains(sniper.Value)) + if (item == null || !_allPreferred.Contains(item.Value)) { return null; } @@ -324,11 +329,13 @@ public static bool IsUsableWeapon(CsItem weapon) return null; } - if (sniper is CsItem.AWP or CsItem.Scout) + if (item == CsItem.AWP) { - return sniper; + return item; } + // Right now these are the only other preferred guns + // If we make preferred guns configurable, we'll have to change this return team == CsTeam.Terrorist ? CsItem.AutoSniperT : CsItem.AutoSniperCT; } @@ -408,12 +415,12 @@ public static ICollection GetWeaponsForRoundType( RoundType roundType, CsTeam team, UserSetting? userSetting, - bool giveSniper + bool givePreferred ) { WeaponAllocationType? primaryWeaponAllocation = - giveSniper - ? WeaponAllocationType.Sniper + givePreferred + ? WeaponAllocationType.Preferred : roundType switch { RoundType.HalfBuy => WeaponAllocationType.HalfBuyPrimary, @@ -480,7 +487,7 @@ private static CsItem GetRandomWeaponForAllocationType(WeaponAllocationType allo WeaponAllocationType.Secondary => team == CsTeam.Terrorist ? _pistolsForT : _pistolsForCt, WeaponAllocationType.HalfBuyPrimary => team == CsTeam.Terrorist ? _smgsForT : _smgsForCt, WeaponAllocationType.FullBuyPrimary => team == CsTeam.Terrorist ? _tRifles : _ctRifles, - WeaponAllocationType.Sniper => team == CsTeam.Terrorist ? _snipersForT : _snipersForCt, + WeaponAllocationType.Preferred => team == CsTeam.Terrorist ? _preferredForT : _preferredForCt, _ => _sharedPistols, }; return Utils.Choice(collectionToCheck.Where(IsUsableWeapon).ToList()); diff --git a/RetakesAllocatorTest/DbTests.cs b/RetakesAllocatorTest/DbTests.cs index 17648b2..2be8bb9 100644 --- a/RetakesAllocatorTest/DbTests.cs +++ b/RetakesAllocatorTest/DbTests.cs @@ -18,13 +18,13 @@ public void TestGetUsersSettings() Queries.SetWeaponPreferenceForUser(1, CsTeam.Terrorist, WeaponAllocationType.HalfBuyPrimary, CsItem.MP5); Queries.SetWeaponPreferenceForUser(1, CsTeam.CounterTerrorist, WeaponAllocationType.FullBuyPrimary, CsItem.AK47); // Should set for both T and CT - Queries.SetSniperPreference(1, CsItem.AWP); + Queries.SetPreferredWeaponPreference(1, CsItem.AWP); Queries.SetWeaponPreferenceForUser(2, CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary, CsItem.AK47); Queries.SetWeaponPreferenceForUser(2, CsTeam.Terrorist, WeaponAllocationType.Secondary, CsItem.Deagle); Queries.SetWeaponPreferenceForUser(2, CsTeam.CounterTerrorist, WeaponAllocationType.Secondary, CsItem.FiveSeven); // Will get different snipers for different teams - Queries.SetSniperPreference(2, CsItem.SCAR20); + Queries.SetPreferredWeaponPreference(2, CsItem.SCAR20); usersSettings = Queries.GetUsersSettings(new List {1}); Assert.Multiple(() => @@ -52,9 +52,9 @@ public void TestGetUsersSettings() Is.EqualTo(CsItem.AK47)); Assert.That(usersSettings[1].GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.HalfBuyPrimary), Is.EqualTo(null)); - Assert.That(usersSettings[1].GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.Sniper), + Assert.That(usersSettings[1].GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.Preferred), Is.EqualTo(CsItem.AWP)); - Assert.That(usersSettings[1].GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.Sniper), + Assert.That(usersSettings[1].GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.Preferred), Is.EqualTo(CsItem.AWP)); Assert.That(usersSettings[2].GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.FullBuyPrimary), @@ -63,9 +63,9 @@ public void TestGetUsersSettings() Is.EqualTo(CsItem.Deagle)); Assert.That(usersSettings[2].GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.Secondary), Is.EqualTo(CsItem.FiveSeven)); - Assert.That(usersSettings[2].GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.Sniper), + Assert.That(usersSettings[2].GetWeaponPreference(CsTeam.Terrorist, WeaponAllocationType.Preferred), Is.EqualTo(CsItem.AutoSniperT)); - Assert.That(usersSettings[2].GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.Sniper), + Assert.That(usersSettings[2].GetWeaponPreference(CsTeam.CounterTerrorist, WeaponAllocationType.Preferred), Is.EqualTo(CsItem.AutoSniperCT)); }); From 93deb73079105e50a934f129ab237d71b4620b1d Mon Sep 17 00:00:00 2001 From: Yoni Lerner Date: Sun, 14 Jan 2024 01:42:36 -0500 Subject: [PATCH 09/14] it builds --- RetakesAllocator/Menus/WeaponsMenu.cs | 9 ++-- RetakesAllocator/RetakesAllocator.cs | 53 ++++++++++++------- RetakesAllocatorCore/OnWeaponCommandHelper.cs | 5 +- RetakesAllocatorCore/WeaponHelpers.cs | 37 ++++--------- RetakesAllocatorTest/WeaponSelectionTests.cs | 5 +- 5 files changed, 58 insertions(+), 51 deletions(-) diff --git a/RetakesAllocator/Menus/WeaponsMenu.cs b/RetakesAllocator/Menus/WeaponsMenu.cs index 4e23b8c..08d14ce 100644 --- a/RetakesAllocator/Menus/WeaponsMenu.cs +++ b/RetakesAllocator/Menus/WeaponsMenu.cs @@ -62,7 +62,7 @@ private void OpenTPrimaryMenu(CCSPlayerController player) { var menu = new ChatMenu($"{MessagePrefix} Select a T Primary Weapon"); - foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForAllocationType(RoundType.FullBuy, CsTeam.Terrorist)) + foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForAllocationType(WeaponAllocationType.FullBuyPrimary, CsTeam.Terrorist)) { menu.AddMenuOption(weapon.ToString(), OnTPrimarySelect); } @@ -92,7 +92,7 @@ private void OpenTSecondaryMenu(CCSPlayerController player) { var menu = new ChatMenu($"{MessagePrefix} Select a T Secondary Weapon"); - foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForAllocationType(RoundType.Pistol, CsTeam.Terrorist)) + foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForAllocationType(WeaponAllocationType.Secondary, CsTeam.Terrorist)) { menu.AddMenuOption(weapon.ToString(), OnTSecondarySelect); } @@ -123,7 +123,7 @@ private void OpenCtPrimaryMenu(CCSPlayerController player) { var menu = new ChatMenu($"{MessagePrefix} Select a CT Primary Weapon"); - foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForAllocationType(RoundType.FullBuy, CsTeam.CounterTerrorist)) + foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForAllocationType(WeaponAllocationType.FullBuyPrimary, CsTeam.CounterTerrorist)) { menu.AddMenuOption(weapon.ToString(), OnCTPrimarySelect); } @@ -153,7 +153,7 @@ private void OpenCtSecondaryMenu(CCSPlayerController player) { var menu = new ChatMenu($"{MessagePrefix} Select a CT Secondary Weapon"); - foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForAllocationType(RoundType.Pistol, CsTeam.Terrorist)) + foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForAllocationType(WeaponAllocationType.Secondary, CsTeam.Terrorist)) { menu.AddMenuOption(weapon.ToString(), OnCTSecondarySelect); } @@ -227,6 +227,7 @@ private static void HandlePreferenceSelection(CCSPlayerController player, CsTeam OnWeaponCommandHelper.Handle( new List{weapon}, player.AuthorizedSteamID?.SteamId64 ?? 0, + null, team, false, out _ diff --git a/RetakesAllocator/RetakesAllocator.cs b/RetakesAllocator/RetakesAllocator.cs index 9fd527b..c36d25d 100644 --- a/RetakesAllocator/RetakesAllocator.cs +++ b/RetakesAllocator/RetakesAllocator.cs @@ -136,25 +136,32 @@ private void HandleWeaponCommand(CCSPlayerController? player, CommandInfo comman var result = OnWeaponCommandHelper.Handle( Helpers.CommandInfoToArgList(commandInfo), playerId, + _currentRoundType, currentTeam, false, out var selectedWeapon ); - if (result is not null) - { - commandInfo.ReplyToCommand($"{MessagePrefix}{result}"); - } + commandInfo.ReplyToCommand($"{MessagePrefix}{result}"); if (Helpers.IsWeaponAllocationAllowed() && selectedWeapon is not null) { - var selectedWeaponRoundType = WeaponHelpers.GetRoundTypeForWeapon(selectedWeapon.Value); - if (selectedWeaponRoundType == RoundType.Pistol || selectedWeaponRoundType == _currentRoundType) + var selectedWeaponAllocationType = WeaponHelpers.GetWeaponAllocationTypeForWeapon(selectedWeapon.Value, _currentRoundType); + if (selectedWeaponAllocationType is not null) { Helpers.RemoveWeapons( player, - item => WeaponHelpers.GetRoundTypeForWeapon(item) == selectedWeaponRoundType + item => WeaponHelpers.GetWeaponAllocationTypeForWeapon(item, _currentRoundType) == + selectedWeaponAllocationType ); - var slot = selectedWeaponRoundType == RoundType.Pistol ? "slot2" : "slot1"; + var slot = selectedWeaponAllocationType.Value switch + { + WeaponAllocationType.FullBuyPrimary => "slot1", + WeaponAllocationType.HalfBuyPrimary => "slot1", + WeaponAllocationType.Secondary => "slot2", + WeaponAllocationType.PistolRound => "slot2", + WeaponAllocationType.Preferred => "slot1", + _ => throw new ArgumentOutOfRangeException() + }; AllocateItemsForPlayer(player, new List {selectedWeapon.Value}, slot); } } @@ -175,14 +182,12 @@ public void OnRemoveWeaponCommand(CCSPlayerController? player, CommandInfo comma var result = OnWeaponCommandHelper.Handle( Helpers.CommandInfoToArgList(commandInfo), playerId, + _currentRoundType, currentTeam, true, out _ ); - if (result is not null) - { - commandInfo.ReplyToCommand($"{MessagePrefix}{result}"); - } + commandInfo.ReplyToCommand($"{MessagePrefix}{result}"); } [ConsoleCommand("css_nextround", "Sets the next round type.")] @@ -227,8 +232,13 @@ public HookResult OnPostItemPurchase(EventItemPurchase @event, GameEventInfo inf var item = Utils.ToEnum(@event.Weapon); var team = (CsTeam) player.TeamNum; var playerId = Helpers.GetSteamId(player); - var weaponRoundType = WeaponHelpers.GetRoundTypeForWeapon(item); var isPreferred = WeaponHelpers.IsPreferred(team, item); + + var purchasedAllocationType = _currentRoundType is not null ? WeaponHelpers.WeaponAllocationTypeForWeaponAndRound( + _currentRoundType.Value, team, item + ) : null; + + var isValidAllocation = WeaponHelpers.IsAllocationTypeValidForRound(purchasedAllocationType, _currentRoundType); // Log.Write($"item {item} team {team} player {playerId}"); // Log.Write($"curRound {_currentRoundType} weapon Round {weaponRoundType}"); @@ -237,14 +247,15 @@ public HookResult OnPostItemPurchase(EventItemPurchase @event, GameEventInfo inf Helpers.IsWeaponAllocationAllowed() && // Preferred weapons are treated like un-buy-able weapons, but at the end we'll set the user preference !isPreferred && - weaponRoundType is not null && - (weaponRoundType == _currentRoundType || weaponRoundType == RoundType.Pistol) + isValidAllocation && + // redundant, just for null checker + purchasedAllocationType is not null ) { Queries.SetWeaponPreferenceForUser( playerId, team, - weaponRoundType.Value, + purchasedAllocationType.Value, item ); } @@ -258,8 +269,13 @@ weaponRoundType is not null && return i == item; } - // Some weapons identify as other weapons, so we just remove them all - return WeaponHelpers.GetRoundTypeForWeapon(i) == weaponRoundType; + if (_currentRoundType is null) + { + return true; + } + + var at = WeaponHelpers.GetWeaponAllocationTypeForWeapon(i, _currentRoundType.Value); + return at is null || at == purchasedAllocationType; }); // Log.Write($"Removed {item}? {removedAnyWeapons}"); @@ -336,6 +352,7 @@ p.AbsOrigin is null var message = OnWeaponCommandHelper.Handle( new List {itemName}, Helpers.GetSteamId(player), + _currentRoundType, team, false, out _ diff --git a/RetakesAllocatorCore/OnWeaponCommandHelper.cs b/RetakesAllocatorCore/OnWeaponCommandHelper.cs index 382cb60..4ff8234 100644 --- a/RetakesAllocatorCore/OnWeaponCommandHelper.cs +++ b/RetakesAllocatorCore/OnWeaponCommandHelper.cs @@ -7,7 +7,7 @@ namespace RetakesAllocatorCore; public class OnWeaponCommandHelper { - public static string? Handle(ICollection args, ulong userId, RoundType roundType, CsTeam currentTeam, + public static string Handle(ICollection args, ulong userId, RoundType? roundType, CsTeam currentTeam, bool remove, out CsItem? outWeapon) { outWeapon = null; @@ -65,7 +65,8 @@ public class OnWeaponCommandHelper var allocateImmediately = ( // Always true for pistols - weaponRoundTypes.Contains(roundType) && + roundType is not null && + weaponRoundTypes.Contains(roundType.Value) && // Only set the outWeapon if the user is setting the preference for their current team currentTeam == team && // TODO Allow immediate allocation of preferred if the config permits it (eg. unlimited preferred) diff --git a/RetakesAllocatorCore/WeaponHelpers.cs b/RetakesAllocatorCore/WeaponHelpers.cs index a2d7e8c..e201430 100644 --- a/RetakesAllocatorCore/WeaponHelpers.cs +++ b/RetakesAllocatorCore/WeaponHelpers.cs @@ -243,12 +243,16 @@ public static ICollection GetPossibleWeaponsForAllocationType(WeaponAllo return _validWeaponsByTeamAndAllocationType[team][allocationType].Where(IsUsableWeapon).ToList(); } - public static bool IsAllocationTypeValidForRound(WeaponAllocationType allocationType, RoundType roundType) + public static bool IsAllocationTypeValidForRound(WeaponAllocationType? allocationType, RoundType? roundType) { - return _validAllocationTypesForRound[roundType].Contains(allocationType); + if (allocationType is null || roundType is null) + { + return false; + } + return _validAllocationTypesForRound[roundType.Value].Contains(allocationType.Value); } - public static WeaponAllocationType? WeaponAllocationTypeForWeaponAndRound(RoundType roundType, CsTeam team, + public static WeaponAllocationType? WeaponAllocationTypeForWeaponAndRound(RoundType? roundType, CsTeam team, CsItem weapon) { if (team != CsTeam.Terrorist && team != CsTeam.CounterTerrorist) @@ -359,27 +363,6 @@ public static ICollection GetRoundTypesForWeapon(CsItem weapon) return new HashSet(); } - // TODO Change all usages of this - public static RoundType? GetRoundTypeForWeapon(CsItem weapon) - { - if (_allFullBuy.Contains(weapon)) - { - return RoundType.FullBuy; - } - - if (_allHalfBuy.Contains(weapon)) - { - return RoundType.HalfBuy; - } - - if (_allPistols.Contains(weapon)) - { - return RoundType.Pistol; - } - - return null; - } - public static ICollection FindValidWeaponsByName(string needle) { return FindItemsByName(needle) @@ -387,14 +370,16 @@ public static ICollection FindValidWeaponsByName(string needle) .ToList(); } - public static WeaponAllocationType? GetWeaponAllocationTypeForWeapon(CsItem weapon, RoundType roundType) + public static WeaponAllocationType? GetWeaponAllocationTypeForWeapon(CsItem weapon, RoundType? roundType) { if (_allPistols.Contains(weapon)) { return roundType switch { RoundType.Pistol => WeaponAllocationType.PistolRound, - _ => WeaponAllocationType.Secondary, + RoundType.HalfBuy => WeaponAllocationType.Secondary, + RoundType.FullBuy => WeaponAllocationType.Secondary, + _ => null, }; } diff --git a/RetakesAllocatorTest/WeaponSelectionTests.cs b/RetakesAllocatorTest/WeaponSelectionTests.cs index d35c3aa..b98f2eb 100644 --- a/RetakesAllocatorTest/WeaponSelectionTests.cs +++ b/RetakesAllocatorTest/WeaponSelectionTests.cs @@ -63,8 +63,11 @@ public void SetWeaponPreferenceDirectly() [TestCase(RoundType.FullBuy, CsTeam.CounterTerrorist, "galil,CT", null, "Galil' is not valid", null)] [TestCase(RoundType.Pistol, CsTeam.CounterTerrorist, "tec9,CT", null, "Tec9' is not valid", null)] [TestCase(RoundType.FullBuy, CsTeam.Terrorist, "poop,T", null, "not found", null)] + [TestCase(null, CsTeam.Terrorist, "ak", null, "AK47' is now", "AK47' is no longer")] + [TestCase(RoundType.FullBuy, CsTeam.Spectator, "ak", null, "must join a team", "must join a team")] + [TestCase(RoundType.FullBuy, CsTeam.Terrorist, "ak,F", null, "Invalid team", "Invalid team")] public void SetWeaponPreferenceCommandSingleArg( - RoundType roundType, + RoundType? roundType, CsTeam team, string strArgs, CsItem? expectedItem, From a353306bf677b1d39fa5875630bec280eb5b1dd0 Mon Sep 17 00:00:00 2001 From: B3none Date: Sun, 14 Jan 2024 22:05:17 +0000 Subject: [PATCH 10/14] Added try catch for UserSetting.cs --- RetakesAllocatorCore/Db/UserSetting.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/RetakesAllocatorCore/Db/UserSetting.cs b/RetakesAllocatorCore/Db/UserSetting.cs index 732ed01..a352671 100644 --- a/RetakesAllocatorCore/Db/UserSetting.cs +++ b/RetakesAllocatorCore/Db/UserSetting.cs @@ -110,7 +110,14 @@ public static string WeaponPreferenceSerialize(WeaponPreferencesType? value) public static WeaponPreferencesType WeaponPreferenceDeserialize(string value) { - var parseResult = JsonSerializer.Deserialize(value); + WeaponPreferencesType? parseResult = null; + try { + parseResult = JsonSerializer.Deserialize(value); + } catch (Exception e) + { + Log.Write($"Failed to deserialize weapon preferences: {e.Message}"); + } + return parseResult ?? new WeaponPreferencesType(); } } From 6e3133100a51ba0509bcca1f3856ee9eef238ee4 Mon Sep 17 00:00:00 2001 From: B3none Date: Sun, 14 Jan 2024 22:14:27 +0000 Subject: [PATCH 11/14] Removed redundant TODOs, added useful TODO --- RetakesAllocator/Menus/WeaponsMenu.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RetakesAllocator/Menus/WeaponsMenu.cs b/RetakesAllocator/Menus/WeaponsMenu.cs index 08d14ce..4040c02 100644 --- a/RetakesAllocator/Menus/WeaponsMenu.cs +++ b/RetakesAllocator/Menus/WeaponsMenu.cs @@ -112,7 +112,6 @@ private void OnTSecondarySelect(CCSPlayerController player, ChatMenuOption optio var weaponName = option.Text; - // TODO: Separate allocation for CT pistol and T pistol player.PrintToChat($"{MessagePrefix} You selected {weaponName} as T Secondary!"); HandlePreferenceSelection(player, CsTeam.Terrorist, weaponName); @@ -173,13 +172,14 @@ private void OnCTSecondarySelect(CCSPlayerController player, ChatMenuOption opti var weaponName = option.Text; - // TODO: Separate allocation for CT pistol and T pistol player.PrintToChat($"{MessagePrefix} You selected {weaponName} as CT Secondary!"); HandlePreferenceSelection(player, CsTeam.CounterTerrorist, weaponName); // OpenGiveAwpMenu(player); OnMenuComplete(player); } + + // TODO: Add menu to select pistol round weapon // private void OpenGiveAwpMenu(CCSPlayerController player) // { From 962ca7975811c35f02ff25ef77e1faa8595f988a Mon Sep 17 00:00:00 2001 From: Yoni Lerner Date: Sun, 14 Jan 2024 20:32:41 -0500 Subject: [PATCH 12/14] fixes --- RetakesAllocator/RetakesAllocator.cs | 28 +++--- RetakesAllocatorCore/OnWeaponCommandHelper.cs | 6 +- RetakesAllocatorCore/WeaponHelpers.cs | 99 +++++++++---------- RetakesAllocatorTest/WeaponSelectionTests.cs | 2 +- 4 files changed, 65 insertions(+), 70 deletions(-) diff --git a/RetakesAllocator/RetakesAllocator.cs b/RetakesAllocator/RetakesAllocator.cs index c36d25d..23c305b 100644 --- a/RetakesAllocator/RetakesAllocator.cs +++ b/RetakesAllocator/RetakesAllocator.cs @@ -42,7 +42,6 @@ public override void Load(bool hotReload) Queries.Migrate(); } - if (hotReload) { HandleHotReload(); @@ -145,12 +144,13 @@ out var selectedWeapon if (Helpers.IsWeaponAllocationAllowed() && selectedWeapon is not null) { - var selectedWeaponAllocationType = WeaponHelpers.GetWeaponAllocationTypeForWeapon(selectedWeapon.Value, _currentRoundType); + var selectedWeaponAllocationType = + WeaponHelpers.GetWeaponAllocationTypeForWeaponAndRound(_currentRoundType, currentTeam, selectedWeapon.Value); if (selectedWeaponAllocationType is not null) { Helpers.RemoveWeapons( player, - item => WeaponHelpers.GetWeaponAllocationTypeForWeapon(item, _currentRoundType) == + item => WeaponHelpers.GetWeaponAllocationTypeForWeaponAndRound(_currentRoundType, currentTeam, item) == selectedWeaponAllocationType ); var slot = selectedWeaponAllocationType.Value switch @@ -233,15 +233,18 @@ public HookResult OnPostItemPurchase(EventItemPurchase @event, GameEventInfo inf var team = (CsTeam) player.TeamNum; var playerId = Helpers.GetSteamId(player); var isPreferred = WeaponHelpers.IsPreferred(team, item); - - var purchasedAllocationType = _currentRoundType is not null ? WeaponHelpers.WeaponAllocationTypeForWeaponAndRound( - _currentRoundType.Value, team, item - ) : null; + + var purchasedAllocationType = _currentRoundType is not null + ? WeaponHelpers.GetWeaponAllocationTypeForWeaponAndRound( + _currentRoundType.Value, team, item + ) + : null; var isValidAllocation = WeaponHelpers.IsAllocationTypeValidForRound(purchasedAllocationType, _currentRoundType); // Log.Write($"item {item} team {team} player {playerId}"); - // Log.Write($"curRound {_currentRoundType} weapon Round {weaponRoundType}"); + // Log.Write($"curRound {_currentRoundType} weapon alloc {purchasedAllocationType} valid? {isValidAllocation}"); + // Log.Write($"Preferred? {isPreferred}"); if ( Helpers.IsWeaponAllocationAllowed() && @@ -274,17 +277,19 @@ purchasedAllocationType is not null return true; } - var at = WeaponHelpers.GetWeaponAllocationTypeForWeapon(i, _currentRoundType.Value); + var at = WeaponHelpers.GetWeaponAllocationTypeForWeaponAndRound(_currentRoundType.Value, team, i); + // Log.Write($"at: {at}"); return at is null || at == purchasedAllocationType; }); - // Log.Write($"Removed {item}? {removedAnyWeapons}"); + Log.Write($"Removed {item}? {removedAnyWeapons}"); var replacedWeapon = false; var slotToSelect = _currentRoundType == RoundType.Pistol ? "slot2" : "slot1"; if (removedAnyWeapons && _currentRoundType is not null && WeaponHelpers.IsWeapon(item)) { var replacementAllocationType = - WeaponHelpers.GetWeaponAllocationTypeForWeapon(item, _currentRoundType.Value); + WeaponHelpers.GetReplacementWeaponAllocationTypeForWeapon(_currentRoundType.Value); + // Log.Write($"Replacement allocation type {replacementAllocationType}"); if (replacementAllocationType is not null) { var replacementItem = WeaponHelpers.GetWeaponForAllocationType(replacementAllocationType.Value, @@ -338,6 +343,7 @@ p.AbsOrigin is null { if (p.IsValid && !p.OwnerEntity.IsValid) { + // Log.Write($"Removing {p.DesignerName}"); p.Remove(); } }); diff --git a/RetakesAllocatorCore/OnWeaponCommandHelper.cs b/RetakesAllocatorCore/OnWeaponCommandHelper.cs index 4ff8234..64ead75 100644 --- a/RetakesAllocatorCore/OnWeaponCommandHelper.cs +++ b/RetakesAllocatorCore/OnWeaponCommandHelper.cs @@ -58,7 +58,7 @@ public static string Handle(ICollection args, ulong userId, RoundType? r return $"Invalid weapon '{weapon}'"; } - var allocationType = WeaponHelpers.WeaponAllocationTypeForWeaponAndRound( + var allocationType = WeaponHelpers.GetWeaponAllocationTypeForWeaponAndRound( roundType, team, weapon ); var isPreferred = allocationType == WeaponAllocationType.Preferred; @@ -90,7 +90,7 @@ roundType is not null && else { Queries.SetWeaponPreferenceForUser(userId, team, allocationType.Value, null); - return $"Weapon '{weapon}' is no longer your {roundType} preference for {team}."; + return $"Weapon '{weapon}' is no longer your {allocationType.Value} preference for {team}."; } } @@ -104,7 +104,7 @@ roundType is not null && else { Queries.SetWeaponPreferenceForUser(userId, team, allocationType.Value, weapon); - message = $"Weapon '{weapon}' is now your {roundType} preference for {team}."; + message = $"Weapon '{weapon}' is now your {allocationType.Value} preference for {team}."; } if (allocateImmediately) diff --git a/RetakesAllocatorCore/WeaponHelpers.cs b/RetakesAllocatorCore/WeaponHelpers.cs index e201430..0eb127c 100644 --- a/RetakesAllocatorCore/WeaponHelpers.cs +++ b/RetakesAllocatorCore/WeaponHelpers.cs @@ -252,48 +252,6 @@ public static bool IsAllocationTypeValidForRound(WeaponAllocationType? allocatio return _validAllocationTypesForRound[roundType.Value].Contains(allocationType.Value); } - public static WeaponAllocationType? WeaponAllocationTypeForWeaponAndRound(RoundType? roundType, CsTeam team, - CsItem weapon) - { - if (team != CsTeam.Terrorist && team != CsTeam.CounterTerrorist) - { - return null; - } - - // First populate all allocation types that could match - // For a pistol this could be multiple allocation types, for any other weapon type only one can match - var potentialAllocationTypes = new HashSet(); - foreach (var (allocationType, items) in _validWeaponsByTeamAndAllocationType[team]) - { - if (items.Contains(weapon)) - { - potentialAllocationTypes.Add(allocationType); - } - } - - // If theres only 1 to choose from, return that, or return null if there are none - if (potentialAllocationTypes.Count == 1) - { - return potentialAllocationTypes.First(); - } - if (potentialAllocationTypes.Count == 0) - { - return null; - } - - // For a pistol, the set will be {PistolRound, Secondary} - // We need to find which of those matches the current round type - foreach (var allocationType in potentialAllocationTypes) - { - if (IsAllocationTypeValidForRound(allocationType, roundType)) - { - return allocationType; - } - } - - return null; - } - public static bool IsPreferred(CsTeam team, CsItem weapon) { return team switch @@ -369,33 +327,64 @@ public static ICollection FindValidWeaponsByName(string needle) .Where(item => _allWeapons.Contains(item)) .ToList(); } - - public static WeaponAllocationType? GetWeaponAllocationTypeForWeapon(CsItem weapon, RoundType? roundType) + + public static WeaponAllocationType? GetWeaponAllocationTypeForWeaponAndRound(RoundType? roundType, CsTeam team, + CsItem weapon) { - if (_allPistols.Contains(weapon)) + if (team != CsTeam.Terrorist && team != CsTeam.CounterTerrorist) { - return roundType switch + return null; + } + + // First populate all allocation types that could match + // For a pistol this could be multiple allocation types, for any other weapon type only one can match + var potentialAllocationTypes = new HashSet(); + foreach (var (allocationType, items) in _validWeaponsByTeamAndAllocationType[team]) + { + if (items.Contains(weapon)) { - RoundType.Pistol => WeaponAllocationType.PistolRound, - RoundType.HalfBuy => WeaponAllocationType.Secondary, - RoundType.FullBuy => WeaponAllocationType.Secondary, - _ => null, - }; + potentialAllocationTypes.Add(allocationType); + } } - if (_allHalfBuy.Contains(weapon) && roundType == RoundType.HalfBuy) + // If theres only 1 to choose from, return that, or return null if there are none + if (potentialAllocationTypes.Count == 1) + { + return potentialAllocationTypes.First(); + } + if (potentialAllocationTypes.Count == 0) { - return WeaponAllocationType.HalfBuyPrimary; + return null; } - if (_allFullBuy.Contains(weapon) && roundType == RoundType.FullBuy) + // For a pistol, the set will be {PistolRound, Secondary} + // We need to find which of those matches the current round type + foreach (var allocationType in potentialAllocationTypes) { - return WeaponAllocationType.FullBuyPrimary; + if (IsAllocationTypeValidForRound(allocationType, roundType)) + { + return allocationType; + } } return null; } + /** + * This function should only be used when you have an item that you want to find out what *replacement* + * allocation type it belongs to. Eg. if you have a Preferred, it should be replaced with a PrimaryFullBuy + */ + public static WeaponAllocationType? GetReplacementWeaponAllocationTypeForWeapon(RoundType? roundType) + { + return roundType switch + { + RoundType.Pistol => WeaponAllocationType.PistolRound, + RoundType.HalfBuy => WeaponAllocationType.HalfBuyPrimary, + RoundType.FullBuy => WeaponAllocationType.FullBuyPrimary, + _ => null, + }; + } + public static ICollection GetWeaponsForRoundType( RoundType roundType, CsTeam team, diff --git a/RetakesAllocatorTest/WeaponSelectionTests.cs b/RetakesAllocatorTest/WeaponSelectionTests.cs index b98f2eb..2764cf4 100644 --- a/RetakesAllocatorTest/WeaponSelectionTests.cs +++ b/RetakesAllocatorTest/WeaponSelectionTests.cs @@ -89,7 +89,7 @@ public void SetWeaponPreferenceCommandSingleArg( var allocationType = selectedItem is not null - ? WeaponHelpers.GetWeaponAllocationTypeForWeapon(selectedItem.Value, roundType) + ? WeaponHelpers.GetWeaponAllocationTypeForWeaponAndRound(roundType, team, selectedItem.Value) : null; var setWeapon = allocationType is not null From 4bd47d0385d71133a99fbe1a7cc88247d6ae8f1e Mon Sep 17 00:00:00 2001 From: Yoni Lerner Date: Sun, 14 Jan 2024 20:33:25 -0500 Subject: [PATCH 13/14] fix ct secondary menu --- RetakesAllocator/Menus/WeaponsMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RetakesAllocator/Menus/WeaponsMenu.cs b/RetakesAllocator/Menus/WeaponsMenu.cs index 4040c02..95057e7 100644 --- a/RetakesAllocator/Menus/WeaponsMenu.cs +++ b/RetakesAllocator/Menus/WeaponsMenu.cs @@ -152,7 +152,7 @@ private void OpenCtSecondaryMenu(CCSPlayerController player) { var menu = new ChatMenu($"{MessagePrefix} Select a CT Secondary Weapon"); - foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForAllocationType(WeaponAllocationType.Secondary, CsTeam.Terrorist)) + foreach (var weapon in WeaponHelpers.GetPossibleWeaponsForAllocationType(WeaponAllocationType.Secondary, CsTeam.CounterTerrorist)) { menu.AddMenuOption(weapon.ToString(), OnCTSecondarySelect); } From df8bb1f695855190128cfaa44de6cef141457c6a Mon Sep 17 00:00:00 2001 From: Yoni Lerner Date: Sun, 14 Jan 2024 20:40:19 -0500 Subject: [PATCH 14/14] menu fixes --- RetakesAllocator/Menus/WeaponsMenu.cs | 3 ++- RetakesAllocatorCore/OnWeaponCommandHelper.cs | 1 + RetakesAllocatorCore/WeaponHelpers.cs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/RetakesAllocator/Menus/WeaponsMenu.cs b/RetakesAllocator/Menus/WeaponsMenu.cs index 95057e7..487c409 100644 --- a/RetakesAllocator/Menus/WeaponsMenu.cs +++ b/RetakesAllocator/Menus/WeaponsMenu.cs @@ -224,7 +224,7 @@ private void OnCTSecondarySelect(CCSPlayerController player, ChatMenuOption opti private static void HandlePreferenceSelection(CCSPlayerController player, CsTeam team, string weapon) { - OnWeaponCommandHelper.Handle( + var message = OnWeaponCommandHelper.Handle( new List{weapon}, player.AuthorizedSteamID?.SteamId64 ?? 0, null, @@ -232,5 +232,6 @@ private static void HandlePreferenceSelection(CCSPlayerController player, CsTeam false, out _ ); + // Log.Write(message); } } diff --git a/RetakesAllocatorCore/OnWeaponCommandHelper.cs b/RetakesAllocatorCore/OnWeaponCommandHelper.cs index 64ead75..4cd7e41 100644 --- a/RetakesAllocatorCore/OnWeaponCommandHelper.cs +++ b/RetakesAllocatorCore/OnWeaponCommandHelper.cs @@ -65,6 +65,7 @@ public static string Handle(ICollection args, ulong userId, RoundType? r var allocateImmediately = ( // Always true for pistols + allocationType is not null && roundType is not null && weaponRoundTypes.Contains(roundType.Value) && // Only set the outWeapon if the user is setting the preference for their current team diff --git a/RetakesAllocatorCore/WeaponHelpers.cs b/RetakesAllocatorCore/WeaponHelpers.cs index 0eb127c..3766411 100644 --- a/RetakesAllocatorCore/WeaponHelpers.cs +++ b/RetakesAllocatorCore/WeaponHelpers.cs @@ -361,7 +361,7 @@ public static ICollection FindValidWeaponsByName(string needle) // We need to find which of those matches the current round type foreach (var allocationType in potentialAllocationTypes) { - if (IsAllocationTypeValidForRound(allocationType, roundType)) + if (roundType is null || IsAllocationTypeValidForRound(allocationType, roundType)) { return allocationType; }