Converted lists to hashsets where appropriate

Removed unnecessary uses of  ToList()
string to mongoId conversions
This commit is contained in:
Chomp
2025-07-24 13:26:34 +01:00
parent 6079e282f5
commit f562c634a7
44 changed files with 106 additions and 126 deletions
@@ -638,10 +638,7 @@ public class DialogueController(
// Only add the profile to the friends list if it doesn't already exist
var profile = saveServer.GetProfile(sessionID);
if (!profile.FriendProfileIds.Contains(request.To.Value))
{
profile.FriendProfileIds.Add(request.To.Value);
}
// We need to delay this so that the friend request gets properly added to the clientside list before we accept it
_ = new Timer(
@@ -677,11 +674,7 @@ public class DialogueController(
public virtual void DeleteFriend(MongoId sessionID, DeleteFriendRequest request)
{
var profile = saveServer.GetProfile(sessionID);
var friendIndex = profile.FriendProfileIds.IndexOf(request.FriendId);
if (friendIndex != -1)
{
profile.FriendProfileIds.RemoveAt(friendIndex);
}
profile?.FriendProfileIds?.Remove(request.FriendId);
}
/// <summary>
@@ -226,7 +226,7 @@ public class QuestController(
}
handedInCount = int.Parse(condition.Value.ToString());
isItemHandoverQuest = condition.ConditionType == handoverQuestTypes.FirstOrDefault();
isItemHandoverQuest = condition.ConditionType == handoverQuestTypes.FirstOrDefault(); // TODO: there's 2 values, why does it only check for the first
handoverRequirements = condition;
if (pmcData.TaskConditionCounters.TryGetValue("ConditionId", out var counter))
@@ -312,7 +312,9 @@ public class QuestController(
else
{
// Remove item with children
var toRemove = pmcData.Inventory.Items.GetItemWithChildrenTpls(itemHandover.Id);
var toRemove = pmcData
.Inventory.Items.GetItemWithChildrenTpls(itemHandover.Id)
.ToHashSet();
var index = pmcData.Inventory.Items.Count;
// Important: don't tell the client to remove the attachments, it will handle it
@@ -365,7 +365,7 @@ namespace SPTarkov.Server.Core.Extensions
/// </summary>
/// <param name="items">Inventory items to look for secure container in</param>
/// <returns>List of ids</returns>
public static List<MongoId> GetSecureContainerItems(this IEnumerable<Item> items)
public static HashSet<MongoId> GetSecureContainerItems(this IEnumerable<Item> items)
{
var secureContainer = items.First(x => x.SlotId == "SecuredContainer");
@@ -378,7 +378,7 @@ namespace SPTarkov.Server.Core.Extensions
var itemsInSecureContainer = items.GetItemWithChildrenTpls(secureContainer.Id);
// Return all items returned and exclude the secure container item itself
return itemsInSecureContainer.Where(x => x != secureContainer.Id).ToList();
return itemsInSecureContainer.Where(x => x != secureContainer.Id).ToHashSet();
}
/// <summary>
@@ -209,7 +209,7 @@ namespace SPTarkov.Server.Core.Extensions
/// <param name="item">Item to check</param>
/// <param name="rootId">Root item id to check for</param>
/// <returns>True when item has rootId, false when not</returns>
public static bool DoesItemHaveRootId(this PmcData pmcData, Item item, string rootId)
public static bool DoesItemHaveRootId(this PmcData pmcData, Item item, MongoId rootId)
{
var currentItem = item;
while (currentItem is not null)
@@ -235,7 +235,7 @@ namespace SPTarkov.Server.Core.Extensions
/// <param name="pmcData">Profile to search</param>
/// <param name="questId">Quest id to look up</param>
/// <returns>QuestStatus enum</returns>
public static QuestStatusEnum GetQuestStatus(this PmcData pmcData, string questId)
public static QuestStatusEnum GetQuestStatus(this PmcData pmcData, MongoId questId)
{
var quest = pmcData.Quests?.FirstOrDefault(q => q.QId == questId);
@@ -34,7 +34,7 @@ namespace SPTarkov.Server.Core.Extensions
assort.LoyalLevelItems.Remove(itemId);
// The item being removed may have children linked to it, find and remove them too
var idsToRemove = assort.Items.GetItemWithChildrenTpls(itemId);
var idsToRemove = assort.Items.GetItemWithChildrenTpls(itemId).ToHashSet();
assort.Items.RemoveAll(item => idsToRemove.Contains(item.Id));
return assort;
@@ -47,7 +47,7 @@ namespace SPTarkov.Server.Core.Extensions
/// <param name="itemsTplsToRemove">Item TPLs the assort should not have</param>
public static void RemoveItemsFromAssort(
this TraderAssort assortToFilter,
HashSet<string> itemsTplsToRemove
HashSet<MongoId> itemsTplsToRemove
)
{
assortToFilter.Items = assortToFilter
@@ -197,7 +197,8 @@ public class BotEquipmentModGenerator(
);
switch (plateSlotFilteringOutcome.Result)
{
case Result.UNKNOWN_FAILURE or Result.NO_DEFAULT_FILTER:
case Result.UNKNOWN_FAILURE
or Result.NO_DEFAULT_FILTER:
if (logger.IsLogEnabled(LogLevel.Debug))
{
logger.Debug(
@@ -1535,7 +1536,10 @@ public class BotEquipmentModGenerator(
// Filtering mod pool to item that wasn't already there can have problems;
// You'd have a mod being picked without any sub-mods in its chain, possibly resulting in missing required mods not being added
// Mod is in existing mod pool
if (request.ItemModPool[request.ModSlot].Contains(matchingModFromPreset.Template))
if (
request.ItemModPool.TryGetValue(request.ModSlot, out var ids)
&& ids.Contains(matchingModFromPreset.Template)
)
// Found mod on preset + it already exists in mod pool
{
return [matchingModFromPreset.Template];
@@ -2056,7 +2060,7 @@ public class BotEquipmentModGenerator(
public HashSet<MongoId> FilterSightsByWeaponType(
Item weapon,
HashSet<MongoId> scopes,
Dictionary<MongoId, List<MongoId>> botWeaponSightWhitelist
Dictionary<MongoId, HashSet<MongoId>> botWeaponSightWhitelist
)
{
var weaponDetails = itemHelper.GetItem(weapon.Template);
@@ -154,11 +154,9 @@ public class LocationLootGenerator(
// Remove christmas items from loot data
if (!_seasonalEventService.ChristmasEventEnabled())
{
allStaticContainersOnMapClone = allStaticContainersOnMapClone
.Where(item =>
allStaticContainersOnMapClone = allStaticContainersOnMapClone.Where(item =>
!_seasonalEventConfig.ChristmasContainerIds.Contains(item.Template.Id)
)
.ToList();
);
}
var staticRandomisableContainersOnMap = GetRandomisableContainersOnMap(
@@ -561,7 +559,8 @@ public class LocationLootGenerator(
// Some containers need to have items forced into it (quest keys etc.)
var tplsForced = staticForced
.Where(forcedStaticProp => forcedStaticProp.ContainerId == containerClone.Template.Id)
.Select(x => x.ItemTpl);
.Select(x => x.ItemTpl)
.ToHashSet();
// Draw random loot
// Allow money to spawn more than once in container
@@ -914,9 +913,9 @@ public class LocationLootGenerator(
// Ensure no seasonal items are in pool if not in-season
if (!seasonalEventActive)
{
spawnPoint.Template.Items = spawnPoint
.Template.Items.Where(item => !seasonalItemTplBlacklist.Contains(item.Template))
.ToList();
spawnPoint.Template.Items = spawnPoint.Template.Items.Where(item =>
!seasonalItemTplBlacklist.Contains(item.Template)
);
}
// Spawn point has no items after filtering, skip
@@ -119,7 +119,7 @@ public class CompletionQuestGenerator(
traderId,
repeatableConfig,
completionConfig,
selectedItems
selectedItems.ToHashSet()
);
return quest;
@@ -636,7 +636,7 @@ public class EliminationQuestGenerator(
// Filter out close range weapons from far distance requirement
case > 50:
{
List<string> weaponTypeBlacklist = ["Shotgun", "Pistol"];
HashSet<string> weaponTypeBlacklist = ["Shotgun", "Pistol"];
// Filter out close range weapons from long distance requirement
generationData.WeaponCategoryRequirementConfig.RemoveAll(category =>
@@ -647,7 +647,7 @@ public class EliminationQuestGenerator(
// Filter out long range weapons from close distance requirement
case < 20:
{
List<string> weaponTypeBlacklist = ["MarksmanRifle", "DMR"];
HashSet<string> weaponTypeBlacklist = ["MarksmanRifle", "DMR"];
// Filter out far range weapons from close distance requirement
generationData.WeaponCategoryRequirementConfig.RemoveAll(category =>
@@ -64,7 +64,7 @@ public class RepeatableQuestRewardGenerator(
MongoId traderId,
RepeatableQuestConfig repeatableConfig,
BaseQuestConfig eliminationConfig,
List<MongoId>? rewardTplBlacklist = null
HashSet<MongoId>? rewardTplBlacklist = null
)
{
// Get vars to configure rewards with
@@ -40,7 +40,7 @@ public class ExternalInventoryMagGen(
var magTemplate = inventoryMagGen.GetMagazineTemplate();
var magazineTpl = magTemplate.Id;
var weapon = inventoryMagGen.GetWeaponTemplate();
List<MongoId> attemptedMagBlacklist = [];
HashSet<MongoId> attemptedMagBlacklist = [];
var defaultMagazineTpl = weapon.GetWeaponsDefaultMagazineTpl();
var isShotgun = itemHelper.IsOfBaseclass(weapon.Id, BaseClasses.SHOTGUN);
@@ -176,7 +176,7 @@ public class ExternalInventoryMagGen(
/// <returns> Item of chosen magazine </returns>
public TemplateItem? GetRandomExternalMagazineForInternalMagazineGun(
MongoId weaponTpl,
List<MongoId> magazineBlacklist
HashSet<MongoId> magazineBlacklist
)
{
// The mag Slot data for the weapon
@@ -28,7 +28,7 @@ public class AssortHelper(
PmcData pmcProfile,
MongoId traderId,
TraderAssort traderAssorts,
Dictionary<string, Dictionary<string, string>> mergedQuestAssorts,
Dictionary<string, Dictionary<MongoId, string>> mergedQuestAssorts,
bool isFlea = false
)
{
@@ -71,15 +71,15 @@ public class AssortHelper(
/// <param name="mergedQuestAssorts">quest assorts to search for assort id</param>
/// <param name="assortId">Assort to look for linked quest id</param>
/// <returns>quest id + array of quest status the assort should show for</returns>
protected KeyValuePair<string, List<QuestStatusEnum>>? GetQuestIdAndStatusThatShowAssort(
Dictionary<string, Dictionary<string, string>> mergedQuestAssorts,
string assortId
protected KeyValuePair<MongoId, HashSet<QuestStatusEnum>>? GetQuestIdAndStatusThatShowAssort(
Dictionary<string, Dictionary<MongoId, string>> mergedQuestAssorts,
MongoId assortId
)
{
if (mergedQuestAssorts.TryGetValue("started", out var dict1) && dict1.ContainsKey(assortId))
// Assort unlocked by starting quest, assort is visible to player when : started or ready to hand in + handed in
{
return new KeyValuePair<string, List<QuestStatusEnum>>(
return new KeyValuePair<MongoId, HashSet<QuestStatusEnum>>(
mergedQuestAssorts["started"][assortId],
[
QuestStatusEnum.Started,
@@ -91,7 +91,7 @@ public class AssortHelper(
if (mergedQuestAssorts.TryGetValue("success", out var dict2) && dict2.ContainsKey(assortId))
{
return new KeyValuePair<string, List<QuestStatusEnum>>(
return new KeyValuePair<MongoId, HashSet<QuestStatusEnum>>(
mergedQuestAssorts["success"][assortId],
[QuestStatusEnum.Success]
);
@@ -99,7 +99,7 @@ public class AssortHelper(
if (mergedQuestAssorts.TryGetValue("fail", out var dict3) && dict3.ContainsKey(assortId))
{
return new KeyValuePair<string, List<QuestStatusEnum>>(
return new KeyValuePair<MongoId, HashSet<QuestStatusEnum>>(
mergedQuestAssorts["fail"][assortId],
[QuestStatusEnum.Fail]
);
@@ -37,7 +37,7 @@ public class BotGeneratorHelper(
nameof(EquipmentSlots.ArmBand),
];
private static readonly string[] _pmcTypes =
private static readonly FrozenSet<string> _pmcTypes =
[
Sides.PmcBear.ToLowerInvariant(),
Sides.PmcUsec.ToLowerInvariant(),
@@ -524,9 +524,7 @@ public class BotGeneratorHelper(
/// <returns>Equipment role (e.g. pmc / assault / bossTagilla)</returns>
public string GetBotEquipmentRole(string botRole)
{
return _pmcTypes.Contains(botRole, StringComparer.OrdinalIgnoreCase)
? Sides.PmcEquipmentRole
: botRole;
return _pmcTypes.Contains(botRole.ToLower()) ? Sides.PmcEquipmentRole : botRole;
}
/// <summary>
@@ -775,13 +773,13 @@ public class BotGeneratorHelper(
return false;
}
// If Filter array only contains 1 filter and its for basetype 'item', allow it
// If Filter array only contains 1 filter and it is for basetype 'item', allow it
if (filter.Count == 1 && filter.Contains(BaseClasses.ITEM))
{
return true;
}
// If allowed filter has something in it + filter doesnt have basetype 'item', not allowed
// If allowed filter has something in it + filter doesn't have basetype 'item', not allowed
if (filter.Count > 0 && !filter.Contains(itemDetails?.Parent ?? string.Empty))
{
return false;
@@ -126,21 +126,6 @@ public class BotHelper(
}
}
/// <summary>
/// is the provided role a PMC, case-agnostic
/// </summary>
/// <param name="botRole">Role to check</param>
/// <returns>True if role is PMC</returns>
public bool BotRoleIsPmc(string botRole)
{
HashSet<string> listToCheck =
[
_pmcConfig.UsecType.ToLowerInvariant(),
_pmcConfig.BearType.ToLowerInvariant(),
];
return listToCheck.Contains(botRole.ToLowerInvariant());
}
/// <summary>
/// Get randomization settings for bot from config/bot.json
/// </summary>
@@ -250,7 +250,7 @@ public class InRaidHelper(
// Ensure we don't pick up pocket items from mannequins
if (
item.SlotId.StartsWith("pocket")
&& pmcProfile.DoesItemHaveRootId(item, pmcProfile.Inventory.Equipment)
&& pmcProfile.DoesItemHaveRootId(item, pmcProfile.Inventory.Equipment.Value)
)
{
return true;
@@ -1152,7 +1152,7 @@ public class ItemHelper(
/// <param name="tpl">Items tpl to check parents of</param>
/// <param name="tplsToCheck">Tpl values to check if parents of item match</param>
/// <returns>bool Match found</returns>
public bool DoesItemOrParentsIdMatch(MongoId tpl, List<MongoId> tplsToCheck)
public bool DoesItemOrParentsIdMatch(MongoId tpl, HashSet<MongoId> tplsToCheck)
{
var (itemExists, item) = GetItem(tpl);
@@ -1631,7 +1631,7 @@ public class ItemHelper(
string caliber,
Dictionary<string, IEnumerable<StaticAmmoDetails>> staticAmmoDist,
MongoId? fallbackCartridgeTpl = null,
ICollection<MongoId>? cartridgeWhitelist = null
ISet<MongoId>? cartridgeWhitelist = null
)
{
var ammos = staticAmmoDist.GetValueOrDefault(caliber, []);
@@ -372,13 +372,13 @@ public class ProfileHelper(
var secureContainer = items.FirstOrDefault(i => i.SlotId == "SecuredContainer");
if (secureContainer is not null)
{
// Find and remove container + children
var childItemsInSecureContainer = items.GetItemWithChildrenTpls(secureContainer.Id);
// Find secure container + children
var secureContainerAndChildrenIds = items
.GetItemWithChildrenTpls(secureContainer.Id)
.ToHashSet();
// Remove child items + secure container
profile.Inventory.Items = items
.Where(i => !childItemsInSecureContainer.Contains(i.Id))
.ToList();
// Remove secure container + its children
items.RemoveAll(x => secureContainerAndChildrenIds.Contains(x.Id));
}
return profile;
@@ -103,7 +103,7 @@ public class QuestHelper(
/// <param name="before">List of quests #1</param>
/// <param name="after">List of quests #2</param>
/// <returns>quests not in before</returns>
public List<Quest> GetDeltaQuests(List<Quest> before, List<Quest> after)
public IEnumerable<Quest> GetDeltaQuests(List<Quest> before, List<Quest> after)
{
// Nothing to compare against, return after
if (before.Count == 0)
@@ -115,7 +115,7 @@ public class QuestHelper(
var beforeQuests = before.Select(quest => quest.Id).ToHashSet();
// Return quests found in after but not before
return after.Where(quest => !beforeQuests.Contains(quest.Id)).ToList();
return after.Where(quest => !beforeQuests.Contains(quest.Id));
}
/// <summary>
@@ -586,7 +586,7 @@ public class QuestHelper(
var acceptedQuestCondition = q.Conditions.AvailableForStart.FirstOrDefault(c =>
c.ConditionType == "Quest"
&& (c.Target.IsList ? c.Target.List : [c.Target.Item]).Contains(failedQuestId)
&& c.Status[0] == QuestStatusEnum.Fail
&& c.Status.First() == QuestStatusEnum.Fail
);
if (acceptedQuestCondition is null)
@@ -1586,7 +1586,7 @@ public class QuestHelper(
/// <param name="completedQuestId">Quest just completed</param>
protected void AddTimeLockedQuestsToProfile(
PmcData pmcData,
List<Quest> quests,
IEnumerable<Quest> quests,
MongoId completedQuestId
)
{
@@ -612,9 +612,9 @@ public class RagfairOfferHelper(
return false;
}
protected HashSet<string> GetLoyaltyLockedOffers(List<RagfairOffer> offers, PmcData pmcProfile)
protected HashSet<MongoId> GetLoyaltyLockedOffers(List<RagfairOffer> offers, PmcData pmcProfile)
{
var loyaltyLockedOffers = new HashSet<string>();
var loyaltyLockedOffers = new HashSet<MongoId>();
foreach (var offer in offers.Where(x => x.IsTraderOffer()))
{
if (
@@ -24,8 +24,8 @@ public class TraderAssortHelper(
ICloner cloner
)
{
private Dictionary<string, Dictionary<string, string>>? _mergedQuestAssorts;
protected virtual Dictionary<string, Dictionary<string, string>> MergedQuestAssorts
private Dictionary<string, Dictionary<MongoId, string>>? _mergedQuestAssorts;
protected virtual Dictionary<string, Dictionary<MongoId, string>> MergedQuestAssorts
{
get { return _mergedQuestAssorts ??= HydrateMergedQuestAssorts(); }
}
@@ -148,9 +148,9 @@ public class TraderAssortHelper(
/// Create a dictionary keyed by quest status (started/success) with every assortId to QuestId from every trader
/// </summary>
/// <returns>Dictionary</returns>
protected Dictionary<string, Dictionary<string, string>> HydrateMergedQuestAssorts()
protected Dictionary<string, Dictionary<MongoId, string>> HydrateMergedQuestAssorts()
{
var result = new Dictionary<string, Dictionary<string, string>>();
var result = new Dictionary<string, Dictionary<MongoId, string>>();
// Loop every trader
var traders = databaseService.GetTraders();
@@ -171,7 +171,7 @@ public class TraderAssortHelper(
}
// Null guard - ensure Started/Success/fail exists
result.TryAdd(unlockStatus, new Dictionary<string, string>());
result.TryAdd(unlockStatus, new Dictionary<MongoId, string>());
foreach (var (assortId, questId) in assortToQuestDict)
{
@@ -606,7 +606,7 @@ public record CounterKeyValue
[JsonExtensionData]
public Dictionary<string, object>? ExtensionData { get; set; }
public IEnumerable<string>? Key { get; set; }
public HashSet<string>? Key { get; set; }
public double? Value { get; set; }
}
@@ -79,11 +79,11 @@ public record ProfileTraderTemplate
/// What traders default to being locked on profile creation
/// </summary>
[JsonPropertyName("lockedByDefaultOverride")]
public List<MongoId>? LockedByDefaultOverride { get; set; }
public HashSet<MongoId>? LockedByDefaultOverride { get; set; }
/// <summary>
/// What traders should have their clothing unlocked/purchased on creation
/// </summary>
[JsonPropertyName("purchaseAllClothingByDefaultForTrader")]
public List<MongoId>? PurchaseAllClothingByDefaultForTrader { get; set; }
public HashSet<MongoId>? PurchaseAllClothingByDefaultForTrader { get; set; }
}
@@ -213,7 +213,7 @@ public record QuestCondition
public string? Type { get; set; }
[JsonPropertyName("status")]
public List<QuestStatusEnum>? Status { get; set; }
public HashSet<QuestStatusEnum>? Status { get; set; }
[JsonPropertyName("availableAfter")]
public int? AvailableAfter { get; set; }
@@ -189,7 +189,7 @@ public record ItemsBlacklist
public int? MinPlayerLevel { get; set; }
[JsonPropertyName("itemIds")]
public List<string>? ItemIds { get; set; }
public HashSet<MongoId>? ItemIds { get; set; }
}
public record ItemsWhitelist
@@ -201,7 +201,7 @@ public record ItemsWhitelist
public int? MinPlayerLevel { get; set; }
[JsonPropertyName("itemIds")]
public List<string>? ItemIds { get; set; }
public HashSet<MongoId>? ItemIds { get; set; }
}
public record SampleQuests
@@ -1725,7 +1725,7 @@ public record GridFilter
public HashSet<string>? Filter { get; set; }
[JsonPropertyName("ExcludedFilter")]
public List<string>? ExcludedFilter { get; set; }
public HashSet<string>? ExcludedFilter { get; set; }
[JsonPropertyName("locked")]
public bool? Locked { get; set; }
@@ -57,7 +57,7 @@ public record SptProfile
/// List of friend profile IDs
/// </summary>
[JsonPropertyName("friends")]
public List<MongoId>? FriendProfileIds { get; set; }
public HashSet<MongoId>? FriendProfileIds { get; set; }
/// <summary>
/// Stores profile-related customisation, e.g. clothing / hideout walls / floors
@@ -422,7 +422,7 @@ public record Spt
/// item TPLs blacklisted from being sold on flea for this profile
/// </summary>
[JsonPropertyName("blacklistedItemTpls")]
public HashSet<string>? BlacklistedItemTemplates { get; set; }
public HashSet<MongoId>? BlacklistedItemTemplates { get; set; }
/// <summary>
/// key: daily type
@@ -54,7 +54,7 @@ public record GenerateEquipmentProperties
/// OPTIONAL - Do not generate mods for tpls in this array
/// </summary>
[JsonPropertyName("generateModsBlacklist")]
public HashSet<string>? GenerateModsBlacklist { get; set; }
public HashSet<MongoId>? GenerateModsBlacklist { get; set; }
[JsonPropertyName("generatingPlayerLevel")]
public double? GeneratingPlayerLevel { get; set; }
@@ -30,7 +30,7 @@ public record ModToSpawnRequest
/// Parent slot the item will be a part of
/// </summary>
[JsonPropertyName("botWeaponSightWhitelist")]
public Dictionary<MongoId, List<MongoId>>? BotWeaponSightWhitelist { get; set; }
public Dictionary<MongoId, HashSet<MongoId>>? BotWeaponSightWhitelist { get; set; }
/// <summary>
/// Blacklist to prevent mods from being picked
@@ -86,7 +86,7 @@ public record AirdropLoot
/// Armor levels to allow inside crate e.g. [4,5,6]
/// </summary>
[JsonPropertyName("armorLevelWhitelist")]
public List<int>? ArmorLevelWhitelist { get; set; }
public HashSet<int>? ArmorLevelWhitelist { get; set; }
/// <summary>
/// Should boss items be added to airdrop crate
@@ -188,7 +188,7 @@ public record WalletLootSettings
/// What wallets will have money in them
/// </summary>
[JsonPropertyName("walletTplPool")]
public required List<MongoId> WalletTplPool { get; set; }
public required HashSet<MongoId> WalletTplPool { get; set; }
}
public record EquipmentFilters
@@ -206,7 +206,7 @@ public record EquipmentFilters
/// Whitelist for weapon sight types allowed per gun
/// </summary>
[JsonPropertyName("weaponSightWhitelist")]
public Dictionary<MongoId, List<MongoId>>? WeaponSightWhitelist { get; set; }
public Dictionary<MongoId, HashSet<MongoId>>? WeaponSightWhitelist { get; set; }
[JsonPropertyName("forceOnlyArmoredRigWhenNoArmor")]
public bool? ForceOnlyArmoredRigWhenNoArmor { get; set; }
@@ -332,13 +332,13 @@ public record RandomisationDetails
/// Mod slots that should be fully randomised -ignores mods from bottype.json and instead creates a pool using items.json
/// </summary>
[JsonPropertyName("randomisedWeaponModSlots")]
public List<string>? RandomisedWeaponModSlots { get; set; }
public HashSet<string>? RandomisedWeaponModSlots { get; set; }
/// <summary>
/// Armor slots that should be randomised e.g. 'Headwear, Armband'
/// </summary>
[JsonPropertyName("randomisedArmorSlots")]
public List<string>? RandomisedArmorSlots { get; set; }
public HashSet<string>? RandomisedArmorSlots { get; set; }
/// <summary>
/// Equipment chances
@@ -17,13 +17,13 @@ public record InRaidConfig : BaseConfig
/// Names of car extracts
/// </summary>
[JsonPropertyName("carExtracts")]
public required List<string> CarExtracts { get; set; }
public required HashSet<string> CarExtracts { get; set; }
/// <summary>
/// Names of coop extracts
/// </summary>
[JsonPropertyName("coopExtracts")]
public required List<string> CoopExtracts { get; set; }
public required HashSet<string> CoopExtracts { get; set; }
/// <summary>
/// Fence rep gain from a single car extract
@@ -23,7 +23,7 @@ public record LocaleConfig : BaseConfig
/// Languages server can be translated into
/// </summary>
[JsonPropertyName("serverSupportedLocales")]
public required List<string> ServerSupportedLocales { get; set; }
public required HashSet<string> ServerSupportedLocales { get; set; }
[JsonPropertyName("fallbacks")]
public required Dictionary<string, string> Fallbacks { get; set; }
@@ -106,7 +106,7 @@ public record LocationConfig : BaseConfig
/// Key: map, value: loose loot ids to ignore
/// </summary>
[JsonPropertyName("looseLootBlacklist")]
public required Dictionary<string, List<string>> LooseLootBlacklist { get; set; }
public required Dictionary<string, HashSet<string>> LooseLootBlacklist { get; set; }
/// <summary>
/// Key: map, value: settings to control how long scav raids are
@@ -413,7 +413,7 @@ public record SpecificExits
/// Whitelist of specific extract types
/// </summary>
[JsonPropertyName("passageRequirementWhitelist")]
public required List<string> PassageRequirementWhitelist { get; set; }
public required HashSet<string> PassageRequirementWhitelist { get; set; }
}
public record Completion : BaseQuestConfig
@@ -541,7 +541,7 @@ public record EliminationConfig : BaseQuestConfig
/// Locations that should be blacklisted as a requirement
/// </summary>
[JsonPropertyName("distLocationBlacklist")]
public required List<string> DistLocationBlacklist { get; set; }
public required HashSet<string> DistLocationBlacklist { get; set; }
/// <summary>
/// Probability that a distance requirement is chosen
@@ -63,7 +63,7 @@ public record SeasonalEventConfig : BaseConfig
/// Ids of containers on locations that only have Christmas loot
/// </summary>
[JsonPropertyName("christmasContainerIds")]
public required List<string> ChristmasContainerIds { get; set; }
public required HashSet<string> ChristmasContainerIds { get; set; }
/// <summary>
/// Season - botType - location (body/feet/hands/head)
@@ -58,7 +58,7 @@ public record LootRequest
/// Allowed armor plate levels 2/3/4/5/6 for armor generated
/// </summary>
[JsonPropertyName("armorLevelWhitelist")]
public List<int>? ArmorLevelWhitelist { get; set; }
public HashSet<int>? ArmorLevelWhitelist { get; set; }
/// <summary>
/// Should boss items be included in allowed items
@@ -161,16 +161,13 @@ public class BotEquipmentFilterService(
/// </summary>
/// <param name="botEquipmentRole">equipment role of bot to look up</param>
/// <returns>Dictionary of weapon type and their whitelisted scope types</returns>
public Dictionary<MongoId, List<MongoId>> GetBotWeaponSightWhitelist(string botEquipmentRole)
public Dictionary<MongoId, HashSet<MongoId>>? GetBotWeaponSightWhitelist(
string botEquipmentRole
)
{
var botEquipmentSettings = _botConfig.Equipment[botEquipmentRole];
if (botEquipmentSettings is null)
{
return null;
}
return botEquipmentSettings.WeaponSightWhitelist;
return _botConfig.Equipment.TryGetValue(botEquipmentRole, out var botEquipmentSettings)
? botEquipmentSettings.WeaponSightWhitelist
: null;
}
/// <summary>
@@ -66,7 +66,7 @@ public class BtrDeliveryService(
// Remove any items that were returned by the item delivery, but also insured, from the player's insurance list
// This is to stop items being duplicated by being returned from both item delivery and insurance
var deliveredItemIds = items.Select(item => item.Id);
var deliveredItemIds = items.Select(item => item.Id).ToHashSet();
pmcData.InsuredItems = pmcData
.InsuredItems.Where(insuredItem => !deliveredItemIds.Contains(insuredItem.ItemId.Value))
.ToList();
@@ -468,6 +468,7 @@ public class CircleOfCultistService(
// Handle special case of tagilla helmets - only one reward is allowed
if (directReward.Reward.Contains(ItemTpl.FACECOVER_TAGILLAS_WELDING_MASK_GORILLA))
{
// TODO: this is likely redundant with direct reward system in config?
directReward.Reward = [randomUtil.GetArrayValue(directReward.Reward)];
}
@@ -598,7 +599,7 @@ public class CircleOfCultistService(
/// </summary>
/// <param name="rewardTpl">Item being rewarded to get stack size of</param>
/// <returns>stack size of item</returns>
protected int GetDirectRewardBaseTypeStackSize(string rewardTpl)
protected int GetDirectRewardBaseTypeStackSize(MongoId rewardTpl)
{
var itemDetails = itemHelper.GetItem(rewardTpl);
if (!itemDetails.Key)
@@ -109,7 +109,7 @@ public class LocaleService(
/// Get array of languages supported for localisation
/// </summary>
/// <returns> List of locales e.g. en/fr/cn </returns>
public List<string> GetServerSupportedLocales()
public HashSet<string> GetServerSupportedLocales()
{
return _localeConfig.ServerSupportedLocales;
}
@@ -601,7 +601,7 @@ public class LocationLifecycleService(
/// </summary>
/// <param name="extractName"> Name of extract player took </param>
/// <returns> True if coop extract </returns>
protected bool ExtractTakenWasCoop(string extractName)
protected bool ExtractTakenWasCoop(string? extractName)
{
// No extract name, not a coop extract
if (extractName is null)
@@ -990,7 +990,8 @@ public class LocationLifecycleService(
is not QuestStatusEnum.AvailableForStart
and not QuestStatusEnum.Success
)
.Select(status => status.QId);
.Select(status => status.QId)
.ToHashSet();
// Get db details of quests we found above
var questDb = databaseService
@@ -53,7 +53,7 @@ public class OpenZoneService(
var zonesToAdd = _locationConfig.OpenZones[mapKvP.Key];
// Convert openzones string into list, easier to work wih
var mapOpenZonesArray = dbLocations[mapKvP.Key].Base.OpenZones.Split(",").ToList();
var mapOpenZonesArray = dbLocations[mapKvP.Key].Base.OpenZones.Split(",").ToHashSet();
foreach (
var zoneToAdd in zonesToAdd.Where(zoneToAdd =>
!mapOpenZonesArray.Contains(zoneToAdd)
@@ -131,9 +131,9 @@ public class ServerLocalisationService(
/// <returns> Locale text </returns>
public string GetRandomTextThatMatchesPartialKey(string partialKey)
{
var matchingKeys = GetLocaleKeys().Where(x => x.Contains(partialKey)).ToList();
var matchingKeys = GetLocaleKeys().Where(x => x.Contains(partialKey));
if (matchingKeys.Count == 0)
if (!matchingKeys.Any())
{
logger.Warning($"No locale keys found for: {partialKey}");
@@ -21,7 +21,7 @@ public class RagfairOfferHolder(
/// <summary>
/// Expired offer Ids
/// </summary>
private readonly HashSet<string> _expiredOfferIds = [];
private readonly HashSet<MongoId> _expiredOfferIds = [];
/// <summary>
/// Ragfair offer cache, keyed by offer Id
@@ -55,7 +55,7 @@ public class RagfairOfferHolder(
/// Get a ragfair offer by its id
/// </summary>
/// <returns>RagfairOffer</returns>
public HashSet<string> GetStaleOfferIds()
public HashSet<MongoId> GetStaleOfferIds()
{
lock (_expiredOfferIdsLock)
{