diff --git a/Libraries/Core/Controllers/TradeController.cs b/Libraries/Core/Controllers/TradeController.cs index b0f7c428..7e426779 100644 --- a/Libraries/Core/Controllers/TradeController.cs +++ b/Libraries/Core/Controllers/TradeController.cs @@ -124,7 +124,7 @@ public class TradeController( } // Exit loop early if problem found - if (output.Warnings.Count > 0) + if (output.Warnings?.Count > 0) { return output; } diff --git a/Libraries/Core/Generators/BotInventoryGenerator.cs b/Libraries/Core/Generators/BotInventoryGenerator.cs index 5cbfd79b..50e4c608 100644 --- a/Libraries/Core/Generators/BotInventoryGenerator.cs +++ b/Libraries/Core/Generators/BotInventoryGenerator.cs @@ -405,7 +405,7 @@ public class BotInventoryGenerator( var found = false; // Limit attempts to find a compatible item as it's expensive to check them all - var maxAttempts = Math.Round(settings.RootEquipmentPool.Count() * 0.75); // Roughly 75% of pool size + var maxAttempts = Math.Round(settings.RootEquipmentPool.Count * 0.75); // Roughly 75% of pool size var attempts = 0; while (!found) { diff --git a/Libraries/Core/Helpers/BotGeneratorHelper.cs b/Libraries/Core/Helpers/BotGeneratorHelper.cs index dc68043b..c2c7eb9a 100644 --- a/Libraries/Core/Helpers/BotGeneratorHelper.cs +++ b/Libraries/Core/Helpers/BotGeneratorHelper.cs @@ -256,7 +256,7 @@ public class BotGeneratorHelper( maxDurability ); - return new UpdRepairable { Durability = (int)currentDurability, MaxDurability = (int)maxDurability }; + return new UpdRepairable { Durability = Math.Round(currentDurability, 5), MaxDurability = Math.Round(maxDurability, 5) }; } /// @@ -267,12 +267,12 @@ public class BotGeneratorHelper( /// Repairable object private UpdRepairable GenerateArmorRepairableProperties(TemplateItem itemTemplate, string? botRole = null) { - double? maxDurability; - double? currentDurability; + double maxDurability; + double currentDurability; if (itemTemplate.Properties?.ArmorClass == 0) { - maxDurability = itemTemplate.Properties.MaxDurability; - currentDurability = itemTemplate.Properties.MaxDurability; + maxDurability = itemTemplate.Properties.MaxDurability.Value; + currentDurability = itemTemplate.Properties.MaxDurability.Value; } else { @@ -280,11 +280,11 @@ public class BotGeneratorHelper( currentDurability = _durabilityLimitsHelper.GetRandomizedArmorDurability( itemTemplate, botRole, - maxDurability.Value + maxDurability ); } - return new UpdRepairable { Durability = (int)currentDurability!, MaxDurability = (int)maxDurability! }; + return new UpdRepairable { Durability = Math.Round(currentDurability, 5), MaxDurability = Math.Round(maxDurability, 5) }; } /// @@ -387,7 +387,7 @@ public class BotGeneratorHelper( } // Does item being checked get blocked/block existing item - if (itemToEquip.Properties.BlocksFaceCover ?? false) + if (itemToEquip.Properties.BlocksFaceCover.GetValueOrDefault(false)) { var existingFaceCover = itemsEquipped.FirstOrDefault((item) => item.SlotId == "FaceCover"); if (existingFaceCover is not null) @@ -403,7 +403,7 @@ public class BotGeneratorHelper( } // Does item being checked get blocked/block existing item - if (itemToEquip.Properties.BlocksEarpiece ?? false) + if (itemToEquip.Properties.BlocksEarpiece.GetValueOrDefault(false)) { var existingEarpiece = itemsEquipped.FirstOrDefault((item) => item.SlotId == "Earpiece"); if (existingEarpiece is not null) @@ -419,7 +419,7 @@ public class BotGeneratorHelper( } // Does item being checked get blocked/block existing item - if (itemToEquip.Properties.BlocksArmorVest is not null) + if (itemToEquip.Properties.BlocksArmorVest.GetValueOrDefault(false)) { var existingArmorVest = itemsEquipped.FirstOrDefault((item) => item.SlotId == "ArmorVest"); if (existingArmorVest is not null) diff --git a/Libraries/Core/Helpers/DurabilityLimitsHelper.cs b/Libraries/Core/Helpers/DurabilityLimitsHelper.cs index c75f0bfc..34955ea3 100644 --- a/Libraries/Core/Helpers/DurabilityLimitsHelper.cs +++ b/Libraries/Core/Helpers/DurabilityLimitsHelper.cs @@ -39,23 +39,24 @@ public class DurabilityLimitsHelper( public double GetRandomizedMaxArmorDurability(TemplateItem? itemTemplate, string? botRole = null) { var itemMaxDurability = itemTemplate.Properties.MaxDurability.Value; - - if (botRole is not null) + if (botRole is null) { - if (_botHelper.IsBotPmc(botRole)) - { - return GenerateMaxPmcArmorDurability(itemMaxDurability); - } + return itemMaxDurability; + } - if (_botHelper.IsBotBoss(botRole)) - { - return itemMaxDurability; - } + if (_botHelper.IsBotPmc(botRole)) + { + return GenerateMaxPmcArmorDurability(itemMaxDurability); + } - if (_botHelper.IsBotFollower(botRole)) - { - return itemMaxDurability; - } + if (_botHelper.IsBotBoss(botRole)) + { + return itemMaxDurability; + } + + if (_botHelper.IsBotFollower(botRole)) + { + return itemMaxDurability; } return itemMaxDurability; diff --git a/Libraries/Core/Helpers/NotificationSendHelper.cs b/Libraries/Core/Helpers/NotificationSendHelper.cs index b31b4fdb..4c676eb6 100644 --- a/Libraries/Core/Helpers/NotificationSendHelper.cs +++ b/Libraries/Core/Helpers/NotificationSendHelper.cs @@ -14,7 +14,8 @@ public class NotificationSendHelper( IWebSocketConnectionHandler _sptWebSocketConnectionHandler, HashUtil _hashUtil, SaveServer _saveServer, - NotificationService _notificationService + NotificationService _notificationService, + TimeUtil _timeUtil ) { /// @@ -47,7 +48,28 @@ public class NotificationSendHelper( string messageText, MessageType messageType) { - throw new NotImplementedException(); + var dialog = GetDialog(sessionId, messageType, senderDetails); + + dialog.New += 1; + Message message = new Message { + Id = _hashUtil.Generate(), + UserId = dialog.Id, + MessageType = messageType, + DateTime = _timeUtil.GetTimeStamp(), + Text = messageText, + HasRewards = null, + RewardCollected = null, + Items = null, + }; + dialog.Messages.Add(message); + + WsChatMessageReceived notification = new WsChatMessageReceived { + EventType = NotificationEventType.new_message, + EventIdentifier = message.Id, + DialogId = message.UserId, + Message = message, + }; + SendMessage(sessionId, notification); } /// @@ -59,6 +81,26 @@ public class NotificationSendHelper( /// Dialogue protected Models.Eft.Profile.Dialogue GetDialog(string sessionId, MessageType messageType, UserDialogInfo senderDetails) { - throw new NotImplementedException(); + // Use trader id if sender is trader, otherwise use nickname + var key = senderDetails.Id; + var dialogueData = _saveServer.GetProfile(sessionId).DialogueRecords; + var isNewDialogue = dialogueData.ContainsKey(key); + var dialogue = dialogueData[key]; + + // Existing dialog not found, make new one + if (isNewDialogue) { + dialogue = new Models.Eft.Profile.Dialogue { + Id = key, + Type = messageType, + Messages = [], + Pinned = false, + New = 0, + AttachmentsNew = 0, + Users = senderDetails.Info.MemberCategory == MemberCategory.Trader ? null : [senderDetails], + }; + + dialogueData[key] = dialogue; + } + return dialogue; } } diff --git a/Libraries/Core/Services/SeasonalEventService.cs b/Libraries/Core/Services/SeasonalEventService.cs index 43d094af..2b46d5e4 100644 --- a/Libraries/Core/Services/SeasonalEventService.cs +++ b/Libraries/Core/Services/SeasonalEventService.cs @@ -50,6 +50,8 @@ public class SeasonalEventService( ItemTpl.HEADWEAR_MASKA1SCH_BULLETPROOF_HELMET_CHRISTMAS_EDITION, ItemTpl.ARMOR_6B13_M_ASSAULT_ARMOR_CHRISTMAS_EDITION, ItemTpl.HEADWEAR_HAT_WITH_HORNS, + ItemTpl.BARTER_JAR_OF_PICKLES, + ItemTpl.BARTER_OLIVIER_SALAD_BOX ]; protected IReadOnlyList _halloweenEventItems = diff --git a/Libraries/Core/Utils/CompareUtil.cs b/Libraries/Core/Utils/CompareUtil.cs index 6c3cd5ad..4d3892b0 100644 --- a/Libraries/Core/Utils/CompareUtil.cs +++ b/Libraries/Core/Utils/CompareUtil.cs @@ -5,8 +5,55 @@ namespace Core.Utils; [Injectable] public class CompareUtil { + private readonly HashSet typesToCheckAgainst = [ + "string", + "number", + "boolean", + "bigint", + "symbol", + "undefined", + "null", + ]; + + /** + * This function does an object comparison, equivalent to applying reflections + * and scanning for all possible properties including arrays. + * @param v1 value 1 to compare + * @param v2 value 2 to compare + * @returns true if equal, false if not + */ public bool RecursiveCompare(object v1, object v2) { + // const typeOfv1 = typeof v1; + // const typeOfv2 = typeof v2; + // if (CompareUtil.typesToCheckAgainst.has(typeOfv1)) { + // return v1 === v2; + // } + // if (typeOfv1 === "object" && typeOfv2 === "object") { + // if (Array.isArray(v1)) { + // if (!Array.isArray(v2)) { + // return false; + // } + // const arr1 = v1 as Array; + // const arr2 = v2 as Array; + // if (arr1.length !== arr2.length) { + // return false; + // } + // return arr1.every((vOf1) => arr2.find((vOf2) => this.recursiveCompare(vOf1, vOf2))); + // } + // for (const propOf1 in v1) { + // if (v2[propOf1] === undefined) { + // return false; + // } + // return this.recursiveCompare(v1[propOf1], v2[propOf1]); + // } + // } + // if (typeOfv1 === typeOfv2) { + // return v1 === v2; + // } + // + // return false; + throw new NotImplementedException(); } } diff --git a/Server/Properties/launchSettings.json b/Server/Properties/launchSettings.json index 541f3861..88fa70a7 100644 --- a/Server/Properties/launchSettings.json +++ b/Server/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Spt Server Debug": { "commandName": "Project", - "hotReloadEnabled": true, + "hotReloadEnabled": false, "workingDirectory": "$(OutputPath)" } }