• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Advanced Switch System v1.4.1

  • Like
Reactions: Apheraz Lucent
Here is a switch system I made out of boredom (yeah boredom is awesome!)

This system will allow a player to switch between player groups / forces / team (whatever you call it, but for commoners, player groups)
Great for AoS game or team fights, if the players wanted the game to be balanced with the number of players in each player group.

It supports multiple forces to be registered on the system but it only allows a maximum of 12 because each player can only be in one force at a time.

See test map documentation for more information

Pros:
* Configurable enough
* Allows more than 2 forces
* Can be GUI friendly
* Pretty compact data index
* Proper player colors (thanks to Ammorth's GetPlayerColored library)
* Provides a safety measure if any changes in our force are executed during a switch process.
Cons:
* Will require Event if it's not set to GUI friendly mode.
* Doesn't automatically refreshes table data when forces are modified outside this system (you must use the functions if you'll do this)
* Auto purge only supports ForceClear and DestroyForce functions

For the meantime, you may use the API functions in case you'd like to modify a force's data registered on this system (it will also update the switch list)

API:
function AddSwitchForce takes force f returns nothing
function RemoveSwitchForce takes force f returns nothing
function GetPlayerSwitchForce takes player p returns force
function IsSwitchOnGoing takes nothing returns boolean
function SwitchEnable takes boolean b returns boolean
function IsSwitchEnabled takes nothing returns boolean
function UpdatePlayerSwitchForce takes player p returns boolean
function SwitchForceRemovePlayer takes force f, player p returns boolean
function AddPlayerToSwitchForce takes force f, player p returns boolean
function GetPlayerSwitchIndex takes player whichPlayer returns integer

This function requires Event and is only activated when GUI_FRIENDLY is set to false:
function RegisterOnSwitchSuccess takes code c returns nothing

Code:
JASS:
library AdvancedSwitchSystem /* v1.4.1
*************************************************************************************
*
*     by jim7777
*
*   This is a switch system that will allow a player to switch between forces.
*   
*   Pros:
*    Configurable enough
*    Allows more than 2 forces
*    Can be GUI friendly 
*    Pretty compact data index 
*    Proper player colors (thanks to Ammorth's GetPlayerColored library)
*    Provides a safety measure if any changes in our force are executed during a switch process.
*   Cons:
*    Will require Event if it isn't set to GUI friendly mode.
*    Doesn't automatically refreshes table data when forces are modified outside this system (you must use the functions if you'll do this)
*
*
*************************************************************************************
*
*   */ uses /*
*   
*       */ Table            /*      http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*       */ GetPlayerColored /*      http://www.wc3c.net/showthread.php?t=101692
*       */ optional Event   /*      http://www.hiveworkshop.com/forums/jass-resources-412/snippet-event-186555/
*
************************************************************************************
*
*   SETTINGS
*
*/
    globals
    
    /*
    *   TRUE:
    *     *When a switch process is successful, it will run the trigger set by the Switch_SuccessTrigger variable
    *    FALSE:
    *     *Requires Event 
    *     *When a switch process is successful, codes registered via the RegisterOnSwitchSuccess function will run
    */
        private constant boolean GUI_FRIENDLY = true 
        
    /*
    *    TRUE:
    *     *Automatically purges/update data whenever a force is modified outside this system
    *    FALSE:
    *     *If you want to purge/update data manually
    */
        private constant boolean AUTO_PURGE = true 
        
    /*
    *    TRUE: 
    *     *accepts a minimum of 3 forces and a maximum of 12 forces.
    *     *the command will take two values as input, e.g. -switch 12 wherein 1 is the force index and 2 is the player's index in force 1
    *    FALSE: 
    *      *allows two forces (min and max)
    *      *this command will only take one value as input, e.g. -switch 1 where 1 is the player's index in the opposite force.
    */
        private constant boolean TWO_DIG = true
        
    /*
    *   The command if a player wants the switch list or start a switch process
    */
        private constant string COM = "-switch"
        
    /*
    *   The command to type if a player agrees on a switch process
    */
        private constant string COM_OK = "-ok" 
        
    /*
    *   The command to type if a player disagrees on a switch process
    */
        private constant string COM_NO = "-no" 
        
    /*
    *   How long should the switch process last if ever voting criteria isn't fulfilled yet
    */
        private constant real TIMEOUT = 10 
        
    /*
    *   This is the default setting if switch is on by the time this system is initiated (true = on; false = off)
    */
        private boolean switchOn = true 
    endglobals
