• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Easy GUI Chat Command Handler

Status
Not open for further replies.
Level 4
Joined
Jun 22, 2016
Messages
71
I've been browsing this site for an easy to use chat command GUI example. The ones I found wasn't the ones I wanted since one relied on vJASS and another had an issue were the command that could be typed in the middle of a message like "something -gold".

So I had an attempt making one but it was difficult. Preferable I would had "indexOf" in JavaScript the location of the first space but a "for loop"was the only method I could use here to find the command data split point. Anyway this is the end result and I hope it really helps others here.

1.04
  • Updated the error comments. Added a JASS version of the Command Handler here and included it in the map.
1.03
  • Updated author name so it's the same as my hiveworkshop account.
1.02
  • Corrected typos.
  • Removed 2 memory leaks caught by JASS Checker.
1.01
  • Changed the global variables to pascal. Since this is more popular with GUI version.
  • Improved performance by setting the starting Index to 2. 1 is the command symbol and no point checking for if it's a space.
  • Added debug toggle command. This, when enabled, shows the variable values of this resource. However this requires another global variable COMMAND_IsDebug. Hopefully this helps give low level coders an idea what is stored when they enter a command.
1.00 - First Stable Release
  • Added all player message events so anyone during the game can trigger them.
  • Added COMMAND_playerNum to obtain the player who triggered it. Handy if you want an admin player during the game. Just make a condition COMMAND_playerNum == 1.
  • Reduced function calls with variable assistance. This however adds more variables to the global space in exchange for performance.
  • The Command Handler processing has been moved to another trigger. That trigger calls the Commands trigger to reduce clutter.
  • Replaced Integer A with it's own variable to prevent leaks in global space of Integer A usage.
  • Extended the example gold command showing how the variables work together to perform a logical command structure.
0.01 - Verify Release

Benefits of this method:
  • All the processing of the commands are done in a separate trigger for you.
  • You only need to focus on making conditions with the obtained variables and actions for them.
  • The commands only work if the first character of the message contains the command symbol.
  • The command value is lowercased. This helps if the player hits caps lock by mistake during heat of battle.
Global Variable Declare Required (9):
  • String COMMAND_ChatString = <Empty String>
  • String COMMAND_Command = <Empty String>
  • String COMMAND_CommandData = <Empty String>
  • Integer COMMAND_Index = 0
  • Boolean COMMAND_IsDebug = false
  • Boolean COMMAND_IsSplitCommand = false
  • Integer COMMAND_Length = 0
  • Integer COMMAND_PlayerNum = 0
  • String COMMAND_Symbol = -
You may change COMMAND_symbol to what ever you prefer. - is the most common I've seen so far.

Example of use:
  • -gold 1000
  • -GOld 1000
  • -GOLD 1000
All 3 above will have "gold" lowercased in command while commandData has "1000" without the quotes.
  • -command data1 data2
The command variable will have "command" while commandData has "data1 data2" without the quotes.

Trigger: Commands

Here is were you place all your custom chat commands.

The variables you only need to focus on are: COMMAND_command, COMMAND_commandData, COMMAND_isSplitCommand and COMMAND_playerNum.

  • Commands
    • Events
    • Conditions
    • Actions
      • -------- Enter all your commands here. --------
      • -------- Debug comamnd example. --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • COMMAND_Command Equal to debug
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • COMMAND_IsDebug Equal to True
            • Then - Actions
              • Set COMMAND_IsDebug = False
              • Game - Display to (All players) the text: Debug disabled.
            • Else - Actions
              • Set COMMAND_IsDebug = True
              • Game - Display to (All players) the text: Debug enabled.
        • Else - Actions
      • -------- Gold command example. --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • COMMAND_Command Equal to gold
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • COMMAND_IsSplitCommand Equal to True
            • Then - Actions
              • Player - Set (Player(COMMAND_PlayerNum)) Current gold to (((Player(COMMAND_PlayerNum)) Current gold) + (Integer(COMMAND_CommandData)))
              • Game - Display to (All players) the text: (Player + ((String(COMMAND_PlayerNum)) + ( was given + (COMMAND_CommandData + gold.))))
            • Else - Actions
              • Game - Display to (All players) the text: Please enter a numb...
        • Else - Actions
Trigger: Command Handler

