Add 'action' for using weapon skills/abilities on targets; part of healer role quests
authorLiza Carvelli <liza@carvel.li>
Wed, 10 Jul 2024 19:01:41 +0000 (21:01 +0200)
committerLiza Carvelli <liza@carvel.li>
Wed, 10 Jul 2024 19:01:41 +0000 (21:01 +0200)
14 files changed:
QuestPathGenerator/QuestSourceGenerator.cs
QuestPaths/Dawntrail/RoleQuests/Healer/4824_In the Sting of Things.json
QuestPaths/Dawntrail/RoleQuests/Healer/4825_Causing Problems on Purpose.json [new file with mode: 0644]
QuestPaths/Dawntrail/RoleQuests/Healer/4826_Living among the Deadly.json [new file with mode: 0644]
QuestPaths/Dawntrail/RoleQuests/Healer/4827_Taste of a Toxin Paradise.json [new file with mode: 0644]
QuestPaths/quest-v1.json
Questionable.Model/V1/Converter/ActionConverter.cs [new file with mode: 0644]
Questionable.Model/V1/Converter/InteractionTypeConverter.cs
Questionable.Model/V1/EAction.cs [new file with mode: 0644]
Questionable.Model/V1/EInteractionType.cs
Questionable.Model/V1/QuestStep.cs
Questionable/Controller/Steps/InteractionFactory/Action.cs [new file with mode: 0644]
Questionable/GameFunctions.cs
Questionable/QuestionablePlugin.cs

index 060d62540835aca28559e1efc4c2388cbfdb7394..be47a0be4c4e0f7da3043b42db28632628145eb3 100644 (file)
@@ -332,6 +332,8 @@ public class QuestSourceGenerator : ISourceGenerator
                                             Assignment(nameof(QuestStep.ChatMessage), step.ChatMessage,
                                                     emptyStep.ChatMessage)
                                                 .AsSyntaxNodeOrToken(),
+                                            Assignment(nameof(QuestStep.Action), step.Action, emptyStep.Action)
+                                                .AsSyntaxNodeOrToken(),
                                             Assignment(nameof(QuestStep.EnemySpawnType), step.EnemySpawnType,
                                                     emptyStep.EnemySpawnType)
                                                 .AsSyntaxNodeOrToken(),
index 777a0ba07a963c4ae1cea7c895665191bd27e7ab..f1687aa70d8dc740655b17c03b4d6aae0dcb9b61 100644 (file)
           "InteractionType": "AcceptQuest"
         }
       ]