/*
************************************************************************************
*
*    Functions
*
*        function AddSwitchForce takes force whichForce returns nothing
*            -   Adds a force and its players to the switch system
*
*        function RemoveSwitchForce takes force whichForce returns nothing
*            -   Removes a force from this system
*
*        function GetPlayerSwitchForce takes player whichPlayer returns force 
*            -   Returns the current force of a player that is registered on this system.
*            -   Returns null if it doesn't exist
*
*        function GetPlayerSwitchIndex takes player whichPlayer returns integer 
*            -   Returns the player's index in its switch force.
*
*        function IsSwitchOnGoing takes nothing returns boolean
*            -   Returns true if there's an ongoing switch process.
*
*        function SwitchEnable takes boolean flag returns boolean
*            -   Enables or disable this system
*            -   Returns false if there's an ongoing switch process.
*
*        function IsSwitchEnabled takes nothing returns boolean
*            -   Returns true if this system is enabled 
*
*        function UpdatePlayerSwitchForce takes player whichPlayer returns boolean
*            -   Re-updates a player's force that is registered on this system.
*            -   Returns true if it is successful
*
*        function SwitchForceRemovePlayer takes force whichForce, player whichPlayer returns boolean
*            -   Removes a player from a registered force on this system.
*            -   Returns true if it is successful
*
*        function AddPlayerToSwitchForce takes force f, player p returns boolean
*            -   Adds a player to a registered force on this system.
*            -   Returns true if it is successful
*            -   This is useful for adding unused player slots
*
*        function RegisterOnSwitchSuccess takes code c returns nothing
*            -   Registers a function to the system that is to be called when a switch is successful.
*            -   This is only available when GUI_FRIENDLY is set to false.
*            -   Requires the Event library
************************************************************************************
*/
    globals
        private boolean onGoing = false
        private hashtable ht = InitHashtable()
        private integer inQueue = 0
        private integer max = 0
        private integer stringLength
        private TableArray dataTable
        private TableArray playerDataTable
    endglobals
    
    private struct VoteSwitch
        boolean array voted[11]
        integer vote = 1
        integer voters
        static boolean ended = false 
        static player sourcePlayer
        static player targetSwitchPlayer
        static timer time
        static thistype this
        
        static if not GUI_FRIENDLY then
            static Event Ev
        endif
        
        private static method recountPlayers takes nothing returns nothing
            local integer i = 0
            set this.voters = 0
            loop
                exitwhen i > 11
                if GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(i)) == MAP_CONTROL_USER then
                    set this.voters = this.voters + 1
                endif
                set i = i + 1
            endloop
        endmethod
        
        private static method switchSuccess takes nothing returns nothing
            local integer playerId = GetPlayerId(this.sourcePlayer)
            local integer targetPlayerId = GetPlayerId(this.targetSwitchPlayer)
            local integer playerForceTableId = R2I(playerDataTable[playerId].real[0]) //since tables no allow integahs
            local integer targetPlayerForceTableId = R2I(playerDataTable[targetPlayerId].real[0])
            local integer playerForceIndex = R2I(playerDataTable[playerId].real[1])
            local integer targetPlayerForceIndex = R2I(playerDataTable[targetPlayerId].real[1])
            
            /*
            * the source force or the force of the player who initiated the switch process
            */
            local force sourceForce = dataTable[playerForceTableId].force[0] 
            
            /*
            * the force of our target player 
            */
            local force targetForce = dataTable[targetPlayerForceTableId].force[0]
            
            set this.ended = true 
            
            /*
            * start updating our table data
            */
            
            call ForceRemovePlayer(sourceForce,this.sourcePlayer)
            call ForceRemovePlayer(targetForce,this.targetSwitchPlayer)
            
            set dataTable[playerForceTableId].player[playerForceIndex] = this.targetSwitchPlayer
            set dataTable[targetPlayerForceTableId].player[targetPlayerForceIndex] = this.sourcePlayer
            
            /*set dataTable[playerForceTableId].force[0] = targetForce
            set dataTable[targetPlayerForceTableId].force[0] = sourceForce*/
            
            call ForceAddPlayer(targetForce,this.sourcePlayer)
            call ForceAddPlayer(sourceForce,this.targetSwitchPlayer)
            
            set playerDataTable[playerId].real[0] = targetPlayerForceTableId
            set playerDataTable[targetPlayerId].real[0] = playerForceTableId
            
            set playerDataTable[playerId].real[1] = targetPlayerForceIndex
            set playerDataTable[targetPlayerId].real[1] = playerForceIndex
            
            set udg_Switch_SourcePlayer = this.sourcePlayer
            set udg_Switch_TargetPlayer = this.targetSwitchPlayer
            
            set udg_Switch_SourceForce = sourceForce
            set udg_Switch_TargetForce = targetForce
            
            /*
            * start executing our trigger or fire our events
            */
            
            static if GUI_FRIENDLY then
                call TriggerExecute(udg_Switch_SuccessTrigger)
            else
                call this.Ev.fire()
            endif
            
            /*
            * if everything was finished... perform finishing touches
            */
            call ClearTextMessages()
            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,8,"The switch process between " + GetPlayerNameColored(this.sourcePlayer) + " and " + GetPlayerNameColored(this.targetSwitchPlayer) + " has successfully completed.")
            
            call this.destroy()
            set onGoing = false
            
            set sourceForce = null
            set targetForce = null
        endmethod
        
        private static method onTimeout takes nothing returns nothing
            set onGoing = false 
            call this.destroy()
            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,5,"|CFFFF0303The switch voting process has timed out.|r")
            call PauseTimer(thistype.time)
        endmethod
        
        private static method showList takes player p returns nothing
            local integer i = 0
            local integer forceIndex
            local player indexedPlayer
            loop
                exitwhen i >= max
                set forceIndex = 1 
                /*
                * check if our selected table isn't empty
                */
                if dataTable[i].real[0] > 1 then 
                    /*
                    * well Loop 'em
                    */
                    loop 
                        exitwhen forceIndex == dataTable[i].real[0]
                        if not IsPlayerInForce(p,dataTable[i].force[0]) then
                            set indexedPlayer = dataTable[i].player[forceIndex]
                            static if TWO_DIG then
                                call DisplayTimedTextFromPlayer(p,0,0,10,"Type " + COM + " " + I2S(i) + "-" + I2S(forceIndex) + " to switch with " + GetPlayerNameColored(indexedPlayer))
                            else
                                call DisplayTimedTextFromPlayer(p,0,0,10,"Type " + COM + " " + I2S(forceIndex) + " to switch with " + GetPlayerNameColored(indexedPlayer))
                            endif
                        endif
                        
                        set forceIndex = forceIndex + 1
                    endloop
                endif
                set i = i + 1
            endloop
            /*
            * le space
            */
            call DisplayTimedTextToPlayer(p,0,0,1," ") 
        endmethod
        
        private static method initSwitch takes player p, player tp returns nothing
            set onGoing = true
            set this = thistype.allocate()
            set this.sourcePlayer = p
            set this.targetSwitchPlayer = tp
            set this.voted[GetPlayerId(p)] = true
                                                                                    
            set udg_Switch_SourcePlayer = null
            set udg_Switch_TargetPlayer = null
            set udg_Switch_SourceForce = null
            set udg_Switch_TargetForce = null
            
            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,15,GetPlayerNameColored(this.sourcePlayer) + " would like to switch with " + GetPlayerNameColored(this.targetSwitchPlayer) + ". Type " + COM_OK + " to accept or " + COM_NO + " to reject.")
            call recountPlayers()
            call TimerStart(thistype.time,TIMEOUT,false,function thistype.onTimeout)
        endmethod
        
        static method start takes nothing returns nothing
            local player triggerPlayer = GetTriggerPlayer()
            local player targetPlayer
            local string chatString = GetEventPlayerChatString()
            
            local integer chatsStringLength = StringLength(chatString)
            local integer playerId = GetPlayerId(triggerPlayer)
            local integer targetId
            local integer forceTableId
            
            if switchOn then
                if SubString(chatString,0,stringLength) == COM then
                    if stringLength == chatsStringLength and IsPlayerInForce(triggerPlayer,dataTable[R2I(playerDataTable[playerId].real[0])].force[0]) then
                        call thistype.showList(triggerPlayer)
                    elseif not onGoing then
                    
                        if playerDataTable[playerId].boolean[0] and IsPlayerInForce(triggerPlayer,dataTable[R2I(playerDataTable[playerId].real[0])].force[0]) then//double check values
                            static if TWO_DIG then
                                
                                set forceTableId = S2I(SubString(SubString(chatString,0,stringLength+2),stringLength+1,1))
                                
                                
                                if dataTable[forceTableId].real[0] > 9 and chatsStringLength >= stringLength+5 then 
                                
                                    set chatString = SubString(chatString,0,stringLength+5) 
                                    set targetId = S2I(SubString(chatString,stringLength+3,2))
                                    
                                    if targetId <= 0 or targetId > dataTable[forceTableId].real[0] then 
                                        set chatString = SubString(chatString,0,stringLength+4)
                                        set targetId = S2I(SubString(chatString,stringLength+3,1))
                                    endif
                                    
                                
                                else
                                    set chatString = SubString(chatString,0,stringLength+4)
                                    set targetId = S2I(SubString(chatString,stringLength+3,1))
                                endif
                                
                                set targetPlayer = dataTable[forceTableId].player[targetId]
                                if targetPlayer != null and dataTable[forceTableId].boolean[0] and dataTable[R2I(playerDataTable[playerId].real[0])].boolean[0] and not IsPlayerInForce(triggerPlayer,dataTable[forceTableId].force[0]) and playerDataTable[GetPlayerId(targetPlayer)].boolean[0] then
                                    
                                    call thistype.initSwitch(triggerPlayer,targetPlayer)
                    
                                else
                                    debug call BJDebugMsg("[Switch System]: Invalid data values!")
                                    call thistype.showList(triggerPlayer)
                                endif
                                
                            else
                                if dataTable[0].boolean[0] and dataTable[1].boolean[0] then
                                    
                                    if IsPlayerInForce(triggerPlayer,dataTable[0].force[0]) then
                                    
                                        if dataTable[1].real[0] > 9 and chatsStringLength >= stringLength+3 then 
                                        
                                            set chatString = SubString(chatString,0,stringLength+3) 
                                            set targetId = S2I(SubString(chatString,stringLength+1,2))
                                            
                                            if targetId <= 0 or targetId > dataTable[1].real[0] then 
                                                set chatString = SubString(chatString,0,stringLength+2) 
                                                set targetId = S2I(SubString(chatString,stringLength+1,1))
                                            endif
                                            
                                        else
                                            set chatString = SubString(chatString,0,stringLength+2) 
                                            set targetId = S2I(SubString(chatString,stringLength+1,1))
                                        endif
                                        
                                        set targetPlayer = dataTable[1].player[targetId]
                                        if playerDataTable[GetPlayerId(targetPlayer)].boolean[0] then
                                            call thistype.initSwitch(triggerPlayer,targetPlayer)
                                        else
                                            debug call BJDebugMsg("[Switch System]: Invalid data value for player " + GetPlayerName(targetPlayer))
                                            call thistype.showList(triggerPlayer)
                                        endif
                                            
                                    elseif IsPlayerInForce(triggerPlayer,dataTable[1].force[0]) then 
                                    
                                        if dataTable[0].real[0] > 9 and chatsStringLength >= stringLength+3 then 
                                        
                                            set chatString = SubString(chatString,0,stringLength+3) 
                                            set targetId = S2I(SubString(chatString,stringLength+1,2))
                                            
                                            if targetId <= 0 or targetId > dataTable[0].real[0] then 
                                                set chatString = SubString(chatString,0,stringLength+2) 
                                                set targetId = S2I(SubString(chatString,stringLength+1,1))
                                            endif
                                            
                                        else
                                        
                                            set chatString = SubString(chatString,0,stringLength+2) //i need to strip off strings.. damn substring native
                                            set targetId = S2I(SubString(chatString,stringLength+1,1))
                                            
                                        endif
                                        
                                        set targetPlayer = dataTable[0].player[targetId]
                                        
                                        if playerDataTable[GetPlayerId(targetPlayer)].boolean[0] then
                                            call thistype.initSwitch(triggerPlayer,targetPlayer)
                                        else
                                            debug call BJDebugMsg("[Switch System]: Invalid data value for player " + GetPlayerName(targetPlayer))
                                            call thistype.showList(triggerPlayer)
                                        endif
                                            
                                    else
                                        debug call BJDebugMsg("[Switch System]: Player " + GetPlayerName(triggerPlayer) + " is in an unknown force. Exiting")
                                        call thistype.showList(triggerPlayer)
                                    endif
                                debug else
                                    debug call BJDebugMsg("[Switch System]: Invalid force data. System crashed")
                                endif
                               
                            endif
                        else
                            call DisplayTimedTextToPlayer(triggerPlayer,0,0,3,"This command is disabled for use.")
                        endif
                    else
                        call DisplayTimedTextToPlayer(triggerPlayer,0,0,5,"Please wait for the ongoing switch voting to finish.")
                    endif
                    
                elseif onGoing then
                    if chatString == COM_OK then
                        call recountPlayers()
                        
                        if this.vote >= (this.voters-1) then //let us first double check before everything else
                        
                            call PauseTimer(this.time)
                            call thistype.switchSuccess()
                            
                        elseif not this.voted[playerId] then //double check fails? check if this player is allowed to vote
                        
                            set this.vote = this.vote + 1
                            
                            if this.vote >= (this.voters-1) then //perform a check
                                call PauseTimer(this.time)
                                call thistype.switchSuccess()
                            else
                                set this.voted[playerId] = true 
                                call DisplayTimedTextToPlayer(triggerPlayer,0,0,8,GetPlayerNameColored(triggerPlayer) + " has accepted the switch offer. (" + I2S(this.vote) + "/" + I2S(this.voters) + ")")
                            endif
                            
                        debug else
                            debug call BJDebugMsg("[Switch System]: Unable to process command " + COM_OK + ". Player " + GetPlayerName(triggerPlayer) + " has already voted.")
                        endif
                        
                    elseif chatString == COM_NO then
                    
                        call ClearTextMessages()
                        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,5,GetPlayerNameColored(triggerPlayer) + "|CFFFF0303 has declined the switch offer.|r") // so sad someone denied the offer
                        set onGoing = false 
                        call this.destroy()
                        call PauseTimer(this.time)
                        
                    endif
                endif
            elseif SubString(chatString,0,stringLength) == COM then
                call DisplayTimedTextToPlayer(triggerPlayer,0,0,5,COM + " is disabled for this game.")
            endif
            set triggerPlayer = null
            set targetPlayer = null
        endmethod
    endstruct
    
    private function RecalculateForce takes force f, integer tid returns nothing
        local integer forceHandle = GetHandleId(f)
        local integer index = 0
        local integer forceIndex = 1
        local player p = null
        if HaveSavedInteger(ht,forceHandle,0) then
            loop
                exitwhen index > 11
                set p = Player(index)
                if IsPlayerInForce(p,f) and playerDataTable[index].real[0] == tid then
                    set dataTable[tid].player[forceIndex] = p
                    set playerDataTable[index].real[1] = forceIndex
                    set forceIndex = forceIndex + 1
                endif
                set index = index + 1
            endloop
            
            set dataTable[tid].real[0] = forceIndex
        debug else
            debug call BJDebugMsg("[Switch System]: Failed to update force " + I2S(forceHandle) + ". Force not registered.")
        endif
        set p = null
    endfunction
    
    /*
    * this function is for filling out missing index..
    */
    private function FindForceIndex takes nothing returns integer
        local integer i = 0
        local integer int
        /*
        * we'll use a dummy table to find a missing index in the system's index
        */
        if dataTable[13].boolean[0] and inQueue > 0 then 
            set int = R2I(dataTable[13].real[inQueue])//why no intagahs
            set inQueue = inQueue - 1 //just for stacked force
            return int 
        endif
        return max
    endfunction
    
    function AddSwitchForce takes force f returns nothing   
        local integer forceHandle = GetHandleId(f)
        local integer i = 0
        local integer forceIndex = 1
        local integer recycledIndex = FindForceIndex()
        static if not TWO_DIG then
            local integer maxForces = 2
        else
            local integer maxForces = 11
        endif
        local player p = null
        
        if max < maxForces then 
            if not dataTable[recycledIndex].boolean[0] then
                call SaveInteger(ht,forceHandle,0,recycledIndex)
                set dataTable[recycledIndex].force[0] = f
                set dataTable[recycledIndex].boolean[0] = true
                loop
                    exitwhen i > 11
                    set p = Player(i)
                    if IsPlayerInForce(p,f) and not playerDataTable[i].boolean[0] then
                        set dataTable[recycledIndex].player[forceIndex] = p
                        set playerDataTable[i].real[0] = recycledIndex
                        set playerDataTable[i].real[1] = forceIndex
                        set playerDataTable[i].boolean[0] = true 
                        set forceIndex = forceIndex + 1
                    endif
                    set i = i + 1
                endloop
                set dataTable[recycledIndex].real[0] = forceIndex
                
                if dataTable[13].boolean[0] then //just to stop max from increasing
                    if inQueue <= 0 then //successfully filled missing index
                        set inQueue = 0
                        set dataTable[13].boolean[0] = false
                    endif
                else
                    set max = max + 1
                endif
                
            debug else
                debug call BJDebugMsg("[Switch System]: Force " + I2S(forceHandle) + " is alredy registered.")
            endif
        debug else
            debug call BJDebugMsg("[Switch System]: Only a maximum of " + I2S(maxForces+1) + ". Denying request for " + I2S(forceHandle))
        endif
        set p = null
    endfunction
    
    function RemoveSwitchForce takes force f returns nothing
        local integer forceHandle = GetHandleId(f)
        local integer i = 0
        local integer playerForceIndex = 0
        if HaveSavedInteger(ht,forceHandle,0) and not onGoing then
            loop
                exitwhen i == max
                if dataTable[i].force[0] == f then
                    loop
                        exitwhen playerForceIndex > 11
                        if playerDataTable[playerForceIndex].real[0] == i and IsPlayerInForce(Player(playerForceIndex),f) then
                            call playerDataTable[playerForceIndex].flush()
                        endif
                        set playerForceIndex = playerForceIndex + 1
                    endloop
                    call dataTable[i].flush()
                    call FlushChildHashtable(ht,forceHandle)
                    set dataTable[13].boolean[0] = true 
                    set inQueue = inQueue + 1
                    set dataTable[13].real[inQueue] = i
                    exitwhen true 
                endif
                set i = i + 1
            endloop
        debug else
            debug call BJDebugMsg("[Switch System]: Unable to remove force " + I2S(forceHandle) + ". Either this force is not registered or a switch process is ongoing.")
        endif
    endfunction
    
    function GetPlayerSwitchIndex takes player p returns integer 
        return R2I(playerDataTable[GetPlayerId(p)].real[1])
    endfunction
    
    static if not GUI_FRIENDLY then
    
    function RegisterOnSwitchSuccess takes code c returns nothing
        call VoteSwitch.Ev.register(Filter(c))
    endfunction
    
    endif
    
    function GetPlayerSwitchForce takes player p returns force 
        return dataTable[R2I(playerDataTable[GetPlayerId(p)].real[0])].force[0]
    endfunction
    
    function IsSwitchOnGoing takes nothing returns boolean
        return onGoing
    endfunction
    
    function SwitchEnable takes boolean b returns boolean
        if not onGoing then
            set switchOn = b
            return true 
        endif
        return false 
    endfunction
    
    function IsSwitchEnabled takes nothing returns boolean
        return switchOn
    endfunction
    
    function UpdatePlayerSwitchForce takes player p returns boolean
        local integer i
        local integer playerId = GetPlayerId(p)
        local integer targetPlayerForceTableId = R2I(playerDataTable[playerId].real[0])
        local integer h = GetHandleId(dataTable[targetPlayerForceTableId].force[0])
        if not onGoing then
            if HaveSavedInteger(ht,h,0) and playerDataTable[playerId].boolean[0] then
                set i = LoadInteger(ht,h,0)
                call RecalculateForce(dataTable[targetPlayerForceTableId].force[0],targetPlayerForceTableId)
            debug else
                debug call BJDebugMsg("[Switch System]: Cannot update player " + GetPlayerName(p) + "'s registered force " + I2S(h) + ". Input values are not registered on this system.")
            endif
        debug else
            debug call BJDebugMsg("[Switch System]: Cannot update "+ GetPlayerName(p) + "'s registered force: " + I2S(h) + ". A switch process is ongoing.")
        endif
        return false 
    endfunction
    
    function SwitchForceRemovePlayer takes force f, player p returns boolean
        local integer forceHandle = GetHandleId(f)
        local integer playerId = GetPlayerId(p)
        if not onGoing then
            if HaveSavedInteger(ht,forceHandle,0) and playerDataTable[playerId].boolean[0] then
                call ForceRemovePlayer(f,p)
                call RecalculateForce(f,R2I(playerDataTable[playerId].real[0]))
                call playerDataTable[playerId].flush()
            debug else
                debug call BJDebugMsg("[Switch System]: Cannot remove player " + GetPlayerName(p) + " from force " + I2S(forceHandle) + ". Input values are not registered on this system.")
            endif
        debug else
            debug call BJDebugMsg("[Switch System]: Unable to remove player "+ GetPlayerName(p) + " from force " + I2S(forceHandle) + ". A switch process is ongoing.")
        endif
        return false 
    endfunction
    
    function AddPlayerToSwitchForce takes force f, player p returns boolean
        local integer forceHandle = GetHandleId(f)
        local integer i
        local integer playerForceIndex
        local integer playerId = GetPlayerId(p)
        if HaveSavedInteger(ht,forceHandle,0) and not playerDataTable[playerId].boolean[0] then
            set i = LoadInteger(ht,forceHandle,0)
            set playerForceIndex = R2I(dataTable[i].real[0])
            /*
            * no more checking! Just to allow unused slots to be registered on the system :/
            */
            call ForceAddPlayer(f,p)
            set dataTable[i].player[playerForceIndex] = p
            set playerDataTable[playerId].real[0] = i
            set playerDataTable[playerId].real[1] = playerForceIndex
            set playerDataTable[playerId].boolean[0] = true 
            set dataTable[i].real[0] = playerForceIndex + 1
            return true
        debug else
            debug call BJDebugMsg("[Switch System]: Unable to add "+GetPlayerName(p)+" to force " + I2S(forceHandle))
        endif
        return false
    endfunction
    
    private function OnDestroyForce takes force f returns nothing
        static if AUTO_PURGE then
            call RemoveSwitchForce(f)
        endif
        if onGoing and (IsPlayerInForce(VoteSwitch.sourcePlayer,f) or IsPlayerInForce(VoteSwitch.targetSwitchPlayer,f)) then
            set onGoing = false 
            static if not DEBUG_MODE then
                call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,5,"Switch process has been cancelled by the game.")
            else
                call BJDebugMsg("[Switch System]: Ongoing switch process terminated unexpectedly. Force " + I2S(GetHandleId(f)) + " was destroyed during the process.")
            endif
        endif
    endfunction
    
    private function OnClearForce takes force f returns nothing
        static if AUTO_PURGE then
            local integer forceHandle = GetHandleId(f)
            local integer i = 0
            local integer playerForceIndex = 0
            if HaveSavedInteger(ht,forceHandle,0) then
                loop
                    exitwhen i == max
                    if dataTable[i].force[0] == f then
                        loop
                            exitwhen playerForceIndex > 11
                            if playerDataTable[playerForceIndex].real[0] == i and IsPlayerInForce(Player(playerForceIndex),f) then
                                call playerDataTable[playerForceIndex].flush()
                            endif
                            set playerForceIndex = playerForceIndex + 1
                        endloop
                        exitwhen true 
                    endif
                    set i = i + 1
                endloop
            endif
        endif
        
        if onGoing and (IsPlayerInForce(VoteSwitch.sourcePlayer,f) or IsPlayerInForce(VoteSwitch.targetSwitchPlayer,f)) then
            set onGoing = false 
            static if not DEBUG_MODE then
                call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,5,"Switch process has been cancelled by the game.")
            else
                call BJDebugMsg("[Switch System]: Ongoing switch process terminated unexpectedly. Force " + I2S(GetHandleId(f)) + " was cleared during the process.")
            endif
        endif
        
    endfunction
        
    private function OnForceRemovePlayer takes force f, player p returns nothing
        if onGoing and not VoteSwitch.ended and (VoteSwitch.sourcePlayer == p or VoteSwitch.targetSwitchPlayer == p ) then
            set onGoing = false
            static if not DEBUG_MODE then
                call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,5,"Switch process has been cancelled by the game.")
            else
                call BJDebugMsg("[Switch System]: Ongoing switch process terminated unexpectedly. Player " + GetPlayerName(p) + " was removed from force " + I2S(GetHandleId(f)) + " during the process. ")
            endif
        endif
    endfunction
        
    private function OnForceRemovePlayerSimp takes player p, force f returns nothing
        if onGoing and not VoteSwitch.ended and (VoteSwitch.sourcePlayer == p or VoteSwitch.targetSwitchPlayer == p ) then
            set onGoing = false
            static if not DEBUG_MODE then
                call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,5,"Switch process has been cancelled by the game.")
            else
                call BJDebugMsg("[Switch System]: Ongoing switch process terminated unexpectedly. Player " + GetPlayerName(p) + " was removed from force " + I2S(GetHandleId(f)) + " during the process. (Simp)")
            endif
        endif
    endfunction
    
    private module O
        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            local integer i = 0
            set dataTable = TableArray[14]
            set playerDataTable = TableArray[12]
            set VoteSwitch.time = CreateTimer()
            set stringLength = StringLength(COM)
            static if not GUI_FRIENDLY then
                set VoteSwitch.Ev = Event.create()
            endif
            loop
                exitwhen i > 11
                call TriggerRegisterPlayerChatEvent(t,Player(i),"",false)
                set i = i + 1
            endloop
            call TriggerAddAction(t,function VoteSwitch.start)
            set t = null
        endmethod
    endmodule
    
    private struct H extends array
        implement O
    endstruct
    
    hook DestroyForce OnDestroyForce
    hook ForceClear OnClearForce
    hook ForceRemovePlayer OnForceRemovePlayer
    hook ForceRemovePlayerSimple OnForceRemovePlayerSimp 