All the processing work is done here. Make sure it's calling Commands trigger above else they won't work.

  • Command Handler
    • Events
      • Player - Player 1 (Red) types a chat message containing <Empty String> as A substring
      • Player - Player 2 (Blue) types a chat message containing <Empty String> as A substring
      • Player - Player 3 (Teal) types a chat message containing <Empty String> as A substring
      • Player - Player 4 (Purple) types a chat message containing <Empty String> as A substring
      • Player - Player 5 (Yellow) types a chat message containing <Empty String> as A substring
      • Player - Player 6 (Orange) types a chat message containing <Empty String> as A substring
      • Player - Player 7 (Green) types a chat message containing <Empty String> as A substring
      • Player - Player 8 (Pink) types a chat message containing <Empty String> as A substring
      • Player - Player 9 (Gray) types a chat message containing <Empty String> as A substring
      • Player - Player 10 (Light Blue) types a chat message containing <Empty String> as A substring
      • Player - Player 11 (Dark Green) types a chat message containing <Empty String> as A substring
      • Player - Player 12 (Brown) types a chat message containing <Empty String> as A substring
    • Conditions
    • Actions
      • -------- This is declared first to reduce function calls. --------
      • Set COMMAND_ChatString = (Entered chat string)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Substring(COMMAND_ChatString, 1, 1)) Not equal to COMMAND_Symbol
        • Then - Actions
          • Skip remaining actions
        • Else - Actions
      • -------- Reset the variables to prepare processing the next command. --------
      • Set COMMAND_Command = <Empty String>
      • Set COMMAND_CommandData = <Empty String>
      • Set COMMAND_IsSplitCommand = False
      • Set COMMAND_Index = 2
      • Set COMMAND_Length = (Length of COMMAND_ChatString)
      • Set COMMAND_PlayerNum = (Player number of (Triggering player))
      • -------- For loop for spaces and substring command and commandData if 1 is found. --------
      • For each (Integer A) from COMMAND_Index to COMMAND_Length, do (Actions)
        • Loop - Actions
          • -------- The condition below is checking for a single space which is the split point. --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Substring(COMMAND_ChatString, COMMAND_Index, COMMAND_Index)) Equal to
            • Then - Actions
              • -------- If a space is found, it will store command and commandData. --------
              • Set COMMAND_IsSplitCommand = True
              • Set COMMAND_Command = (String((Substring(COMMAND_ChatString, 2, (COMMAND_Index - 1)))) as Lower case)
              • Set COMMAND_CommandData = (Substring(COMMAND_ChatString, (COMMAND_Index + 1), COMMAND_Length))
              • -------- Stops the rest of the current for loop since it's no longer needed. This improves performance by not wasting processing. --------
              • Custom script: exitwhen true
            • Else - Actions
          • Set COMMAND_Index = (COMMAND_Index + 1)
      • -------- If no split point is found for commandData. This will just get the current command. --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • COMMAND_IsSplitCommand Equal to False
        • Then - Actions
          • Set COMMAND_Command = (String((Substring(COMMAND_ChatString, 2, COMMAND_Length))) as Lower case)
        • Else - Actions
      • -------- Debug messages. --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • COMMAND_IsDebug Equal to True
        • Then - Actions
          • Game - Display to (All players) the text: COMMAND Debug:
          • Game - Display to (All players) the text: (.ChatString = " + (COMMAND_ChatString + "))
          • Game - Display to (All players) the text: (.Command = " + (COMMAND_Command + "))
          • Game - Display to (All players) the text: (.CommandData = " + (COMMAND_CommandData + "))
          • If (COMMAND_IsDebug Equal to True) then do (Game - Display to (All players) the text: .isDebug = true) else do (Game - Display to (All players) the text: .isDebug = false)
          • Game - Display to (All players) the text: (.Index = " + ((String(COMMAND_Index)) + "))
          • If (COMMAND_IsSplitCommand Equal to True) then do (Game - Display to (All players) the text: .IsSplitCommand = t...) else do (Game - Display to (All players) the text: .IsSplitCommand = f...)
          • Game - Display to (All players) the text: (.Length = " + ((String(COMMAND_Length)) + "))
          • Game - Display to (All players) the text: (.PlayerNum = " + ((String(COMMAND_PlayerNum)) + "))
          • Game - Display to (All players) the text: (.Symbol = " + (COMMAND_Symbol + "))
        • Else - Actions
      • Trigger - Run Commands <gen> (checking conditions)
JASS version of the Command Handler. Requires lessor global variables and has greater performance. Use this if you have understanding of the JASS language.