+    },
+    {
+      "Sequence": 1,
+      "Steps": [
+        {
+          "DataId": 1046291,
+          "Position": {
+            "X": -409.07916,
+            "Y": 3.9999695,
+            "Z": 14.846985
+          },
+          "TerritoryId": 129,
+          "InteractionType": "Interact",
+          "AetheryteShortcut": "Limsa Lominsa",
+          "AethernetShortcut": [
+            "[Limsa Lominsa] Aetheryte Plaza",
+            "[Limsa Lominsa] Arcanists' Guild"
+          ]
+        }
+      ]
+    },
+    {
+      "Sequence": 2,
+      "Steps": [
+        {
+          "Position": {
+            "X": -387.69412,
+            "Y": 5.999984,
+            "Z": 41.170013
+          },
+          "TerritoryId": 129,
+          "InteractionType": "WalkTo"
+        },
+        {
+          "DataId": 1046292,
+          "Position": {
+            "X": -195.36127,
+            "Y": 19.999954,
+            "Z": 112.962524
+          },
+          "TerritoryId": 129,
+          "InteractionType": "Interact",
+          "AethernetShortcut": [
+            "[Limsa Lominsa] Arcanists' Guild",
+            "[Limsa Lominsa] Hawkers' Alley"
+          ]
+        }
+      ]
+    },
+    {
+      "Sequence": 3,
+      "Steps": [
+        {
+          "DataId": 1001540,
+          "Position": {
+            "X": -202.68567,
+            "Y": 16,
+            "Z": 56.99243
+          },
+          "TerritoryId": 129,
+          "InteractionType": "Interact",
+          "CompletionQuestVariablesFlags": [
+            null,
+            null,
+            null,
+            null,
+            null,
+            64
+          ]
+        },
+        {
+          "DataId": 1003272,
+          "Position": {
+            "X": -262.92822,
+            "Y": 16.2,
+            "Z": 51.407593
+          },
+          "TerritoryId": 129,
+          "InteractionType": "Interact",
+          "CompletionQuestVariablesFlags": [
+            null,
+            null,
+            null,
+            null,
+            null,
+            32
+          ]
+        },
+        {
+          "DataId": 1003277,
+          "Position": {
+            "X": -136.67511,
+            "Y": 18.2,
+            "Z": 16.494995
+          },
+          "TerritoryId": 129,
+          "InteractionType": "Interact",
+          "CompletionQuestVariablesFlags": [
+            null,
+            null,
+            null,
+            null,
+            null,
+            128
+          ]
+        }
+      ]
+    },
+    {
+      "Sequence": 4,
+      "Steps": [
+        {
+          "DataId": 1046293,
+          "Position": {
+            "X": -143.35852,
+            "Y": 3.9999998,
+            "Z": 189.6543
+          },
+          "TerritoryId": 129,
+          "InteractionType": "Interact",
+          "AethernetShortcut": [
+            "[Limsa Lominsa] Aetheryte Plaza",
+            "[Limsa Lominsa] Fishermens' Guild"
+          ]
+        }
+      ]
+    },
+    {
+      "Sequence": 5,
+      "Steps": [
+        {
+          "DataId": 1046294,
+          "Position": {
+            "X": -115.19043,
+            "Y": 20,
+            "Z": 111.95532
+          },
+          "StopDistance": 1,
+          "TerritoryId": 129,
+          "InteractionType": "Interact"
+        }
+      ]
+    },
+    {
+      "Sequence": 6,
+      "Steps": [
+        {
+          "DataId": 1046296,
+          "Position": {
+            "X": -114.42743,
+            "Y": 20,
+            "Z": 111.283936
+          },
+          "StopDistance": 10,
+          "TerritoryId": 129,
+          "InteractionType": "Action",
+          "Action": "Esuna"
+        }
+      ]
+    },
+    {
+      "Sequence": 255,
+      "Steps": [
+        {
+          "DataId": 1046290,
+          "Position": {
+            "X": -114.091736,
+            "Y": 20,
+            "Z": 111.436646
+          },
+          "TerritoryId": 129,
+          "InteractionType": "CompleteQuest"
+        }
+      ]
     }
   ]
 }