endlibrary

PS. It's not intended to collide with Mag's ASS system since its initials are ASS too.. lol
You see? I'm using Nes Nes' comment header template lol


v1.4.1
- Fixed a bug wherein a disabled message is shown even though COM command isn't entered.
v1.4.0
- New function: GetPlayerSwitchIndex. Returns the player's current index in its switch force.
v1.3.2
- Fixed handling of missing index which will make further registration of forces impossible if there are any missing force index.
- Optimized some functions.
- Improved test map documentation
- Fixed some minor bugs.
v1.3.1
- Improved debug messages
- Fixed a bug wherein if there are two or more forces are destroyed, only one index can be filled.
- Added additional checks for some circumstances (just to be sure with the data provided)
v1.3.0
- Fixed function names
- Fixed some little bugs
- Added a difference between the debug messages of ForceRemovePlayer and ForceRemovePlayerSimple
- Fixed test map codes (copy-only variables are now separated)
v1.2.2
- Updated for readability.
v1.2.1
- Added an auto purge option
- Reworked hook functions
- Optimized data retrieval functions
v1.2.0
- New function: RemoveSwitchForce
- Added safety measures whenever a force is modified during a switch process
- Optimized API functions
- Improved command checking
- Fixed test map problems
v1.1.0
- Allows more than 10 players in a force.
- Added support for numbers 01 - 12 (switch list)
- New command pattern when 3 forces or more (TWO_DIG) mode is enabled: COM + 0-1 (e.g. -switch 0-1)
- Optimized API functions
v1.0.0
- Initial Release


