Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 111 additions & 73 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion RetakesAllocator/RetakesAllocator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.329" />
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.341" />
<PackageReference Include="RetakesPluginShared" Version="2.0.0" />
</ItemGroup>

Expand Down
86 changes: 84 additions & 2 deletions RetakesAllocatorCore/Config/Configs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ public Dictionary<
CsTeam,
Dictionary<CsItem, int>
>
> MaxNades { get; set; } = new()
> MaxNades
{ get; set; } = new()
{
{
NadeHelpers.GlobalSettingName, new()
Expand Down Expand Up @@ -181,7 +182,8 @@ public Dictionary<
CsTeam,
Dictionary<RoundType, MaxTeamNadesSetting>
>
> MaxTeamNades { get; set; } = new()
> MaxTeamNades
{ get; set; } = new()
{
{
NadeHelpers.GlobalSettingName, new()
Expand All @@ -206,6 +208,86 @@ public Dictionary<
}
};

public Dictionary<
string,
Dictionary<
CsTeam,
Dictionary<RoundType, Dictionary<CsItem, MaxTeamNadesPerTypeSetting>>
>
> MaxTeamNadesPerType
{ get; set; } = new()
{
{
NadeHelpers.GlobalSettingName, new()
{
{
CsTeam.Terrorist, new()
{
{
RoundType.Pistol, new()
{
{CsItem.Flashbang, MaxTeamNadesPerTypeSetting.Unlimited},
{CsItem.Smoke, MaxTeamNadesPerTypeSetting.Two},
{CsItem.Molotov, MaxTeamNadesPerTypeSetting.Unlimited},
{CsItem.HE, MaxTeamNadesPerTypeSetting.Unlimited},
}
},
{
RoundType.HalfBuy, new()
{
{CsItem.Flashbang, MaxTeamNadesPerTypeSetting.Unlimited},
{CsItem.Smoke, MaxTeamNadesPerTypeSetting.Three},
{CsItem.Molotov, MaxTeamNadesPerTypeSetting.Unlimited},
{CsItem.HE, MaxTeamNadesPerTypeSetting.Unlimited},
}
},
{
RoundType.FullBuy, new()
{
{CsItem.Flashbang, MaxTeamNadesPerTypeSetting.Unlimited},
{CsItem.Smoke, MaxTeamNadesPerTypeSetting.Three},
{CsItem.Molotov, MaxTeamNadesPerTypeSetting.Unlimited},
{CsItem.HE, MaxTeamNadesPerTypeSetting.Unlimited},
}
},
}
},
{
CsTeam.CounterTerrorist, new()
{
{
RoundType.Pistol, new()
{
{CsItem.Flashbang, MaxTeamNadesPerTypeSetting.Unlimited},
{CsItem.Smoke, MaxTeamNadesPerTypeSetting.Two},
{CsItem.Incendiary, MaxTeamNadesPerTypeSetting.Unlimited},
{CsItem.HE, MaxTeamNadesPerTypeSetting.Unlimited},
}
},
{
RoundType.HalfBuy, new()
{
{CsItem.Flashbang, MaxTeamNadesPerTypeSetting.Unlimited},
{CsItem.Smoke, MaxTeamNadesPerTypeSetting.Three},
{CsItem.Incendiary, MaxTeamNadesPerTypeSetting.Unlimited},
{CsItem.HE, MaxTeamNadesPerTypeSetting.Unlimited},
}
},
{
RoundType.FullBuy, new()
{
{CsItem.Flashbang, MaxTeamNadesPerTypeSetting.Unlimited},
{CsItem.Smoke, MaxTeamNadesPerTypeSetting.Three},
{CsItem.Incendiary, MaxTeamNadesPerTypeSetting.Unlimited},
{CsItem.HE, MaxTeamNadesPerTypeSetting.Unlimited},
}
},
}
},
}
}
};

public RoundTypeSelectionOption RoundTypeSelection { get; set; } = RoundTypeSelectionOption.Random;

public Dictionary<RoundType, int> RoundTypePercentages { get; set; } = new()
Expand Down
88 changes: 86 additions & 2 deletions RetakesAllocatorCore/NadeHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,22 @@ public enum MaxTeamNadesSetting
AverageTwoPerPlayer,
}

public enum MaxTeamNadesPerTypeSetting
{
Unlimited,
Zero,
One,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
}

public class NadeHelpers
{
public static string GlobalSettingName = "GLOBAL";
Expand Down Expand Up @@ -58,7 +74,7 @@ public static Stack<CsItem> GetUtilForTeam(string? map, RoundType roundType, CsT
MaxTeamNadesSetting.Eight => 8,
MaxTeamNadesSetting.Nine => 9,
MaxTeamNadesSetting.Ten => 10,
_ => (int) Math.Ceiling(numPlayers * multiplier)
_ => (int)Math.Ceiling(numPlayers * multiplier)
};

Log.Debug($"Nade setting: {maxNadesSetting}. Total: {maxTotalNades}. Map: {map}");
Expand All @@ -72,6 +88,7 @@ public static Stack<CsItem> GetUtilForTeam(string? map, RoundType roundType, CsT
molly, molly
};

// Per-player limits from MaxNades config
var nadeAllocations = new Dictionary<CsItem, int>
{
{CsItem.Flashbang, GetMaxNades(map, team, CsItem.Flashbang)},
Expand All @@ -80,7 +97,24 @@ public static Stack<CsItem> GetUtilForTeam(string? map, RoundType roundType, CsT
{molly, GetMaxNades(map, team, molly)},
};

// Per-team per-type limits from MaxTeamNadesPerType config
var teamNadeTypeLimits = new Dictionary<CsItem, int>
{
{CsItem.Flashbang, GetMaxTeamNadesPerType(map, team, roundType, CsItem.Flashbang)},
{CsItem.Smoke, GetMaxTeamNadesPerType(map, team, roundType, CsItem.Smoke)},
{CsItem.HE, GetMaxTeamNadesPerType(map, team, roundType, CsItem.HE)},
{molly, GetMaxTeamNadesPerType(map, team, roundType, molly)},
};

var nades = new Stack<CsItem>();
var teamNadeTypeCounts = new Dictionary<CsItem, int>
{
{CsItem.Flashbang, 0},
{CsItem.Smoke, 0},
{CsItem.HE, 0},
{molly, 0},
};

while (true)
{
if (nadeAllocations.Count == 0 || maxTotalNades <= 0)
Expand All @@ -89,6 +123,16 @@ public static Stack<CsItem> GetUtilForTeam(string? map, RoundType roundType, CsT
}

var nextNade = Utils.Choice(nadeDistribution);

// Check if team has reached per-type limit
if (teamNadeTypeCounts[nextNade] >= teamNadeTypeLimits[nextNade])
{
nadeDistribution.RemoveAll(item => item == nextNade);
nadeAllocations.Remove(nextNade);
continue;
}

// Check if player limit reached
if (nadeAllocations[nextNade] <= 0)
{
nadeDistribution.RemoveAll(item => item == nextNade);
Expand All @@ -98,6 +142,7 @@ public static Stack<CsItem> GetUtilForTeam(string? map, RoundType roundType, CsT

nades.Push(nextNade);
nadeAllocations[nextNade]--;
teamNadeTypeCounts[nextNade]++;
maxTotalNades--;
}

Expand Down Expand Up @@ -125,6 +170,45 @@ private static MaxTeamNadesSetting GetMaxTeamNades(string map, CsTeam team, Roun
return GetMaxTeamNades(GlobalSettingName, team, roundType);
}

private static int GetMaxTeamNadesPerType(string map, CsTeam team, RoundType roundType, CsItem nadeType)
{
if (Configs.GetConfigData().MaxTeamNadesPerType.TryGetValue(map, out var mapMaxNades))
{
if (mapMaxNades.TryGetValue(team, out var teamMaxNades))
{
if (teamMaxNades.TryGetValue(roundType, out var roundTypeMaxNades))
{
if (roundTypeMaxNades.TryGetValue(nadeType, out var maxNadesSetting))
{
return maxNadesSetting switch
{
MaxTeamNadesPerTypeSetting.Unlimited => 999999,
MaxTeamNadesPerTypeSetting.Zero => 0,
MaxTeamNadesPerTypeSetting.One => 1,
MaxTeamNadesPerTypeSetting.Two => 2,
MaxTeamNadesPerTypeSetting.Three => 3,
MaxTeamNadesPerTypeSetting.Four => 4,
MaxTeamNadesPerTypeSetting.Five => 5,
MaxTeamNadesPerTypeSetting.Six => 6,
MaxTeamNadesPerTypeSetting.Seven => 7,
MaxTeamNadesPerTypeSetting.Eight => 8,
MaxTeamNadesPerTypeSetting.Nine => 9,
MaxTeamNadesPerTypeSetting.Ten => 10,
_ => 999999,
};
}
}
}
}

if (map == GlobalSettingName)
{
return 999999;
}

return GetMaxTeamNadesPerType(GlobalSettingName, team, roundType, nadeType);
}

private static int GetMaxNades(string map, CsTeam team, CsItem nade)
{
if (Configs.GetConfigData().MaxNades.TryGetValue(map, out var mapNades))
Expand Down Expand Up @@ -227,4 +311,4 @@ Dictionary<T, ICollection<CsItem>> nadesByPlayer
}
}
}
}
}
2 changes: 1 addition & 1 deletion RetakesAllocatorCore/PluginInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace RetakesAllocatorCore;

public static class PluginInfo
{
public const string Version = "2.4.3";
public const string Version = "2.5.0";

public static readonly string LogPrefix = $"[RetakesAllocator {Version}] ";

Expand Down
2 changes: 1 addition & 1 deletion RetakesAllocatorCore/RetakesAllocatorCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.329" />
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.341" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.14"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.14">
<PrivateAssets>all</PrivateAssets>
Expand Down
25 changes: 22 additions & 3 deletions RetakesAllocatorTest/NadeAllocationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,31 @@ public void TestAllocateNadesToPlayers()
{
var util = NadeHelpers.GetUtilForTeam(null, RoundType.Pistol, CsTeam.Terrorist, 4);
Dictionary<int, ICollection<CsItem>> nadesByPlayer = new();
NadeHelpers.AllocateNadesToPlayers(new Stack<CsItem>(util), new List<int> {1, 2, 3, 4}, nadesByPlayer);
NadeHelpers.AllocateNadesToPlayers(new Stack<CsItem>(util), new List<int> { 1, 2, 3, 4 }, nadesByPlayer);
Assert.That(util, Is.EquivalentTo(nadesByPlayer.Values.SelectMany(x => x)));

util = NadeHelpers.GetUtilForTeam("de_dust2", RoundType.Pistol, CsTeam.CounterTerrorist, 0);
nadesByPlayer = new();
NadeHelpers.AllocateNadesToPlayers(new Stack<CsItem>(util), new List<int>(), nadesByPlayer);
Assert.That(util, Is.EquivalentTo(nadesByPlayer.Values.SelectMany(x => x)));
}
}

[Test]
public void TestMaxTeamNadesPerTypeRestriction()
{
// Test that smoke grenades are limited to 2 for pistol rounds (per default config)
var util = NadeHelpers.GetUtilForTeam(null, RoundType.Pistol, CsTeam.Terrorist, 5);
var smokeCount = util.Count(nade => nade == CsItem.Smoke);
Assert.That(smokeCount, Is.LessThanOrEqualTo(2), "Pistol round should have max 2 smokes for Terrorist");

// Test that smoke grenades are limited to 3 for full buy rounds (per default config)
util = NadeHelpers.GetUtilForTeam(null, RoundType.FullBuy, CsTeam.Terrorist, 5);
smokeCount = util.Count(nade => nade == CsItem.Smoke);
Assert.That(smokeCount, Is.LessThanOrEqualTo(3), "Full buy round should have max 3 smokes for Terrorist");

// Test CT side with Incendiary
util = NadeHelpers.GetUtilForTeam(null, RoundType.Pistol, CsTeam.CounterTerrorist, 5);
smokeCount = util.Count(nade => nade == CsItem.Smoke);
Assert.That(smokeCount, Is.LessThanOrEqualTo(2), "Pistol round should have max 2 smokes for CT");
}
}