diff --git a/QuestPaths/Dawntrail/RoleQuests/Healer/4825_Causing Problems on Purpose.json b/QuestPaths/Dawntrail/RoleQuests/Healer/4825_Causing Problems on Purpose.json
new file mode 100644 (file)
index 0000000..b3fdcec
--- /dev/null
@@ -0,0 +1,123 @@
+{
+  "$schema": "https://carvel.li/questionable/quest-1.0",
+  "Author": "liza",
+  "QuestSequence": [
+    {
+      "Sequence": 0,
+      "Steps": [
+        {
+          "DataId": 1046290,
+          "Position": {
+            "X": -114.091736,
+            "Y": 20,
+            "Z": 111.436646
+          },
+          "TerritoryId": 129,
+          "InteractionType": "AcceptQuest"
+        }
+      ]
+    },
+    {
+      "Sequence": 1,
+      "Steps": [
+        {
+          "DataId": 1047092,
+          "Position": {
+            "X": 297.38306,
+            "Y": -33.02986,
+            "Z": 284.99268
+          },
+          "TerritoryId": 138,
+          "InteractionType": "Interact",
+          "AetheryteShortcut": "Western La Noscea - Aleport"
+        }
+      ]
+    },
+    {
+      "Sequence": 2,
+      "Steps": [
+        {
+          "DataId": 1046297,
+          "Position": {
+            "X": 211.68835,
+            "Y": -25.006758,
+            "Z": 230.85376
+          },
+          "TerritoryId": 138,
+          "InteractionType": "Interact",
+          "Fly": true
+        }
+      ]
+    },
+    {
+      "Sequence": 3,
+      "Steps": [
+        {
+          "Position": {
+            "X": 465.86716,
+            "Y": 11.231914,
+            "Z": 326.10486
+          },
+          "StopDistance": 0.25,
+          "TerritoryId": 138,
+          "InteractionType": "Combat",
+          "EnemySpawnType": "AutoOnEnterArea",
+          "KillEnemyDataIds": [
+            17612,
+            17613
+          ],
+          "Fly": true
+        }
+      ]
+    },
+    {
+      "Sequence": 4,
+      "Steps": [
+        {
+          "DataId": 1046302,
+          "Position": {
+            "X": 465.50684,
+            "Y": 11.444184,
+            "Z": 330.89185
+          },
+          "StopDistance": 7,
+          "TerritoryId": 138,
+          "InteractionType": "Interact"
+        }
+      ]
+    },
+    {
+      "Sequence": 5,
+      "Steps": [
+        {
+          "DataId": 1046303,
+          "Position": {
+            "X": 462.39404,
+            "Y": 11.569952,
+            "Z": 329.57947
+          },
+          "StopDistance": 10,
+          "TerritoryId": 138,
+          "InteractionType": "Action",
+          "Action": "Esuna"
+        }
+      ]
+    },
+    {
+      "Sequence": 255,
+      "Steps": [
+        {
+          "DataId": 1046290,
+          "Position": {
+            "X": -114.091736,
+            "Y": 20,
+            "Z": 111.436646
+          },
+          "TerritoryId": 129,
+          "InteractionType": "CompleteQuest",
+          "AetheryteShortcut": "Limsa Lominsa"
+        }
+      ]
+    }
+  ]
+}
diff --git a/QuestPaths/Dawntrail/RoleQuests/Healer/4826_Living among the Deadly.json b/QuestPaths/Dawntrail/RoleQuests/Healer/4826_Living among the Deadly.json
new file mode 100644 (file)
index 0000000..30debaf
--- /dev/null
@@ -0,0 +1,107 @@
+{
+  "$schema": "https://carvel.li/questionable/quest-1.0",
+  "Author": "liza",
+  "QuestSequence": [
+    {
+      "Sequence": 0,
+      "Steps": [
+        {
+          "DataId": 1046290,
+          "Position": {
+            "X": -114.091736,
+            "Y": 20,
+            "Z": 111.436646
+          },
+          "TerritoryId": 129,
+          "InteractionType": "AcceptQuest"
+        }
+      ]
+    },
+    {
+      "Sequence": 1,
+      "Steps": [
+        {
+          "DataId": 1046307,
+          "Position": {
+            "X": 216.84595,
+            "Y": 14.096056,
+            "Z": 660.3646
+          },
+          "TerritoryId": 135,
+          "InteractionType": "Interact",
+          "AetheryteShortcut": "Lower La Noscea - Moraby Drydocks"
+        }
+      ]
+    },
+    {
+      "Sequence": 2,
+      "Steps": [
+        {
+          "DataId": 1046309,
+          "Position": {
+            "X": 106.7063,
+            "Y": 22.880846,
+            "Z": 618.4634
+          },
+          "TerritoryId": 135,
+          "InteractionType": "Interact",
+          "Fly": true
+        }
+      ]
+    },
+    {
+      "Sequence": 3,
+      "Steps": [
+        {
+          "DataId": 1046308,
+          "Position": {
+            "X": 217.39526,
+            "Y": 14.096056,
+            "Z": 658.7776
+          },
+          "TerritoryId": 135,
+          "InteractionType": "Interact",
+          "Fly": true
+        }
+      ]
+    },
+    {
+      "Sequence": 4,
+      "Steps": [
+        {
+          "DataId": 1046307,
+          "Position": {
+            "X": 216.84595,
+            "Y": 14.096056,
+            "Z": 660.3646
+          },
+          "TerritoryId": 135,
+          "InteractionType": "Interact",
+          "DialogueChoices": [
+            {
+              "Type": "List",
+              "Prompt": "TEXT_KINGBA221_04826_Q1_000_048",
+              "Answer": "TEXT_KINGBA221_04826_A1_000_002"
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "Sequence": 255,
+      "Steps": [
+        {
+          "DataId": 1046290,
+          "Position": {
+            "X": -114.091736,
+            "Y": 20,
+            "Z": 111.436646
+          },
+          "TerritoryId": 129,
+          "InteractionType": "CompleteQuest",
+          "AetheryteShortcut": "Limsa Lominsa"
+        }
+      ]
+    }
+  ]
+}
diff --git a/QuestPaths/Dawntrail/RoleQuests/Healer/4827_Taste of a Toxin Paradise.json b/QuestPaths/Dawntrail/RoleQuests/Healer/4827_Taste of a Toxin Paradise.json
new file mode 100644 (file)
index 0000000..b49047e
--- /dev/null
@@ -0,0 +1,145 @@
+{
+  "$schema": "https://carvel.li/questionable/quest-1.0",
+  "Author": "liza",
+  "QuestSequence": [
+    {
+      "Sequence": 0,
+      "Steps": [
+        {
+          "DataId": 1046290,
+          "Position": {
+            "X": -114.091736,
+            "Y": 20,
+            "Z": 111.436646
+          },
+          "TerritoryId": 129,
+          "InteractionType": "AcceptQuest"
+        }
+      ]
+    },
+    {
+      "Sequence": 1,
+      "Steps": [
+        {
+          "DataId": 1046310,
+          "Position": {
+            "X": 268.39087,
+            "Y": -25,
+            "Z": 264.05737
+          },
+          "TerritoryId": 138,
+          "InteractionType": "Interact",
+          "AetheryteShortcut": "Western La Noscea - Aleport"
+        }
+      ]
+    },
+    {
+      "Sequence": 2,
+      "Steps": [
+        {
+          "DataId": 1046311,
+          "Position": {
+            "X": 384.60352,
+            "Y": 0.14576934,
+            "Z": 74.32666
+          },
+          "TerritoryId": 139,
+          "InteractionType": "Interact",
+          "AetheryteShortcut": "Upper La Noscea - Camp Bronze Lake"
+        }
+      ]
+    },
+    {
+      "Sequence": 3,
+      "Steps": [
+        {
+          "DataId": 1046314,
+          "Position": {
+            "X": 457.60278,
+            "Y": 4.1072555,
+            "Z": 103.89868
+          },
+          "TerritoryId": 139,
+          "InteractionType": "Action",
+          "Action": "Esuna",
+          "CompletionQuestVariablesFlags": [
+            null,
+            null,
+            null,
+            null,
+            null,
+            32
+          ]
+        },
+        {
+          "DataId": 1046313,
+          "Position": {
+            "X": 432.6084,
+            "Y": 8.108173,
+            "Z": 133.80627
+          },
+          "TerritoryId": 139,
+          "InteractionType": "Action",
+          "Action": "Esuna",
+          "CompletionQuestVariablesFlags": [
+            null,
+            null,
+            null,
+            null,
+            null,
+            64
+          ]
+        },
+        {
+          "DataId": 1046312,
+          "Position": {
+            "X": 413.0464,
+            "Y": 3.616333,
+            "Z": 113.969604
+          },
+          "TerritoryId": 139,
+          "InteractionType": "Action",
+          "Action": "Esuna",
+          "CompletionQuestVariablesFlags": [
+            null,
+            null,
+            null,
+            null,
+            null,
+            128
+          ]
+        }
+      ]
+    },
+    {
+      "Sequence": 4,
+      "Steps": [
+        {
+          "DataId": 1046316,
+          "Position": {
+            "X": 415.8236,
+            "Y": 8.12099,
+            "Z": 40.72632
+          },
+          "TerritoryId": 139,
+          "InteractionType": "Interact"
+        }
+      ]
+    },
+    {
+      "Sequence": 255,
+      "Steps": [
+        {
+          "DataId": 1046290,
+          "Position": {
+            "X": -114.091736,
+            "Y": 20,
+            "Z": 111.436646
+          },
+          "TerritoryId": 129,
+          "InteractionType": "CompleteQuest"
+        }
+      ]
+    }
+  ]
+}
index 0ebe0e6333844bb96d03e4c67c241af1e4799852..ee2996d4bb212b81a316ddbb16ff8f28ce17f6da 100644 (file)
@@ -96,6 +96,7 @@
                     "EquipItem",
                     "Say",
                     "Emote",
+                    "Action",
                     "WaitForNpcAtPosition",
                     "WaitForManualProgress",
                     "Duty",
                     ]
                   }
                 },
+                {
+                  "if": {
+                    "properties": {
+                      "InteractionType": {
+                        "const": "Action"
+                      }
+                    }
+                  },
+                  "then": {
+                    "properties": {
+                      "Action": {
+                        "type": "string",
+                        "description": "The action to use",
+                        "enum": [
+                          "Esuna"
+                        ]
+                      }
+                    },
+                    "required": [
+                      "Action"
+                    ]
+                  }
+                },
                 {
                   "if": {
                     "properties": {
diff --git a/Questionable.Model/V1/Converter/ActionConverter.cs b/Questionable.Model/V1/Converter/ActionConverter.cs
new file mode 100644 (file)
index 0000000..6184659
--- /dev/null
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace Questionable.Model.V1.Converter;
+
+public sealed class ActionConverter() : EnumConverter<EAction>(Values)
+{
+    private static readonly Dictionary<EAction, string> Values = new()
+    {
+        { EAction.Esuna, "Esuna" },
+    };
+}
index 820c21560164747adb7f5ed9737e2e8018c6076f..0fa5f1d1f7c3ca318bc59bbc83de6a430d944978 100644 (file)
@@ -16,6 +16,7 @@ public sealed class InteractionTypeConverter() : EnumConverter<EInteractionType>
         { EInteractionType.EquipItem, "EquipItem" },
         { EInteractionType.Say, "Say" },
         { EInteractionType.Emote, "Emote" },
+        { EInteractionType.Action, "Action" },
         { EInteractionType.WaitForObjectAtPosition, "WaitForNpcAtPosition" },
         { EInteractionType.WaitForManualProgress, "WaitForManualProgress" },
         { EInteractionType.Duty, "Duty" },
diff --git a/Questionable.Model/V1/EAction.cs b/Questionable.Model/V1/EAction.cs
new file mode 100644 (file)
index 0000000..b5bf2ff
--- /dev/null
@@ -0,0 +1,10 @@
+using System.Text.Json.Serialization;
+using Questionable.Model.V1.Converter;
+
+namespace Questionable.Model.V1;
+
+[JsonConverter(typeof(ActionConverter))]
+public enum EAction
+{
+    Esuna = 7568,
+}
index 4fb2cb871da4cd66b0061ce67875d569d32b2f53..af1281303f42364b64c7226edc192b76f2e40d68 100644 (file)
@@ -16,6 +16,7 @@ public enum EInteractionType
     EquipItem,
     Say,
     Emote,
+    Action,
     WaitForObjectAtPosition,
     WaitForManualProgress,
     Duty,
index 7b5bc5f2536db94e08b68a66527f211ec876d802..377c9cd7ba1c74aa4b9e95086440893e322bd409 100644 (file)
@@ -41,6 +41,7 @@ public sealed class QuestStep
 
     public EEmote? Emote { get; set; }
     public ChatMessage? ChatMessage { get; set; }
+    public EAction? Action { get; set; }
 
     public EEnemySpawnType? EnemySpawnType { get; set; }
     public IList<uint> KillEnemyDataIds { get; set; } = new List<uint>();
diff --git a/Questionable/Controller/Steps/InteractionFactory/Action.cs b/Questionable/Controller/Steps/InteractionFactory/Action.cs
new file mode 100644 (file)
index 0000000..a94f706
--- /dev/null
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using Dalamud.Game.ClientState.Conditions;
+using Dalamud.Game.ClientState.Objects.Types;
+using FFXIVClientStructs.FFXIV.Client.Game;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Questionable.Controller.Steps.BaseTasks;
+using Questionable.Model;
+using Questionable.Model.V1;
+
+namespace Questionable.Controller.Steps.InteractionFactory;
+
+internal static class Action
+{
+    internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
+    {
+        public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
+        {
+            if (step.InteractionType != EInteractionType.Action)
+                return [];
+
+            ArgumentNullException.ThrowIfNull(step.DataId);
+            ArgumentNullException.ThrowIfNull(step.Action);
+
+            var unmount = serviceProvider.GetRequiredService<UnmountTask>();
+            var task = serviceProvider.GetRequiredService<UseOnObject>()
+                .With(step.DataId.Value, step.Action.Value);
+            return [unmount, task];
+        }
+
+        public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+            => throw new InvalidOperationException();
+    }
+
+    internal sealed class UseOnObject(GameFunctions gameFunctions, ILogger<UseOnObject> logger) : ITask
+    {
+        private bool _usedAction;
+        private DateTime _continueAt = DateTime.MinValue;
+
+        public uint DataId { get; set; }
+        public EAction Action { get; set; }
+
+        public ITask With(uint dataId, EAction action)
+        {
+            DataId = dataId;
+            Action = action;
+            return this;
+        }
+
+        public bool Start()
+        {
+            IGameObject? gameObject = gameFunctions.FindObjectByDataId(DataId);
+            if (gameObject == null)
+            {
+                logger.LogWarning("No game object with dataId {DataId}", DataId);
+                return false;
+            }
+
+            if (gameObject.IsTargetable)
+            {
+                _usedAction = gameFunctions.UseAction(gameObject, Action);
+                _continueAt = DateTime.Now.AddSeconds(0.5);
+                return true;
+            }
+
+            return true;
+        }
+
+        public ETaskResult Update()
+        {
+            if (DateTime.Now <= _continueAt)
+                return ETaskResult.StillRunning;
+
+            if (!_usedAction)
+            {
+                IGameObject? gameObject = gameFunctions.FindObjectByDataId(DataId);
+                if (gameObject == null || !gameObject.IsTargetable)
+                    return ETaskResult.StillRunning;
+
+                _usedAction = gameFunctions.UseAction(gameObject, Action);
+                _continueAt = DateTime.Now.AddSeconds(0.5);
+                return ETaskResult.StillRunning;
+            }
+
+            return ETaskResult.TaskComplete;
+        }
+
+        public override string ToString() => $"Action({Action})";
+    }
+}
index 790c67687ef8ed62a0e6fbcbc6e11adaab51feb7..5e42ec4e61f75a8c72a3fa9fa745aa1e599bc976 100644 (file)
@@ -356,6 +356,26 @@ internal sealed unsafe class GameFunctions
         return false;
     }
 
+    public bool UseAction(IGameObject gameObject, EAction action)
+    {
+        if (!ActionManager.CanUseActionOnTarget((uint)action, (GameObject*)gameObject.Address))
+        {
+            _logger.LogWarning("Can not use action {Action} on target {Target}", action, gameObject);
+            return false;
+        }
+
+        _targetManager.Target = gameObject;
+        if (ActionManager.Instance()->GetActionStatus(ActionType.Action, (uint)action, gameObject.GameObjectId) == 0)
+        {
+            bool result = ActionManager.Instance()->UseAction(ActionType.Action, (uint)action, gameObject.GameObjectId);
+            _logger.LogInformation("UseAction {Action} on target {Target} result: {Result}", action, gameObject, result);
+
+            return result;
+        }
+
+        return false;
+    }
+
     public bool IsObjectAtPosition(uint dataId, Vector3 position, float distance)
     {
         IGameObject? gameObject = FindObjectByDataId(dataId);
index d7bf229f16cdb77bcff8b5e57717bf753b6bb7b7..75a1c295ac0294bbf70d9943a7f86723fea9135f 100644 (file)
@@ -16,6 +16,7 @@ using Questionable.Controller.Steps.InteractionFactory;
 using Questionable.Data;
 using Questionable.External;
 using Questionable.Windows;
+using Action = Questionable.Controller.Steps.InteractionFactory.Action;
 
 namespace Questionable;
 
@@ -91,6 +92,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
         serviceCollection.AddSingleton<ITaskFactory, Combat.Factory>();
         serviceCollection.AddTaskWithFactory<Duty.Factory, Duty.OpenDutyFinder>();
         serviceCollection.AddTaskWithFactory<Emote.Factory, Emote.UseOnObject, Emote.Use>();
+        serviceCollection.AddTaskWithFactory<Action.Factory, Action.UseOnObject>();
         serviceCollection.AddTaskWithFactory<Interact.Factory, Interact.DoInteract>();
         serviceCollection.AddTaskWithFactory<Jump.Factory, Jump.DoJump>();
         serviceCollection.AddTaskWithFactory<Say.Factory, Say.UseChat>();