Keywords:
switch, system, advance, player, group, multiple, unused, vote, jim7777
Contents

Advanced Switch System v1.4.1 (Map)

Reviews
26th Apr 2012 Bribe: PROS - High calibur program, neat coding, decent API, great flexibility. CONS - Doesn't have the "wow" factor to merit true 5/5. Usefulness is a tough spot to fit this in as I have never needed it. Approved 4/5.

Moderator

M

Moderator

26th Apr 2012
Bribe:

PROS - High calibur program, neat coding, decent API, great flexibility.
CONS - Doesn't have the "wow" factor to merit true 5/5. Usefulness is a tough spot to fit this in as I have never needed it.

Approved 4/5.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
I really like the docs ; D. I could actually read them ^_^. The actual code and comments in the code are not so great though : \.

JASS:
                                            set t = S2I(SubString(str,stringLength+1,1))
                                        endif
                                        
                                        set tp = TB[0].player[t]
                                        if PTB[GetPlayerId(tp)].boolean[0] then

With the advent of a working optimizer, there are no longer any excuses for ugly code ;o.
 
By ugly code, he means you're using short variable names :p
You need to use longer and meaningful names so that we may actually read and understand your code in order to review it.
Despite the fact that it's unreadable, I'll still review this.

  • Instead of constantly calling DisplayTimedTextToPlayer in a loop, you could declare a string variable and concatenate it with the text you want to display, making sure to add a linebreak at the end of it. You would then display that string at the end of the function. This is an awesome optimization devised by le me
  • JASS:
    set str = SubString(str,0,stringLength+2) //i need to strip off strings.. damn substring native
    set t = S2I(SubString(str,stringLength+1,1))
    This can be changed to:
    set t = S2I(SubString(SubString(str,0,stringLength+2),stringLength+1,1))
    I made sure of this by reading the entire function to see if the str variable value is needed anywhere after you execute those 2 lines of code.
  • The functions onClearForce and onDestroyForce should be changed to be called OnClearForce and OnDestroyForce because according to JASS convention, functions should be WrittenLikeThis. methodsAndVariables are only written like this.

I'd add more content to this review, but I'm pretty tired now :p
 
Level 10
Joined
May 27, 2009
Messages
494
ohh onClearForce was recently a member of the struct >:D (version 1.2.0)
for the ugly code... i just used acronyms lol, for shorter var names but lol still can't read it D: like pid for player id or PTB for player table lol or tp for target player or t for target

i'll update this whenever i can 'cause i'm having an internet connection of 512bytes/s - 5kbp/s
 
Level 4
Joined
Apr 11, 2012
Messages
67
why i got line error on Switch System,Table,GetPlayerNameColored
How can i fix it ? please help me
Thanks
 
Level 10
Joined
May 27, 2009
Messages
494
mehh.. they should follow the documentation because it's clearly written there that those variables should be copied.. LOL

but anyways, i'm having a problem with that thing too >:D since the variable name is too long when I write it in jass..

should update this thing when I have the time
 
Top