JASS:
function Trig_Command_Handler_JASS_Version_Actions takes nothing returns nothing
    // This is declared first to reduce function calls.
    local string chatString = GetEventPlayerChatString()
    local integer index
    local integer length

    // Stop execution if command symbol doesn't match.
    if (SubStringBJ(chatString, 1, 1) != udg_COMMAND_Symbol) then
        return
    endif

    // Reset the variables to prepare processing the next command.
    set udg_COMMAND_Command = ""
    set udg_COMMAND_CommandData = ""
    set udg_COMMAND_IsSplitCommand = false
    set udg_COMMAND_PlayerNum = GetConvertedPlayerId(GetTriggerPlayer())

    // For loop for spaces and substring command and commandData if 1 is found.
    set index = 2
    set length = StringLength(chatString)
    loop
        exitwhen index > length

        // The condition below is checking for a single space which is the split point.
        if (SubStringBJ(chatString, index, index) == " ") then
            // If a space is found, it will store command and commandData.
            set udg_COMMAND_Command = StringCase(SubStringBJ(chatString, 2, (index - 1)), false)
            set udg_COMMAND_CommandData = SubStringBJ(chatString, (index + 1), length)
            set udg_COMMAND_IsSplitCommand = true
            // Stops the rest of the current for loop since it's no longer needed.
            // This improves performance by not wasting processing.
            exitwhen true
        endif

        set index = index + 1
    endloop

    // If no split point is found for commandData. This will just get the current command.
    if (udg_COMMAND_IsSplitCommand == false) then
        set udg_COMMAND_Command = StringCase(SubStringBJ(chatString, 2, length), false)
    endif

    // Debug messages.
    if (udg_COMMAND_IsDebug == true) then
        call DisplayTextToForce(GetPlayersAll(), "COMMAND Debug")
        call DisplayTextToForce(GetPlayersAll(), "=== === === ===")
        call DisplayTextToForce(GetPlayersAll(), ("ChatString = '" + chatString + "'"))
        call DisplayTextToForce(GetPlayersAll(), ("Command = '" + udg_COMMAND_Command + "'"))
        call DisplayTextToForce(GetPlayersAll(), ("CommandData = '" + udg_COMMAND_CommandData + "'"))
        if (udg_COMMAND_IsDebug == true) then
            call DisplayTextToForce(GetPlayersAll(), "IsDebug = true")
        else
            call DisplayTextToForce(GetPlayersAll(), "IsDebug = false")
        endif
        call DisplayTextToForce(GetPlayersAll(), ("Index = " + I2S(index)))
        if (udg_COMMAND_IsSplitCommand == true) then
            call DisplayTextToForce(GetPlayersAll(), "IsSplitCommand = true")
        else
            call DisplayTextToForce(GetPlayersAll(), "IsSplitCommand = false")
        endif
        call DisplayTextToForce(GetPlayersAll(), ("Length = " + I2S(length)))
        call DisplayTextToForce(GetPlayersAll(), ("PlayerNum = " + I2S(udg_COMMAND_PlayerNum)))
        call DisplayTextToForce(GetPlayersAll(), ("Symbol = '" + udg_COMMAND_Symbol + "'"))
    endif
    call ConditionalTriggerExecute(gg_trg_Commands)
endfunction

//===========================================================================
function InitTrig_Command_Handler_JASS_Version takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterPlayerChatEvent(t, Player(0), "", false)
    call TriggerRegisterPlayerChatEvent(t, Player(1), "", false)
    call TriggerRegisterPlayerChatEvent(t, Player(2), "", false)
    call TriggerRegisterPlayerChatEvent(t, Player(3), "", false)
    call TriggerRegisterPlayerChatEvent(t, Player(4), "", false)
    call TriggerRegisterPlayerChatEvent(t, Player(5), "", false)
    call TriggerRegisterPlayerChatEvent(t, Player(6), "", false)
    call TriggerRegisterPlayerChatEvent(t, Player(7), "", false)
    call TriggerRegisterPlayerChatEvent(t, Player(8), "", false)
    call TriggerRegisterPlayerChatEvent(t, Player(9), "", false)
    call TriggerRegisterPlayerChatEvent(t, Player(10), "", false)
    call TriggerRegisterPlayerChatEvent(t, Player(11), "", false)
    call TriggerAddAction(t, function Trig_Command_Handler_JASS_Version_Actions)
endfunction

I've added an attachment TFT map. So anyone can copy and paste the triggers easily.

By default Command Handler JASS Version trigger is disabled. If you have knowledge in how to handle the script it's better to use it due to lessor global variable pollution and has greater performance. They both produce the exact same result. Only use one of either Command Handler or Command Handler JASS Version.
 

Attachments

  • ChatCommandHandlerExample_By_Furry_Nova.w3x
    19 KB · Views: 35
Last edited:
Level 13
Joined
May 10, 2009
Messages
868
You can break a loop by typing (in a custom script): exitwhen true.
In order to post your GUI triggers like the others, you need to use the tag [trigger][/trigger]

Example:

  • break loop
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • For each (Integer loop) from 1 to 30, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • LIST[loop] Equal to CORRECT_VALUE
            • Then - Actions
              • Custom script: exitwhen true
            • Else - Actions
 
Level 4
Joined
Jun 22, 2016
Messages
71
You can break a loop by typing (in a custom script): exitwhen true.
In order to post your GUI triggers like the others, you need to use the tag [trigger][/trigger]

