diff --git a/src/Commands/BaseCommand.cs b/src/Commands/BaseCommand.cs
index d56bd16..85cfbbd 100644
--- a/src/Commands/BaseCommand.cs
+++ b/src/Commands/BaseCommand.cs
@@ -57,10 +57,15 @@ internal abstract class BaseCommand
internal virtual bool ShowCommand() => true;
///
- /// Determines whether this command should be shown in suggestions.
+ /// Determines whether this command can be executed based on current conditions.
///
- /// True if the command should be shown in suggestions; otherwise, false.
- internal virtual bool ShowSuggestion() => ShowCommand();
+ /// When this method returns, contains the reason why the command cannot be run if it returns false; otherwise, an empty string.
+ /// True if the command can be executed; otherwise, false.
+ internal virtual bool CanRunCommand(out string reason)
+ {
+ reason = string.Empty;
+ return true;
+ }
///
/// Executes the command.
diff --git a/src/Commands/DumpCommand.cs b/src/Commands/DumpCommand.cs
index 0bc3175..9d3653c 100644
--- a/src/Commands/DumpCommand.cs
+++ b/src/Commands/DumpCommand.cs
@@ -1,5 +1,5 @@
-using BetterAmongUs.Data;
-using BetterAmongUs.Attributes;
+using BetterAmongUs.Attributes;
+using BetterAmongUs.Data;
using BetterAmongUs.Modules;
namespace BetterAmongUs.Commands;
@@ -9,7 +9,17 @@ internal sealed class DumpCommand : BaseCommand
{
internal override string Name => "dump";
internal override string Description => "Dump the entire log to the user's desktop";
- internal override bool ShowCommand() => !GameState.IsInGamePlay;
+
+ internal override bool CanRunCommand(out string reason)
+ {
+ if (GameState.IsInGamePlay)
+ {
+ reason = "Only can run in lobby";
+ return false;
+ }
+
+ return base.CanRunCommand(out reason);
+ }
internal override void Run()
{
diff --git a/src/Commands/ForceSkipCommand.cs b/src/Commands/ForceSkipCommand.cs
index 6e4e664..0b2c13f 100644
--- a/src/Commands/ForceSkipCommand.cs
+++ b/src/Commands/ForceSkipCommand.cs
@@ -8,7 +8,23 @@ internal sealed class ForceSkipCommand : BaseCommand
{
internal override string Name => "forceskip";
internal override string Description => "Force skips a meeting in progress";
- internal override bool ShowCommand() => GameState.IsHost && GameState.IsMeeting;
+ internal override bool CanRunCommand(out string reason)
+ {
+ if (!GameState.IsHost)
+ {
+ reason = "Can only run as host";
+ return false;
+ }
+
+ if (!GameState.IsMeeting)
+ {
+ reason = "Can only run in meeting";
+ return false;
+ }
+
+ return base.CanRunCommand(out reason);
+ }
+
internal override void Run()
{
if (GameState.IsHost)
diff --git a/src/Commands/KickCommand.cs b/src/Commands/KickCommand.cs
index bb0eef9..f553f94 100644
--- a/src/Commands/KickCommand.cs
+++ b/src/Commands/KickCommand.cs
@@ -1,6 +1,6 @@
-using BetterAmongUs.Commands.Arguments;
+using BetterAmongUs.Attributes;
+using BetterAmongUs.Commands.Arguments;
using BetterAmongUs.Helpers;
-using BetterAmongUs.Attributes;
using BetterAmongUs.Modules;
namespace BetterAmongUs.Commands;
@@ -10,6 +10,16 @@ internal sealed class KickCommand : BaseCommand
{
internal override string Name => "kick";
internal override string Description => "Kick a player from the game";
+ internal override bool CanRunCommand(out string reason)
+ {
+ if (!GameState.IsHost)
+ {
+ reason = "Can only run as host";
+ return false;
+ }
+
+ return base.CanRunCommand(out reason);
+ }
public KickCommand()
{
playerArgument = new PlayerArgument(this);
@@ -19,7 +29,6 @@ internal sealed class KickCommand : BaseCommand
private PlayerArgument playerArgument { get; }
private BoolArgument boolArgument { get; }
- internal override bool ShowCommand() => GameState.IsHost;
internal override void Run()
{
var player = playerArgument.TryGetTarget();
diff --git a/src/Patches/Gameplay/UI/Chat/ChatCommandsPatch.cs b/src/Patches/Gameplay/UI/Chat/ChatCommandsPatch.cs
index d6b987f..75300fc 100644
--- a/src/Patches/Gameplay/UI/Chat/ChatCommandsPatch.cs
+++ b/src/Patches/Gameplay/UI/Chat/ChatCommandsPatch.cs
@@ -52,6 +52,11 @@ internal static class ChatCommandsPatch
return true;
}
+ if (!closestCommand.CanRunCommand(out string _))
+ {
+ return false;
+ }
+
HandleCommand();
if (ChatPatch.ChatHistory.Count == 0 || ChatPatch.ChatHistory[^1] != text) ChatPatch.ChatHistory.Add(text);
@@ -130,8 +135,7 @@ internal static class ChatCommandsPatch
closestCommand = GetClosestCommand(typedParts[0]);
bool isSuggestionValid = closestCommand != null
- && (typedParts[0].Equals(closestCommand.Name, StringComparison.OrdinalIgnoreCase) || typedParts.Length == 1)
- && closestCommand.ShowSuggestion();
+ && (typedParts[0].Equals(closestCommand.Name, StringComparison.OrdinalIgnoreCase) || typedParts.Length == 1);
if (isSuggestionValid)
{
@@ -167,8 +171,13 @@ internal static class ChatCommandsPatch
__instance.freeChatField.textArea.SetText(fullSuggestion);
}
+ if (!closestCommand.CanRunCommand(out string _))
+ {
+ fullSuggestion = fullSuggestion.ToColor("#FF0300".HexToColor());
+ }
+
commandText.text = fullSuggestion;
- commandInfo.text = $"{closestCommand.Description}{GenerateArgumentInfo()}";
+ commandInfo.text = $"{closestCommand.Description}{GenerateArgumentInfo()}{GenerateCanRunInfo()}";
}
private static string GenerateSuggestion(string[] typedParts)
@@ -207,6 +216,15 @@ internal static class ChatCommandsPatch
return $" - <#a6a6a6>{argumentInfo}";
}
+ private static string GenerateCanRunInfo()
+ {
+ if (!closestCommand.CanRunCommand(out string reason))
+ {
+ return $" - <#FF0300>{reason}";
+ }
+
+ return string.Empty;
+ }
internal static BaseCommand? GetClosestCommand(string typedCommand)
{