diff --git a/ConsoleCommands/BaseTemplateCommand.cs b/ConsoleCommands/BaseTemplateCommand.cs index 8a8b7519..4c0562af 100644 --- a/ConsoleCommands/BaseTemplateCommand.cs +++ b/ConsoleCommands/BaseTemplateCommand.cs @@ -12,7 +12,7 @@ internal abstract class BaseTemplateCommand : ConsoleCommandWithArgument { public override string Pattern => RequiredArgumentPattern; - protected static IEnumerable FindTemplates(string searchShortNameOrTemplateId) + protected static ItemTemplate[] FindTemplates(string searchShortNameOrTemplateId) { if (!Singleton.Instantiated) return []; @@ -29,6 +29,7 @@ protected static IEnumerable FindTemplates(string searchShortNameO return templates .Values .Where(t => t.ShortNameLocalizationKey.Localized().IndexOf(searchShortNameOrTemplateId, StringComparison.OrdinalIgnoreCase) >= 0 - || t.NameLocalizationKey.Localized().IndexOf(searchShortNameOrTemplateId, StringComparison.OrdinalIgnoreCase) >= 0); + || t.NameLocalizationKey.Localized().IndexOf(searchShortNameOrTemplateId, StringComparison.OrdinalIgnoreCase) >= 0) + .ToArray(); } } diff --git a/ConsoleCommands/Spawn.cs b/ConsoleCommands/Spawn.cs index e399b29f..0f1f0c65 100644 --- a/ConsoleCommands/Spawn.cs +++ b/ConsoleCommands/Spawn.cs @@ -1,13 +1,17 @@ -using System.Linq; +using System; +using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using Comfort.Common; using Diz.Utils; using EFT.CameraControl; +using EFT.InventoryLogic; using EFT.Trainer.Extensions; using EFT.Trainer.Features; using EFT.Trainer.Properties; using JetBrains.Annotations; +using UnityEngine; +using Random = UnityEngine.Random; #nullable enable @@ -29,7 +33,7 @@ public override void Execute(Match match) return; var search = matchGroup.Value; - var templates = FindTemplates(search).ToArray(); + var templates = FindTemplates(search); switch (templates.Length) { @@ -45,25 +49,41 @@ public override void Execute(Match match) } var tpl = templates[0]; + SpawnTemplate(tpl, player, this); + } + + internal static void SpawnTemplate(string template, Player player, ConsoleCommand command, Func filter) + { + var result = FindTemplates(template) + .FirstOrDefault(filter); + + if (result == null) + return; + + SpawnTemplate(result, player, command); + } + + private static void SpawnTemplate(ItemTemplate template, Player player, ConsoleCommand command) + { var poolManager = Singleton.Instance; poolManager - .LoadBundlesAndCreatePools(PoolManager.PoolsCategory.Raid, PoolManager.AssemblyType.Online, [.. tpl.AllResources], JobPriority.Immediate) + .LoadBundlesAndCreatePools(PoolManager.PoolsCategory.Raid, PoolManager.AssemblyType.Online, [.. template.AllResources], JobPriority.Immediate) .ContinueWith(task => { AsyncWorker.RunInMainTread(delegate { if (task.IsFaulted) { - AddConsoleLog(Strings.ErrorFailedToLoadItemBundle.Red()); + command.AddConsoleLog(Strings.ErrorFailedToLoadItemBundle.Red()); } else { var itemFactory = Singleton.Instance; - var item = itemFactory.CreateItem(MongoID.Generate(), tpl._id, null); + var item = itemFactory.CreateItem(MongoID.Generate(), template._id, null); if (item == null) { - AddConsoleLog(Strings.ErrorFailedToCreateItem.Red()); + command.AddConsoleLog(Strings.ErrorFailedToCreateItem.Red()); } else { @@ -74,7 +94,14 @@ public override void Execute(Match match) go.SetActive(value: true); var lootItem = Singleton.Instance.CreateLootWithRigidbody(go, item, item.ShortName, Singleton.Instance, randomRotation: false, null, out _); - lootItem.transform.SetPositionAndRotation(player.Transform.position + player.Transform.forward * 2f + player.Transform.up * 0.5f, player.Transform.rotation); + + var transform = player.Transform; + var position = transform.position + + transform.right * Random.Range(-1f, 1f) + + transform.forward * 2f + + transform.up * 0.5f; + + lootItem.transform.SetPositionAndRotation(position, transform.rotation); lootItem.LastOwner = player; } } diff --git a/ConsoleCommands/SpawnBot.cs b/ConsoleCommands/SpawnBot.cs index ce517c36..50e9106b 100644 --- a/ConsoleCommands/SpawnBot.cs +++ b/ConsoleCommands/SpawnBot.cs @@ -11,10 +11,11 @@ namespace EFT.Trainer.ConsoleCommands; [UsedImplicitly] -internal class SpawnBot : BaseTemplateCommand +internal class SpawnBot : ConsoleCommandWithArgument { public const string MatchAll = "*"; + public override string Pattern => RequiredArgumentPattern; public override string Name => Strings.CommandSpawnBot; public override void Execute(Match match) diff --git a/ConsoleCommands/SpawnQuestItems.cs b/ConsoleCommands/SpawnQuestItems.cs new file mode 100644 index 00000000..28ec0238 --- /dev/null +++ b/ConsoleCommands/SpawnQuestItems.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Linq; +using EFT.Quests; +using EFT.Trainer.Extensions; +using EFT.Trainer.Features; +using EFT.Trainer.Properties; +using JetBrains.Annotations; +using UnityEngine; + +#nullable enable + +namespace EFT.Trainer.ConsoleCommands; + +[UsedImplicitly] +internal class SpawnQuestItems : ConsoleCommandWithoutArgument +{ + public override string Name => Strings.CommandSpawnQuestItems; + + public override void Execute() + { + var player = GameState.Current?.LocalPlayer; + if (!player.IsValid()) + return; + + var profile = player.Profile; + + var startedQuests = profile.QuestsData + .Where(q => q.Status is EQuestStatus.Started && q.Template != null) + .ToArray(); + + if (!startedQuests.Any()) + return; + + foreach (var quest in startedQuests) + { + foreach (var condition in GetConditions(quest)) + { + var count = Mathf.RoundToInt(condition.value); + if (count is <= 0 or > 20) + continue; + + foreach (var target in condition.target) + { + for (var i = 0; i < count; i++) + Spawn.SpawnTemplate(target, player, this, t => !t.QuestItem); // for now we are only able to spawn non location-specific items like batteries, meds, keys, etc. + } + } + } + } + + private static IEnumerable GetConditions(QuestDataClass quest) + { + // do we need to add ConditionMultipleTargets / ConditionItem / ConditionPlaceItem + var conditions = quest.Template!.Conditions[EQuestStatus.AvailableForFinish]; + return conditions.OfType(); + } +} diff --git a/NLog.EFT.Trainer.csproj b/NLog.EFT.Trainer.csproj index dc68becd..6efbaf83 100644 --- a/NLog.EFT.Trainer.csproj +++ b/NLog.EFT.Trainer.csproj @@ -131,6 +131,7 @@ + diff --git a/Properties/Strings.Designer.cs b/Properties/Strings.Designer.cs index 30ea35fe..d1502a1b 100644 --- a/Properties/Strings.Designer.cs +++ b/Properties/Strings.Designer.cs @@ -260,6 +260,15 @@ internal static string CommandSpawnBotEnumerateFormat { } } + /// + /// Looks up a localized string similar to spawnqi. + /// + internal static string CommandSpawnQuestItems { + get { + return ResourceManager.GetString("CommandSpawnQuestItems", resourceCulture); + } + } + /// /// Looks up a localized string similar to status. /// diff --git a/Properties/Strings.fr.resx b/Properties/Strings.fr.resx index 3c94a45f..d1329f57 100644 --- a/Properties/Strings.fr.resx +++ b/Properties/Strings.fr.resx @@ -934,4 +934,7 @@ Les couleurs sont stockées sous la forme d'un tableau de valeurs flottantes 'RG {0} + + spawnqi + \ No newline at end of file diff --git a/Properties/Strings.resx b/Properties/Strings.resx index f389f4f8..cc86fb41 100644 --- a/Properties/Strings.resx +++ b/Properties/Strings.resx @@ -934,4 +934,7 @@ Colors are stored as an array of 'RGBA' floats {0} + + spawnqi + \ No newline at end of file diff --git a/Properties/Strings.zh-cn.resx b/Properties/Strings.zh-cn.resx index 66b3b4d8..3e34b1ab 100644 --- a/Properties/Strings.zh-cn.resx +++ b/Properties/Strings.zh-cn.resx @@ -935,4 +935,7 @@ {0} + + spawnqi + \ No newline at end of file diff --git a/README.md b/README.md index 898ce0dd..e09ca801 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,7 @@ This trainer hooks into the command system, so you can easily setup features usi | savetl | `[filename]` | | Save current tracklist to file | | spawn | `[name]` | | Spawn object in front of player | | spawnbot | `[name]` or `*` | | Spawn a bot, ex `spawnbot bossKilla` | +| spawnqi | | | Spawn items-to-find in active quests | | stamina | `on` or `off` | `off` | Enable/Disable unlimited stamina | | stash | `on` or `off` | `off` | Show/Hide stashes | | status | | | Show status of all features |