1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[System] ChatCommand

Discussion in 'JASS Resources' started by edo494, Jun 4, 2014.

  1. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,852
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    Finally something useful from me

    Code (vJASS):
    library ChatCommand uses StringIterator
    /*

        Made by: edo494
        Version: 1.2
       
        Requires: StringIterator - http://www.hiveworkshop.com/forums/jass-resources-412/snippet-stringiterator-246143/
       
        This Library provides very easy way to create game modes
        accessed via chat.
       
        There is currently no way to group multiple modes into
        one internally like in dota, where -amap means
        -allmid -allpick, but can be done externally by user.
             
        If you register 2 switches, and both appear in one
        line (-what -ever), it will always trigger them in order
        of appearance in the chat(function registered to swtich -what
        runs, then function registered to swtich -ever runs)
               
        Currently, even if you type something sensible and then
        type some valid switch, it will get triggered. This may
        be filtered, but requires some thinking of doing it inteligently.
       
        Example: "hey boys, its nice to -play with you" will still try
                 to run callback to switch "play" with string "with you"
                         
        This resource should be safe for recursive execution, which means
        that if in some way to get to trigger the checker from the event
        registered to this resource, it should work properly
       
        API:
               
        function AddChatCommand takes string modeString, code callBack returns nothing
            - registers new Mode(mode1 in -mode1 etc) and adds callBack object
              into it for custom functionality
            - please note that "-" is not requested, because it is prepended
              to the switch
              Example: AddModeString("what", function ...) will trigger when you
                       type "-what" in chat
                       
        function RemoveChatCommand takes string modeString returns nothing
            - removes the modeString from the system, so it will no longer be
              triggered
            - O(log n), worst case is O(n), where n is number of modeStrings
              registered
                       
        function SetChatCommandDelimiter takes string newDelimiter returns nothing
            - sets new delimiter for the system to look for in the strings
            - this can be any number of characters
              Note: if you use "" or null, it will always consider the whole
              message after the switch as ChatCommandString even if there is
              another Switch inside the line
             
                 
        Getters:
       
            native GetTriggerPlayer takes nothing returns player
                - returns the player that entered the chat mode to chat
           
            function GetChatCommand takes nothing returns string
                - returns the string used as switch
                  Example: "-what else" returns "what"
               
            function GetChatCommandString takes nothing returns string
                - returns the string that appears after the mode until
                  another mode or until the end of string
                  Example: "-what else or -if something" will return "else or"
                           or "something", dependsing when you request it.
           
            function GetChatCommandDelimiter takes nothing returns string
                - returns the delimiter string
                - defaults to "-"
                 
                 
            struct ChatCommand extends array
               
                static method command takes nothing returns string
                    - returns the string used as switch(-what returns "what")
               
                static method sring takes nothing returns string
                    - returns the string that appears after the mode until
                      another mode or until the end of string
               
                static method operator delimiter takes nothing returns string
                    - returns the delimiter string
                    - defaults to "-"
               
                static method operator delimiter= takes string newDelimiter returns nothing
                    - sets new delimiter for the system to look for in the strings
                    - this can be any number of characters
                      Note: if you use "" or null, it will always consider the whole
                      message after the switch as ChatCommandString even if there is
                      another Switch inside the line
    */

        globals
            private string array modes
            private trigger array trig
            private integer modesC = 0
            private string chatStringTrig = null
            private trigger globTrig = CreateTrigger()
            private string chatString = null
            private string modesDelimiter = "-"
        endglobals
       
        struct ChatCommand extends array
            static method command takes nothing returns string
                return chatStringTrig
            endmethod
           
            static method string takes nothing returns string
                return chatString
            endmethod
           
            static method operator delimiter takes nothing returns string
                return modesDelimiter
            endmethod
           
            static method operator delimiter= takes string newDelimiter returns nothing
                set modesDelimiter = newDelimiter
            endmethod
        endstruct
       
        function GetChatCommandString takes nothing returns string
            return chatString
        endfunction
       
        function GetChatCommand takes nothing returns string
            return chatStringTrig
        endfunction
       
        function GetChatCommandDelimiter takes nothing returns string
            return modesDelimiter
        endfunction
       
        function SetChatCommandDelimiter takes string newDelimiter returns nothing
            set modesDelimiter = newDelimiter
        endfunction
       
        function AddChatCommand takes string modeString, code callBack returns nothing
            local integer m = modesC
            if trig[m] == null and callBack != null then
                set trig[m] = CreateTrigger()
            endif
            set modes[m] = modeString
            call TriggerAddCondition(trig[m], Condition(callBack))
            set modesC = m + 1
        endfunction
       
        function RemoveChatCommand takes string modeString returns nothing
            local integer i = modesC - 1
            loop
                if modes[i] == modeString then
                    call TriggerClearConditions(trig[i])
                    set modes[i] = modes[modesC - 1]
                    call DestroyTrigger(trig[i])
                    set trig[i] = trig[modesC - 1]
                    set modesC = i
                    return
                endif
                exitwhen i == 0
                set i = i - 1
            endloop
        endfunction
       
        private module m
            private static method removeStartingSpaces takes string s returns string
                local integer i = 0
                local integer e = StringLength(s)-1
                local boolean b = false
                local string q = ""
                loop
                    set q = SubString(s, i, i+1)
                    if q != " " then
                        set b = true
                    endif
                    exitwhen i == e or b
                    set i = i + 1
                endloop
                return SubString(s, i, e+1)
            endmethod
           
            private static method removeTrailingSpaces takes string s returns string
                local integer i = 0
                local integer e = StringLength(s) - 1
                local boolean b = false
               
                if e < 0 then
                    return s
                endif
                loop
                    if SubString(s, e, 1) == " " then
                        set e = e - 1
                    else
                        set b = true
                    endif
                    exitwhen b or e <= 1
                endloop
                return SubString(s, 0, e + 1)
            endmethod
           
            private static method onChatEnter takes nothing returns boolean
                local string entered = GetEventPlayerChatString()
                local StringIterator iter = StringIterator.create(entered)
                local string helper = ""
                local string outer = ""
                local string inner = ""
                local integer looper = 0
                local string lock1 = ""
                local string lock2 = ""
                local string lock3 = ""
                local string endHelper = ""
                local string readDelimiter = ""
                local boolean equals = false
                loop
                    call iter.setDelims(" ")
                    exitwhen iter.end
                    set helper = iter.read()
                    set looper = 0
                    set readDelimiter = SubString(helper, 0, StringLength(modesDelimiter))
                    set equals = readDelimiter == modesDelimiter or /*
                                    */
    modesDelimiter == "" or modesDelimiter == null
                    set outer = SubString(helper, StringLength(modesDelimiter), StringLength(helper))
                    if equals then
                        loop
                            if outer == modes[looper] then
                                set lock1 = chatStringTrig
                                set chatStringTrig = outer
                                call iter.setDelims(modesDelimiter)
                                set inner = removeTrailingSpaces(removeStartingSpaces(iter.read()))
                                set lock2 = chatString
                                set lock3 = modesDelimiter
                                set chatString = inner
                                call TriggerEvaluate(trig[looper])
                                set chatStringTrig = lock1
                                set chatString = lock2
                                set modesDelimiter = lock3
                            endif
                            exitwhen looper == modesC - 1
                            set looper = looper + 1
                        endloop
                    endif
                endloop
                return false
            endmethod
            private static method onInit takes nothing returns nothing
                local integer i = 0
                call TriggerAddCondition(globTrig, Condition(function thistype.onChatEnter))
                loop
                    call TriggerRegisterPlayerChatEvent(globTrig, Player(i), "", false)
                    exitwhen i == 15
                    set i = i + 1
                endloop
            endmethod
        endmodule
       
        private struct s extends array
            implement m
        endstruct

    endlibrary
     


    Examples(ripped from my map):

    Code (vJASS):

    scope UserModeClear initializer init
       
        private function clear takes nothing returns nothing
            if GetLocalPlayer() == GetTriggerPlayer() then
                call ClearTextMessages()
            endif
        endfunction
       
        private function init takes nothing returns nothing
            call AddChatCommand("clear", function clear)
        endfunction
       
    endscope
     


    More(a looot more) complex one:

    Code (vJASS):
    scope UserModeCamera initializer init
       
        globals
            private real DEFAULT_DISTANCE = 0
            private constant string CAMERA_HINT = "|cffffff00-camera|r has following options:\n"/*
                                               */
    +"|cffffff00-camera X|r, where X is number - sets the camera distance to X(Default value is 1906)\n"/*
                                               */
    +"|cffff0000Note: |rThe distance must be between 100 and 2500\n"/*
                                               */
    +"|cffffff00-camera fix|r - fixes the distance of camera to certain value\n"/*
                                               */
    +"|cffff0000Note:|r|cffcccc00If you use |r|cffffff00-camera|r|cffcccc00 fix while the camera is fixed, it reverts the effect|r\n"/*
                                               */
    +"|cffffff00-camera lock|r - locks the camera to your hero\n"/*
                                               */
    +"|cffffff00-camera unlock|r - unlocks the camera to free view\n"/*
                                               */
    +"|cffffff00-camera default|r - sets the distance to default distance\n"/*
                                               */
    +"|cffffff00-camera toggle|r - toggles the camera mode(if it is locked, it unlocks it and vice versa)\n"/*
                                               */
    +"|cffffff00-camera help|r - displays this message\n"/*
                                               */
    +"Note: You can also use |cffffff00-cam|r with the same switches"
                                               
            private constant string CAMERA_ERROR = "|cffff0000Invalid input into -camera|r"
           
            private boolean array cameraLocked
           
            private real array cameraDistances
            private integer instances = 0
            private boolean array cameraFixed
            private timer cameraFix = CreateTimer()
           
            unit array StartingUnits
            integer PlayerCount
        endglobals
       
        //******************************************************//
        /*              Handle Camera things                    */
        /*            Lock camera to main unit                  */
        private function CamLock takes player p returns nothing
            local integer id = GetPlayerId(p)
            local unit u = StartingUnits[id]
            if not cameraLocked[id] then
                if GetLocalPlayer() == p then
                    call ClearTextMessages()
                endif
                if GetLocalPlayer() == p then
                    call SetCameraTargetController(u, 0, 0, true)
                    call DisplayTimedTextToPlayer(p, 0, 0, 10, "|cffffcc00Camera Mode: |rLocked")
                endif
                set cameraLocked[id] = true
            else
                call DisplayTimedTextToPlayer(p, 0, 0, 16, "|cffffcc00Camera|r already locked")
            endif
            set u = null
        endfunction
       
        /*           Unlock camera for free movement            */
        private function CamUnlock takes player p returns nothing
            local integer id = GetPlayerId(p)
            local unit u = StartingUnits[id]
            local real x = GetUnitX(u)
            local real y = GetUnitY(u)
            local real d = 0
            if cameraLocked[id] then
                if GetLocalPlayer() == p then
                    call ClearTextMessages()
                endif
                if GetLocalPlayer() == p then
                    set d = GetCameraField(CAMERA_FIELD_TARGET_DISTANCE)
                    call ResetToGameCamera(0)
                    call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, d, 0)
                    call SetCameraPosition(x, y)
                    call DisplayTimedTextToPlayer(p, 0, 0, 10, "|cffffcc00Camera Mode: |rFree")
                endif
                set cameraLocked[id] = false
            else
                call DisplayTimedTextToPlayer(p, 0, 0, 16, "|cffffcc00Camera|r already unlocked")
            endif
            set u = null
        endfunction
       
        /*                Reset camera for players              */
        private function CamReset takes nothing returns nothing
            local integer i = 0
            loop
                if cameraFixed[i] then
                    if GetLocalPlayer() == Player(i) then
                        call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, cameraDistances[i], 0)
                    endif
                endif
                exitwhen i == PlayerCount
                set i = i + 1
            endloop
        endfunction
       
        /*             Fix cameras distance for player          */
        private function CamFix takes player p returns nothing
            local integer id = GetPlayerId(p)
            if GetLocalPlayer() == p then
                call ClearTextMessages()
            endif
            if not cameraFixed[id] then
                call DisplayTimedTextToPlayer(p, 0, 0, 16, "Camera has been fixed")
                set instances = instances + 1
                set cameraFixed[id] = true
                if instances == 1 then
                    call TimerStart(cameraFix, 0.1, true, function CamReset)
                endif
            else
                call DisplayTimedTextToPlayer(p, 0, 0, 16, "Camera has been un-fixed")
                set instances = instances - 1
                set cameraFixed[id] = false
                if instances == 0 then
                    call PauseTimer(cameraFix)
                endif
            endif
        endfunction
       
        /*              Change distance of the Camera           */
        private function CamDist takes integer value, player triggerer returns nothing
            if GetLocalPlayer() == triggerer then
                if value < 100 or value > 2500 then
                    if GetLocalPlayer() == triggerer then
                        call ClearTextMessages()
                    endif
                    call DisplayTimedTextToPlayer(triggerer, 0, 0, 16, "|cffff0000The value after -camera or -cam must be higher or equal to 100 and smaller or equal to 2500|r")
                else
                    call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, value, 1 + (value/DEFAULT_DISTANCE))
                    set cameraDistances[GetPlayerId(triggerer)] = value
                endif
            endif
        endfunction
       
        /*              Dispatch entered string                 */
       
        /*  Checking if the entered string is value for zoom,
            lock or unlock commands
        */

        private function CamDispatch takes nothing returns nothing
            local string s = GetChatCommand()
            local string q = ""
            local integer value = -1
            local player p = GetTriggerPlayer()
            set value = S2I(s)
            if S2I(s) != 0 and SubString(s, 0, 1) != "0" then
                call CamDist(value, p)
            elseif s == "fix" then
                call CamFix(p)
            elseif s == "lock" then
                call CamLock(p)
            elseif s == "unlock" then
                call CamUnlock(p)
            elseif s == "default" then
                call CamDist(R2I(DEFAULT_DISTANCE), p)
            elseif s == "toggle" then
                if cameraLocked[GetPlayerId(p)] then
                    call CamUnlock(p)
                else
                    call CamLock(p)
                endif
            elseif s == "help" then
                if GetLocalPlayer() == p then
                    call ClearTextMessages()
                endif
                call DisplayTimedTextToPlayer(p, 0, 0, 30, CAMERA_HINT)
            else
                if GetLocalPlayer() == p then
                    call ClearTextMessages()
                endif
                if s != "" or s != null then
                    call DisplayTimedTextToPlayer(p, 0, 0, 30, CAMERA_ERROR)
                endif
                call DisplayTimedTextToPlayer(p, 0, 0, 30, CAMERA_HINT)
            endif
        endfunction
       
        private function init takes nothing returns nothing
            local integer i = 0
            call AddChatCommand("camera", function CamDispatch)
            call AddChatCommand("cam", function CamDispatch)
            set DEFAULT_DISTANCE = GetCameraField(CAMERA_FIELD_TARGET_DISTANCE)
            loop
                set cameraDistances[i] = DEFAULT_DISTANCE
                exitwhen i == PlayerCount
                set i = i + 1
            endloop
        endfunction
    endscope
     


    To see the first one work just output something using
    BJDebugMsg
    and then enter "-clear" into chat as if you were trying to type something to everyone.

    To see how the second one works just check
    global variable CAMERA_HINT
    . If you dont want to read the entire variable just download it and type -cam help or -camera help to see it displayed ingame.
    Please take into consideration that the example is ripped off of my map and following globals are not provided(they are placed in other libraries in my map):

    Code (vJASS):

    integer PlayerCount
    unit array StartingUnits
     


    Changelog:

    version 1.2.0a

    • struct ChatMode
      ->
      struct ChatCommand

    version 1.2

    • Overhauled the API:
      • AddChatMode
        ->
        AddChatCommand
      • RemoveModeString
        ->
        RemoveChatCommand
      • AddChatModeString
        ->
        AddChatCommandString
      • SetChatModeDelimiter
        ->
        SetChatCommandDelimiter
      • GetChatModeSwtich
        ->
        GetChatCommand
      • GetChatModeString
        ->
        GetChatCommandString
      • GetChatModeDelimiter
        ->
        GetChatCommandDelimiter
      • ChatMode.switch
        ->
        ChatMode.command

    version 1.1

    • Changed the behaviour of how the ChatModeString is filled. It was kind of glitched and included trailing and leading spaces("-what ever -what" would output " ever " instead of "ever"). This is now fixed
    • Accordingly to the previous mentioned change, I changed the camera example, because the call to
      RemoveStartingSpaces
      is no longer needed
    • Optimized the loop quite a bit
    • Delimiter that is used is now configurable, instead of hardcoded to "-"
    • New functions:
      • SetChatModeDelimiter takes string newDelimiter returns nothing
      • GetChatModeDelimiter takes nothing returns string
    • New methods:
      • static method operator delimiter takes nothing returns string
      • static method operator delimiter= takes string newDelimiter returns nothing
    • Added the forgotten function
      RemoveModeString
      to the API


    version 1.0

    • initial release

     
    Last edited: Sep 10, 2014
  2. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,667
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Maybe the delimiter
    "-"
    should be configurable?

    Otherwise it looks nice. :thumbs_up:
     
  3. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,852
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    yea I can switch it to check for exact match, and use static if, or I could make it turnable during runtime with some checks and boolean value. Static if-ing is overall better for performance and seems less tricky for users to use
     
  4. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,856
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    Sorry I didn't read the code.

    may i ask how is this related to this resource?
     
  5. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,852
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    I didnt even know it exists, so to answer your question, there is no relation.

    But lets do some comparision:

    What mag's does and mine doesnt:

    • his uses structs, whereas mine does not
    • his allows to lock player from executing commands internally, mine does not and you need to check in the callback for player
    • his allows to lock command internally, whereas mine doesnt and requires to check in the callback whether or not it was used before

    What mine does and mag's doesnt:

    • allows you to pass additional information to the mode which means you need only 1 mod for multiple things, such as the camera example whereas with mags you will have to do each command separatly. As it seems it would be next to impossible to create -cam X with his system, in mine its trivial

    What both do:

    • can get callback registered to mode
    • can execute multiple commands in one line of text, even tho a bit differently
     
  6. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,852
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    updated to version 1.1

    added changelog

    I forgot to mention that this resource, even if not needed most likely, is recursivelly safe, so if you somehow trigger chat event with switches while another one is running, it should work properly(not tested)
     
  7. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,852
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    shameless bump
     
  8. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,667
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Shouldn't the library be called ChatCommand or something?

    AddChatCommand fits more than AddModeString IMO.

    The code looks good otherwise.
     
  9. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,852
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    well yea I can rename it, you know Im not the best one when it comes to naming stuff :D
     
  10. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,852
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    version 1.2 is now Up!:

    Overhauled the API:
    • AddChatMode
      ->
      AddChatCommand
    • RemoveModeString
      ->
      RemoveChatCommand
    • AddChatModeString
      ->
      AddChatCommandString
    • SetChatModeDelimiter
      ->
      SetChatCommandDelimiter
    • GetChatModeSwtich
      ->
      GetChatCommand
    • GetChatModeString
      ->
      GetChatCommandString
    • GetChatModeDelimiter
      ->
      GetChatCommandDelimiter
    • ChatMode.switch
      ->
      ChatMode.command
     
  11. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,667
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    I still feel like ChatMode is kind of an awkward name, but it works.

    Approved.
     
  12. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,852
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    ou, the struct should've most likely been called ChatCommand, dont know why I called it ChatMode when even the snippet has been renamed :D

    Change:

    - renamed struct ChatMode -> ChatCommand

    edit: apparently it is called ChatModes, can you rename it to ChatCommands, because the library is called that way now too