diff --git a/Libraries/Core/Helpers/Dialogue/Commando/SptCommands/GiveCommand/GiveSptCommand.cs b/Libraries/Core/Helpers/Dialogue/Commando/SptCommands/GiveCommand/GiveSptCommand.cs index 5493cd88..0ed6704f 100644 --- a/Libraries/Core/Helpers/Dialogue/Commando/SptCommands/GiveCommand/GiveSptCommand.cs +++ b/Libraries/Core/Helpers/Dialogue/Commando/SptCommands/GiveCommand/GiveSptCommand.cs @@ -1,13 +1,42 @@ -using SptCommon.Annotations; +using System.Text.RegularExpressions; using Core.Models.Eft.Common.Tables; using Core.Models.Eft.Dialog; using Core.Models.Eft.Profile; +using Core.Models.Enums; +using Core.Models.Utils; +using Core.Services; +using Core.Utils; +using Core.Utils.Cloners; +using SptCommon.Annotations; -namespace Core.Helpers.Dialog.Commando.SptCommands.GiveCommand; +namespace Core.Helpers.Dialogue.Commando.SptCommands.GiveCommand; [Injectable] -public class GiveSptCommand +public class GiveSptCommand( + ISptLogger _logger, + HashUtil _hashUtil, + DatabaseService _databaseService, + ItemHelper _itemHelper, + PresetHelper _presetHelper, + ItemFilterService _itemFilterService, + MailSendService _mailSendService, + LocaleService _localeService, + ICloner _cloner + +) { + protected Dictionary _savedCommand = new(); + //private const Regex _commandRegex = new Regex(@"/^spt give(((([a - z]{ 2,5}) )?")(.+)"|\w+) )?([0 - 9]+)$/"; + private const double _acceptableConfidence = 0.9d; + + // Exception for flares + protected readonly HashSet _excludedPresetItems = + [ + ItemTpl.FLARE_RSP30_REACTIVE_SIGNAL_CARTRIDGE_RED, + ItemTpl.FLARE_RSP30_REACTIVE_SIGNAL_CARTRIDGE_GREEN, + ItemTpl.FLARE_RSP30_REACTIVE_SIGNAL_CARTRIDGE_YELLOW + ]; + public string GetCommand() { return "give"; @@ -15,14 +44,198 @@ public class GiveSptCommand public string GetCommandHelp() { - return "spt give\n========\nSends items to the player through the message system.\n\n\tspt give [template ID] [quantity]\n\t\tEx: " + - "spt give 544fb25a4bdc2dfb738b4567 2\n\n\tspt give [\"item name\"] [quantity]\n\t\tEx: spt give \"pack of sugar\" 10\n\n\tspt " + - "give [locale] [\"item name\"] [quantity]\n\t\tEx: spt give fr \"figurine de chat\" 3"; + return + "spt give\n========\nSends items to the player through the message system.\n\n\tspt give [template ID] [quantity]\n\t\tEx: " + + "spt give 544fb25a4bdc2dfb738b4567 2\n\n\tspt give [\"item name\"] [quantity]\n\t\tEx: spt give \"pack of sugar\" 10\n\n\tspt " + + "give [locale] [\"item name\"] [quantity]\n\t\tEx: spt give fr \"figurine de chat\" 3"; } public string PerformAction(UserDialogInfo commandHandler, string sessionId, SendMessageRequest request) { throw new NotImplementedException(); + /** + + if (!_commandRegex.IsMatch(request.Text)) { + _mailSendService.SendUserMessageToPlayer( + sessionId, + commandHandler, + "Invalid use of give command. Use 'help' for more information."); + return request.DialogId; + } + + var result = _commandRegex.Match(request.Text); + + string item; + int quantity; + bool isItemName; + string? locale = null; + Dictionary? localizedGlobal = null; + + // This is a reply to a give request previously made pending a reply + if (result.Groups[1].Value == null) { + if (!_savedCommand.ContainsKey(sessionId)) { + _mailSendService.SendUserMessageToPlayer( + sessionId, + commandHandler, + "Invalid use of give command. Use 'help' for more information." + ); + return request.DialogId; + } + _savedCommand.TryGetValue(sessionId, out var savedCommand); + var locationSixValue = +int.Parse( result.Groups[6].Value); + if (locationSixValue > savedCommand.PotentialItemNames.Count) { + _mailSendService.SendUserMessageToPlayer( + sessionId, + commandHandler, + "Invalid selection. Outside of bounds! Use 'help' for more information."); + return request.DialogId; + } + item = savedCommand.PotentialItemNames[locationSixValue - 1]; + quantity = savedCommand.Quantity; + locale = savedCommand.Locale; + isItemName = true; + _savedCommand.Remove(sessionId); + } else { + // A new give request was entered, we need to ignore the old saved command + if (_savedCommand.ContainsKey(sessionId)) { + _savedCommand.Remove(sessionId); + } + isItemName = result.Groups[5].Value != null; + item = result.Groups[5].Value is not null ? result.Groups[5].Value : result.Groups[2].Value; + quantity = +int.Parse(result.Groups[6].Value); + if (quantity <= 0) { + _mailSendService.SendUserMessageToPlayer( + sessionId, + commandHandler, + "Invalid quantity! Must be 1 or higher. Use 'help' for more information."); + return request.DialogId; + } + + if (isItemName) { + try { + locale = result.Groups[4] ?? _localeService.GetDesiredGameLocale() ?? "en"; + } catch (Exception ex) { + _mailSendService.SendUserMessageToPlayer( + sessionId, + commandHandler, + $"An error occurred while trying to use localized text. Locale will be defaulted to 'en'. {ex.Message}"); + + _logger.Warning(ex.Message); + locale = "en"; + } + + localizedGlobal = GetGlobalsLocale(locale); + + var closestItemsMatchedByName = _itemHelper + .GetItems() + .Where((i) => IsItemAllowed(i)) + .Select((i) => localizedGlobal[$"{i?.Id} Name"]?.ToLower() ?? i.Properties.Name) + .Where((i) => !string.IsNullOrEmpty(i)) + .Select((i) => ({ Match = StringSimilarity(item.ToLower(), i.ToLower())ItemName = i })) + .Sort((a1, a2) => a2.match - a1.match); + + if (closestItemsMatchedByName[0].match >= _acceptableConfidence) { + item = closestItemsMatchedByName[0].ItemName; + } else { + var i = 1; + var slicedItems = closestItemsMatchedByName.Slice(0, 10); + // max 10 item names and map them + var itemList = slicedItems + .map((match) => $"{i++}. {match.ItemName} (conf: ${(match.match * 100).toFixed(2)})") + .Join("\n"); + _savedCommand.Add( + sessionId, + new SavedCommand( + quantity, + slicedItems.map((item) => item.ItemName), + locale)); + _mailSendService.SendUserMessageToPlayer( + sessionId, + commandHandler, + "Could not find exact match. Closest matches are:\n\n${itemList}\n\nUse 'spt give [number]' to select one."); + + return request.DialogId; + } + } + } + + localizedGlobal ??= GetGlobalsLocale(locale ?? "en"); + // If item is an item name, we need to search using that item name and the locale which one we want otherwise + // item is just the tplId. + var tplId = isItemName + ? _itemHelper + .GetItems() + .Where((i) => IsItemAllowed(i)) + .FirstOrDefault((i) => (localizedGlobal[$"{i?.Id} Name"]?.ToLower() ?? i.Properties.Name) == item).Id + : item; + + var checkedItem = _itemHelper.GetItem(tplId); + if (!checkedItem.Key) { + _mailSendService.SendUserMessageToPlayer( + sessionId, + commandHandler, + "That item could not be found. Please refine your request and try again."); + return request.DialogId; + } + + List itemsToSend = []; + var preset = _presetHelper.GetDefaultPreset(checkedItem.Value.Id); + if (preset is not null && !_excludedPresetItems.Contains(checkedItem.Value.Id)) { + for (var i = 0; i < quantity; i++) { + var items = _cloner.Clone(preset.Items); + items = _itemHelper.ReplaceIDs(items); + itemsToSend.AddRange(items); + } + } else if (_itemHelper.IsOfBaseclass(checkedItem.Value.Id, BaseClasses.AMMO_BOX)) { + for (var i = 0; i < quantity; i++) { + List ammoBoxArray = []; + ammoBoxArray.Add( new Item{ Id = _hashUtil.Generate(), Template = checkedItem.Value.Id }); + // DO NOT generate the ammo box cartridges, the mail service does it for us! :) + // _itemHelper.addCartridgesToAmmoBox(ammoBoxArray, checkedItem[1]); + itemsToSend.AddRange(ammoBoxArray); + } + } else { + if (checkedItem.Value.Properties.StackMaxSize == 1) { + for (var i = 0; i < quantity; i++) { + itemsToSend.Add( new Item{ + Id = _hashUtil.Generate(), + Template = checkedItem.Value.Id, + Upd = _itemHelper.generateUpdForItem(checkedItem.Value) }); + } + } else { + var itemToSend = new Item{ + Id = _hashUtil.Generate(), + Template = checkedItem.Value.Id, + Upd = _itemHelper.generateUpdForItem(checkedItem.Value), + }; + itemToSend.Upd.StackObjectsCount = quantity; + try { + itemsToSend.AddRange(_itemHelper.SplitStack(itemToSend)); + } catch { + _mailSendService.SendUserMessageToPlayer( + sessionId, + commandHandler, + "Too many items requested. Please lower the amount and try again."); + + return request.DialogId; + } + } + } + + // Flag the items as FiR + _itemHelper.SetFoundInRaid(itemsToSend); + + _mailSendService.SendSystemMessageToPlayer(sessionId, "SPT GIVE", itemsToSend); + return request.DialogId; + + */ + } + + protected Dictionary GetGlobalsLocale(string desiredLocale) + { + return _databaseService.GetLocales().Global.TryGetValue(desiredLocale, out var locale) + ? locale.Value + : _databaseService.GetLocales().Global["en"].Value; } /** @@ -32,6 +245,16 @@ public class GiveSptCommand */ protected bool IsItemAllowed(TemplateItem templateItem) { - throw new NotImplementedException(); + return templateItem.Type != "Node" && + !_itemHelper.IsQuestItem(templateItem.Id) && + !_itemFilterService.IsItemBlacklisted(templateItem.Id) && + (templateItem.Properties?.Prefab?.Path ?? "") != "" && + !_itemHelper.IsOfBaseclasses( + templateItem.Id, + [ + BaseClasses.HIDEOUT_AREA_CONTAINER, BaseClasses.LOOT_CONTAINER, + BaseClasses.RANDOM_LOOT_CONTAINER, BaseClasses.MOB_CONTAINER + ] + ); } }