Example:

  • break loop
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • For each (Integer loop) from 1 to 30, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • LIST[loop] Equal to CORRECT_VALUE
            • Then - Actions
              • Custom script: exitwhen true
            • Else - Actions
Thanks, mate. I've added the "exitwhen true" which should improve performance and I've replaced the tags.
 
For some game mods it's even wanted that the players can write multiple commands in only 1 line.
But sometimes it's definitly not wanted, so your way is legit, too.

The command key "!" or "-" could be stored in a variable and just defined by user.

"(Length of (Entered chat string))"
^Can be stored into a variable to avoid multiple function calls. Variable lookup is faster than a function call, and it's called more often here.

(Integer A)
^Could be replaced by a custom integer variable. Same principle with function call vs variable lookup.

Outsourcing is something good. Instead of putting the system code into an empty If/Else statement, it could be outsourced into a new trigger.
Then at start of actions you run the trigger, the system makes it magic, and then the user can directly work with the correct command input.

----

You probably have chosen the wrong forum. Triggers & Scripts is usually used when you stuck in a code issue.
Did you want it to have it as tutorial for GUI?
 
Level 4
Joined
Jun 22, 2016
Messages
71
Yes, I intended this as a resource. But I thought it better to get it verified.

For the command key. Do you mean were it only works with 1 symbol or loop an array? I thought condition would be faster.

I thought Integer A was a variable.

If I understand correctly, you mean 1st trigger that collects the command values and at the end it calls a second trigger with the condition triggers? I thought having everything in a in a single trigger would be less clutter.

If this is okay to moved to tutorial. Like me to rename the vars like the spells? COMMAND_command, COMMAND_commandData and COMMAND_isSplitCommand?
 
Last edited:
Yes, I intended this as a resource. But I thought it better to get it verified.
If this is okay to moved to tutorial.
It probably fits best or in The Lab, or in Tutorials, because you explain a technique that the user can apply, instead of dealing with a specific task.

I thought Integer A was a variable.
Actually yes, it is. Though, to lookup the variable you need to call a function which returns IntegerA, and therefor is potentialy slower. One can check it when converting GUI to JASS.

Do you mean were it only works with 1 symbol
One symbol seems okay. Everything else is probably silly in most cases. I just meant a variable to clearly and easily define it for the user.

I thought having everything in a in a single trigger would be less clutter.
With putting the core code into an empty If/Else statement you yourself wanted to "hide" it to get a better overview! ;p
And an easy way is really just to move it into a new trigger. But it makes no real difference, I find it just cleaner and the user actually doesn't need to see the loop code all the time when writing his own code.

Yes, a name prefix might be helpful.
 
Level 4
Joined
Jun 22, 2016
Messages
71
I've tested, updated the post and uploaded an updated map. I don't think there's anything else that needs adding or changing unless there's any more minor improvements that could be added.

Thanks for all your help so far, guys.
 
I went ahead and moved this to The Lab. As what IcemanBo said, it seems the most appropriate forum.

On a side note, I'm not a big fan of camelCase in GUI, but whatever works for you. Also, why do you add 1 to COMMAND_index in the loop? The GUI for loops increment them for you already. Adding 1 on top of the function adding 1 will end the for loop prematurely.
 
Level 4
Joined
Jun 22, 2016
Messages
71
I just tested by disabling
  • Set COMMAND_index = (COMMAND_index + 1)
The commands doesn't work so I think the loop is stuck at value 1 and continuously running in the background unless I misunderstood? I just had another look, I think the index would be better starting at 2. I'm so use to 0 being first character in another language. Unless that's what you meant?

I just double checked the [Trigger] - Variable Naming Standards. I forgot it was pascal case after the resource name. I'll push that through the next update. Thanks.
 
Last edited:
Level 4
Joined
Jun 22, 2016
Messages
71
I've added update version 1.01. Details are listed in the first post.

I can't imagine this GUI version being improved any further as it is now. I'd like to say thanks for all your help and I hope many benefit from this work. If there are any tiny performance improvements that could be added let me know.

Lastly. I've had a bit of practise with JASS recently. I'm no expert. But if requested, I could copy and convert only the Command Handler trigger into it and improve the messy code the GUI makes behind the scenes. In a way, I could have a GUI and a JASS version of the trigger and the user can enable which one they prefer. But I can't imagine the JASS version improving performance much more than a millisecond or 2 which may seem unnecessary.
 
Level 4
Joined
Jun 22, 2016
Messages
71
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • COMMAND_Command Equal to gold
    • Then - Actions
      • -------- // do actions --------
      • Skip remaining actions
    • Else - Actions
You mean a Skip Remaining Actions at the end of the command condition comparison after the actions? I try reduce the comments also.
 
Status
Not open for further replies.
Top