• 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.

Combo System v1.5.2

What system does

- This system will count the combo that a unit attack enemies. Affected only when a unit attack without using spell.
How to use
JASS:
//        How to use:
//                   - To start counting combo from a unit, use this function:
//      function UnitComboAdd takes unit whichUnit,real Scale,real ComboDisableTimer returns nothing
//                   => whichUnit - Which unit you want to count
//                   => Scale - Scaling size of the unit (Make the height more accurate)
//                   => ComboDisableTimer - Timer of the combo, if the unit stop attacking the enemies,
//                      Combo will be destroyed and reset to 0.
//                   - To start counting combo from a unit, use this function (Ex):
//      function UnitComboAddEx takes unit whichUnit,real Scale,real ComboDisableTimer,real whichHeight,string decorateLeft,string decorateRight,string textCombo returns nothing
//                   => whichUnit - Which unit you want to count
//                   => Scale - Scaling size of the unit (Make the height more accurate)
//                   => ComboDisableTimer - Timer of the combo, if the unit stop attacking the enemies,
//                      Combo will be destroyed and reset to 0.
//                   => whichHeight: Your height value
//                   => decorateLeft: Your decorate left text
//                   => decorateRogjt: Your decorate right text
//                   => textCombo: Your text combo text
//                   - To stop counting combo from a unit, use this function:
//      function UnitComboRemove takes unit whichUnit returns nothing
//                   => whichUnit - Which unit you want to remove from this system
//                   - To get combo count of a unit, use this function:
//      function GetUnitComboCount takes unit whichUnit returns integer
//                   => whichUnit - Which unit you want to get combo count
//                   - To change scale value from a unit, use this function:
//      function SetTextComboScale takes unit whichUnit,real whichScale returns nothing
//                   => whichUnit - Which unit you want to change
//                   => whichScale - Your scale value
//                   - To change height value from a unit, use this function:
//      function SetTextComboheighttakes unit whichUnit,real whichHeight returns nothing
//                   => whichUnit - Which unit you want to change
//                   => whichHeight - Your height value
//                   - To change text decorate left from a unit, use this function:
//      function SetTextComboDecorateLeft takes unit whichUnit,string whichText returns nothing
//                   => whichUnit - Which unit you want to change
//                   => whichText - Your text
//                   - To change text decorate right from a unit, use this function:
//      function SetTextComboDecorateRight takes unit whichUnit,string whichText returns nothing
//                   => whichUnit - Which unit you want to change
//                   => whichText - Your text
//                   - To change text combo from a unit, use this function:
//      function SetTextComboText takes unit whichUnit,string whichText returns nothing
//                   => whichUnit - Which unit you want to change
//                   => whichText - Your text
//                   - To register unit combo event, use this function:
//      function TriggerRegisterUnitComboEvent takes unit whichUnit,integer whichCombo,trigger whichTrigger returns nothing
//                   => whichUnit - Which unit you want to register
//                   => whichCombo - Which combo you want to register
//                   => whichTrigger - Which trigger you want to register
//                   - To register unit any combo event, use this function:
//      function TriggerRegisterUnitAnyComboEvent takes unit whichUnit,trigger whichTrigger returns nothing
//                   => whichUnit - Which unit you want to register
//                   => whichTrigger - Which trigger you want to register
//                   - To remove any event combo event, use this function:
//      function RemoveTriggerComboEvent takes unit whihUnit, trigger whichTrigger returns nothing
//                   => whichTrigger - Which trigger you want to remove
//                   => whichUnit - Which unit you want to remove the triger event
System Code
JASS:
//----------------------------------------------------------------------
//----------------------------------------------------------------------
//              C O M B O   S Y S T E M
//                  Made by: Nyuu
//                  Version: 1.5.2
//
//        What system does:
//              - This system will count the combo that a unit attack
//               enemies. Affected only when a unit attack without using spell.
//        
//        How to use:
//                   - To start counting combo from a unit, use this function:
//      function UnitComboAdd takes unit whichUnit,real Scale,real ComboDisableTimer returns nothing
//                   => whichUnit - Which unit you want to count
//                   => Scale - Scaling size of the unit (Make the height more accurate)
//                   => ComboDisableTimer - Timer of the combo, if the unit stop attacking the enemies,
//                      Combo will be destroyed and reset to 0.
//                   - To start counting combo from a unit, use this function (Ex):
//      function UnitComboAddEx takes unit whichUnit,real Scale,real ComboDisableTimer,real whichHeight,string decorateLeft,string decorateRight,string textCombo returns nothing
//                   => whichUnit - Which unit you want to count
//                   => Scale - Scaling size of the unit (Make the height more accurate)
//                   => ComboDisableTimer - Timer of the combo, if the unit stop attacking the enemies,
//                      Combo will be destroyed and reset to 0.
//                   => whichHeight: Your height value
//                   => decorateLeft: Your decorate left text
//                   => decorateRogjt: Your decorate right text
//                   => textCombo: Your text combo text
//                   - To stop counting combo from a unit, use this function:
//      function UnitComboRemove takes unit whichUnit returns nothing
//                   => whichUnit - Which unit you want to remove from this system
//                   - To get combo count of a unit, use this function:
//      function GetUnitComboCount takes unit whichUnit returns integer
//                   => whichUnit - Which unit you want to get combo count
//                   - To change scale value from a unit, use this function:
//      function SetTextComboScale takes unit whichUnit,real whichScale returns nothing
//                   => whichUnit - Which unit you want to change
//                   => whichScale - Your scale value
//                   - To change height value from a unit, use this function:
//      function SetTextComboheighttakes unit whichUnit,real whichHeight returns nothing
//                   => whichUnit - Which unit you want to change
//                   => whichHeight - Your height value
//                   - To change text decorate left from a unit, use this function:
//      function SetTextComboDecorateLeft takes unit whichUnit,string whichText returns nothing
//                   => whichUnit - Which unit you want to change
//                   => whichText - Your text
//                   - To change text decorate right from a unit, use this function:
//      function SetTextComboDecorateRight takes unit whichUnit,string whichText returns nothing
//                   => whichUnit - Which unit you want to change
//                   => whichText - Your text
//                   - To change text combo from a unit, use this function:
//      function SetTextComboText takes unit whichUnit,string whichText returns nothing
//                   => whichUnit - Which unit you want to change
//                   => whichText - Your text
//                   - To register unit combo event, use this function:
//      function TriggerRegisterUnitComboEvent takes unit whichUnit,integer whichCombo,trigger whichTrigger returns nothing
//                   => whichUnit - Which unit you want to register
//                   => whichCombo - Which combo you want to register
//                   => whichTrigger - Which trigger you want to register
//                   - To register unit any combo event, use this function:
//      function TriggerRegisterUnitAnyComboEvent takes unit whichUnit,trigger whichTrigger returns nothing
//                   => whichUnit - Which unit you want to register
//                   => whichTrigger - Which trigger you want to register
//                   - To remove any event combo event, use this function:
//      function RemoveTriggerComboEvent takes unit whihUnit, trigger whichTrigger returns nothing
//                   => whichTrigger - Which trigger you want to remove
//                   => whichUnit - Which unit you want to remove the triger event
//
//         How to import:
//                       - Copy Combo system code and play with the configuration below.
//                       - Go to Variables panel (Ctrl + B) copy variable System_ComboUnit, System_ComboCount and all the variables of GDD
//                       - Be sure "Automatically create unknown variables while pasting trigger data" is
//                         enabled in the World Editor general preferences.
//          
//          Credit:
//                  - Weep - Damage Detection System - http://www.hiveworkshop.com/forums/spells-569/gui-friendly-damage-detection-v1-2-1-a-149098/?prev=search%3DDamage%2520Detection%26d%3Dlist%26r%3D20
//----------------------------------------------------------------------
//----------------------------------------------------------------------
library ComboSystem
    
    private module Init
        static method onInit takes nothing returns nothing
            call Init()
        endmethod
    endmodule
    
    globals
        //----------------------------------------------------------------
        //                     SYSTEM CONFIGURABLE
        //System period
        private constant real PERIODIC              = .0312500
        //Default size of the text tag
        private constant real TEXT_TAG_SIZE         = 10.
        //Limited size of the text tag when it increasing
        private constant real TEXT_TAG_SIZE_LIMIT   = 20.
        //Text tag size increase per period
        private constant real SIZE_INCREASE         = 2.
        //Text tag size decrease per period
        private constant real SIZE_DECREASE         = 3.
        //Height default of the text tag
        private constant real HEIGHT_DEFAULT        = 150.
        //
        private constant real TEXT_CENTER           = -65.
        //Text tag color decrease per period
        private constant integer TEXT_COLOR_DECREASE= 12
        
        private constant string COMBO_TEXT          = "Combo - "
        private constant string DECORATE_TEXT_LEFT  = "=>"
        private constant string DECORATE_TEXT_RIGHT = "<="
        //----------------------------------------------------------------
        
        //----------------------------------------------------------------
        //                       NON - CONFIGURABLE                     //
        /*-*/private integer Index                  = 0              /*-*/
        /*-*/private integer Counter                = 0              /*-*/
        /*-*/private integer EventCount             = 0              /*-*/
        /*-*/private integer DataIndex                               /*-*/
        
        /*-*/private integer array StructIndex                       /*-*/
        /*-*/private integer array ComboCount                        /*-*/
        /*-*/private integer array Color                             /*-*/
        /*-*/private integer array ComboCounter                      /*-*/
        
        /*-*/private real array SizeValue                            /*-*/
        /*-*/private real array ScaleValue                           /*-*/
        /*-*/private real array ComboTime                            /*-*/
        /*-*/private real array ComboTimeCounter                     /*-*/
        /*-*/private real array Height                               /*-*/
        /*-*/private real array HeightSetting                        /*-*/
        
        /*-*/private trigger array EventTrigger                      /*-*/
        
        /*-*/private player array ComboPlayer                        /*-*/
        
        /*-*/private unit array ComboUnit                            /*-*/
        /*-*/private unit array EventUnit                            /*-*/
        
        /*-*/private boolean array SizeBlock                         /*-*/
        /*-*/private boolean array AttackDetector                    /*-*/
        
        /*-*/private texttag array TextTag                           /*-*/
        
        /*-*/private string array Value                              /*-*/
        /*-*/private string array DecorateLeft                       /*-*/
        /*-*/private string array DecorateRight                      /*-*/
        /*-*/private string array ComboText                          /*-*/
        
        /*-*/private hashtable    ComboHashtable                     /*-*/
        
        /*-*/private constant timer TIMER = CreateTimer()            /*-*/
        
        /*-*/private constant location LOC = Location(0.,0.)         /*-*/
        //----------------------------------------------------------------
    endglobals
    
    private function eventConditions takes integer whichCombo, integer index returns nothing
    
        set udg_System_ComboUnit = EventUnit[index]
        set udg_System_ComboCount = whichCombo
    
        if ComboCounter[index] == whichCombo or ComboCounter[index] == -1 then
            call TriggerExecute(EventTrigger[index])
        endif
    endfunction
    
    private function DoesUnitExist takes integer id returns boolean
        set DataIndex=LoadInteger(ComboHashtable,0,id)
        return DataIndex != 0
    endfunction
    
    private function removeKey takes boolean isAny,integer handleId1,integer handleId2 returns nothing
        local integer key
        
        if isAny then
            set key=1
        else
            set key=2
        endif
        
        call RemoveSavedInteger(ComboHashtable,key,handleId1)
        
        if DataIndex!=EventCount then
            call SaveInteger(ComboHashtable,key,handleId2,DataIndex)
        endif
    endfunction
    
    function RemoveTriggerComboEvent takes unit whichUnit, trigger whichTrigger returns nothing
        local integer hid=GetHandleId(whichUnit)
        
        if DoesUnitExist(hid) then
        
            set DataIndex=LoadInteger(ComboHashtable,1,hid)
            
            if EventTrigger[DataIndex] != whichTrigger then
                set DataIndex=LoadInteger(ComboHashtable,2,hid)
                
                if EventTrigger[DataIndex]!=whichTrigger then
                    debug call BJDebugMsg("This trigger doesn't exist in the system.")
                    return
                endif
            endif
        
            set EventUnit[DataIndex] = EventUnit[EventCount]
            set EventUnit[EventCount] = null
            set EventTrigger[DataIndex] = EventTrigger[EventCount]
            set EventTrigger[EventCount] = null
            set ComboCounter[DataIndex] = ComboCounter[EventCount]
            
            call removeKey(ComboCounter[DataIndex]!=-1,GetHandleId(whichUnit),GetHandleId(EventUnit[DataIndex]))
            
            set EventCount = EventCount - 1
            
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
    endfunction
    
    private function TriggerRegisterComboEventHelper takes unit whichUnit, integer whichCombo, trigger whichTrigger, integer hashIndex returns nothing
        local integer hid = GetHandleId(whichUnit)
        
        if DoesUnitExist(hid) then
            set EventCount = EventCount + 1
            call SaveInteger(ComboHashtable,hashIndex,hid,EventCount)
            set EventTrigger[EventCount] = whichTrigger
            set EventUnit[EventCount] = whichUnit
            set ComboCounter[EventCount] = whichCombo
        else
            debug call BJDebugMsg("This unit doesn't exists in the system.")
        endif
    endfunction

    function TriggerRegisterComboEvent takes unit whichUnit,integer whichCombo,trigger whichTrigger returns nothing
        if whichCombo > 0 then
            call TriggerRegisterComboEventHelper(whichUnit, whichCombo, whichTrigger, 1)
        else
            debug call BJDebugMsg("Combo = "+I2S(whichCombo)+" ?????, what kind of your combo 0.0..")
        endif
    endfunction
    
    function TriggerRegisterAnyComboEvent takes unit whichUnit,trigger whichTrigger returns nothing
        call TriggerRegisterComboEventHelper(whichUnit,-1,whichTrigger, 2)
    endfunction
    
    function GetUnitComboCount takes unit whichUnit returns integer
        if DoesUnitExist(GetHandleId(whichUnit)) then
            return ComboCount[DataIndex]
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
            
        return 0
    endfunction
    
    function UnitAddComboEx takes unit whichUnit,real Scale,real ComboDisableTimer,real whichHeight,string decorateLeft,string decorateRight,string comboText returns nothing
        local integer hid=GetHandleId(whichUnit)
        
        if not DoesUnitExist(hid) then
            set Index = Index + 1
            call SaveInteger(ComboHashtable,0,hid,Index)
            set ComboUnit[Index] = whichUnit
            set ComboCount[Index] = 0
            set ScaleValue[Index] = Scale
            set ComboTime[Index] = ComboDisableTimer
            set StructIndex[Index] = Index
            set DecorateLeft[Index] = decorateLeft
            set DecorateRight[Index] = decorateRight
            set ComboText[Index] = comboText
            set HeightSetting[Index] = whichHeight
            set ComboPlayer[Index] = GetOwningPlayer(ComboUnit[Index])
            
            set TextTag[Index] = CreateTextTag()
            call MoveLocation(LOC,GetUnitX(ComboUnit[Index]),GetUnitY(ComboUnit[Index]))
            call SetTextTagPosUnit(TextTag[Index],ComboUnit[Index],GetUnitFlyHeight(ComboUnit[Index])+GetLocationZ(LOC))
            call SetTextTagText(TextTag[Index],null,TEXT_TAG_SIZE)
            call SetTextTagSuspended(TextTag[Index],true)
            call SetTextTagPermanent(TextTag[Index],false)
        else
            debug call BJDebugMsg("This unit already exist in the system.")
        endif
        
    endfunction
    
    function UnitComboAdd takes unit whichUnit, real Scale, real ComboDisableTimer returns nothing
        call UnitAddComboEx(whichUnit, Scale, ComboDisableTimer, HEIGHT_DEFAULT, /*
            */ DECORATE_TEXT_LEFT, DECORATE_TEXT_RIGHT, COMBO_TEXT)
    endfunction
    
    function UnitComboRemove takes unit whichUnit returns nothing
        local integer hid=GetHandleId(whichUnit)
        
        if DoesUnitExist(hid) then
            set ComboUnit[DataIndex] = ComboUnit[Index]
            set ComboCount[DataIndex] = ComboCount[Index]
            set ScaleValue[DataIndex] = ScaleValue[Index]
            set ComboTime[DataIndex] = ComboTime[Index]
            set DecorateLeft[DataIndex] = DecorateLeft[Index]
            set DecorateRight[DataIndex] = DecorateRight[Index]
            set ComboText[DataIndex] = ComboText[Index]
            set HeightSetting[DataIndex] = HeightSetting[Index]
            set ScaleValue[DataIndex] = ScaleValue[Index]
            set ComboPlayer[DataIndex] = ComboPlayer[Index]
            call DestroyTextTag(TextTag[DataIndex])
            set TextTag[DataIndex] = TextTag[Index]
            set StructIndex[DataIndex] = StructIndex[Index]
            set StructIndex[Index] = 0
            set TextTag[Index] = null
            set ComboUnit[Index] = null
            
            call RemoveSavedInteger(ComboHashtable,0,hid)
            
            if DataIndex!=Index then
                call SaveInteger(ComboHashtable,0,GetHandleId(ComboUnit[DataIndex]),DataIndex)
            endif
            
            set Index = Index - 1
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
        
    endfunction
    
    function SetTextComboHeight takes unit whichUnit,real whichHeight returns nothing
        if DoesUnitExist(GetHandleId(whichUnit)) then
            set HeightSetting[DataIndex] = whichHeight
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
    endfunction
    
    function SetTextComboText takes unit whichUnit,string whichText returns nothing
        if DoesUnitExist(GetHandleId(whichUnit)) then
            set ComboText[DataIndex] = whichText
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
    endfunction
    
    function SetTextComboDecorateLeft takes unit whichUnit,string whichText returns nothing
        if DoesUnitExist(GetHandleId(whichUnit)) then
            set DecorateLeft[DataIndex] = whichText
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
    endfunction
    
    function SetTextComboDecorateRight takes unit whichUnit,string whichText returns nothing
        if DoesUnitExist(GetHandleId(whichUnit)) then
            set DecorateRight[DataIndex] = whichText
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
    endfunction
    
    function SetTextComboScale takes unit whichUnit,real whichScale returns nothing
        if DoesUnitExist(GetHandleId(whichUnit)) then
            set ScaleValue[DataIndex] = whichScale
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
    endfunction
    
    private struct ComboSystem extends array
    
        static method textTag takes string whichString,integer Indexx,real Scale returns nothing
                local thistype this = Indexx
                
                call SetTextTagText(TextTag[this],whichString,TEXT_TAG_SIZE)
                
                call SetTextTagVisibility(TextTag[this], GetLocalPlayer() == ComboPlayer[this])
                
                set ComboTimeCounter[this] = ComboTime[this]
                set SizeValue[this] = TEXT_TAG_SIZE
                set SizeBlock[this] = false
                set Value[this] = whichString
                set Color[this] = 255
                set Height[this] = HeightSetting[this]*ScaleValue[this]
                call SetTextTagColor(TextTag[this],255,255,255,255)
                
                set Counter = Counter + 1
                if Counter == 1 then
                    call TimerStart(TIMER,PERIODIC,true,function thistype.onPeriodic)
                endif
                
            endmethod
        
        static method damageDetection takes nothing returns boolean
            
            if IsUnitEnemy(udg_GDD_DamageSource,GetOwningPlayer(udg_GDD_DamagedUnit)) then
                if DoesUnitExist(GetHandleId(udg_GDD_DamageSource)) then
                    if AttackDetector[DataIndex] then
                        set ComboCount[DataIndex] = ComboCount[DataIndex] + 1
                        call textTag(DecorateLeft[DataIndex]+ComboText[DataIndex]+I2S(ComboCount[DataIndex])+DecorateRight[DataIndex],DataIndex,ScaleValue[DataIndex])
                        call eventConditions(1,DataIndex)
                        call eventConditions(2,DataIndex)
                    endif
                endif
            endif
            
            return false
        endmethod
        
        private static constant real cos= TEXT_CENTER * Cos(.0174533)
        private static constant real sin= TEXT_CENTER * Sin(.0174533)
        
       static method onPeriodic takes nothing returns nothing
            local integer i = 1
            local thistype this
            local real x
            local real y
            
            loop
                exitwhen i > Index
                set this = StructIndex[i]
                
                if GetUnitTypeId(ComboUnit[this]) != 0 then
                    if not SizeBlock[this] then
                        set SizeValue[this] = SizeValue[this] + SIZE_INCREASE
                        set SizeBlock[this] = SizeValue[this] >= TEXT_TAG_SIZE_LIMIT
                    else
                        if SizeValue[this] > TEXT_TAG_SIZE then
                            set SizeValue[this] = SizeValue[this] - SIZE_DECREASE
                        endif
                    endif
                    
                    set x = GetUnitX(ComboUnit[this])+cos
                    set y = GetUnitY(ComboUnit[this])+sin
                    
                    call MoveLocation(LOC,x,y)
                
                    call SetTextTagColor(TextTag[this],255,255,255,Color[this])
                    call SetTextTagText(TextTag[this],Value[this],SizeValue[this]*0.0023)
                    
                    call SetTextTagPos(TextTag[this],x,y,GetUnitFlyHeight(ComboUnit[this])+GetLocationZ(LOC)+Height[this])
                    
                    if ComboTimeCounter[this] > 0. and not IsUnitType(ComboUnit[this],UNIT_TYPE_DEAD) then
                        set ComboTimeCounter[this] = ComboTimeCounter[this] - PERIODIC
                    else
                        if Color[this] > 0 then
                            set Color[this] = Color[this] - TEXT_COLOR_DECREASE
                            set SizeValue[this] = SizeValue[this] + SIZE_INCREASE
                        else
                            set ComboCount[this] = 0
                            call SetTextTagVisibility(TextTag[this],false)
                            
                            set Counter = Counter - 1
                            
                            if Counter == 0 then
                                call PauseTimer(TIMER)
                            endif
                        endif
                    endif
                else
                    call UnitComboRemove(ComboUnit[this])
                    
                    set Counter = Counter - 1
                            
                    if Counter == 0 then
                        call PauseTimer(TIMER)
                    endif
                endif
                
                set i = i + 1
            endloop
        endmethod
        
        static method attackDetection takes nothing returns boolean
            if DoesUnitExist(GetHandleId(GetAttacker())) then
                set AttackDetector[DataIndex] = true
            endif
            
            return false
        endmethod
        
        static method spellDetection takes nothing returns boolean
            if DoesUnitExist(GetHandleId(GetTriggerUnit())) then
                set AttackDetector[DataIndex] = false
            endif
            
            return false
        endmethod
        
        static method Init takes nothing returns nothing
            local trigger damageTrigger = CreateTrigger()
            local trigger attackTrigger = CreateTrigger()
            local trigger spellTrigger = CreateTrigger()
            local integer i = 0
            
            set ComboHashtable=InitHashtable()
            
            loop
                exitwhen i > 15
                
                call TriggerRegisterPlayerUnitEvent(attackTrigger,Player(i),EVENT_PLAYER_UNIT_ATTACKED,null)
                call TriggerRegisterPlayerUnitEvent(spellTrigger,Player(i),EVENT_PLAYER_UNIT_SPELL_CAST,null)
                
                set i = i + 1
            endloop
            
            call TriggerRegisterVariableEvent(damageTrigger, "udg_GDD_Event", EQUAL, 0 )
            
            call TriggerAddCondition(damageTrigger,function thistype.damageDetection)
            call TriggerAddCondition(attackTrigger,function thistype.attackDetection)
            call TriggerAddCondition(spellTrigger,function thistype.spellDetection)
            
            set damageTrigger = null
            set attackTrigger = null
            set spellTrigger = null
            
        endmethod
        
        implement Init
    endstruct
    
endlibrary
Test trigger
  • Hero Create
    • Events
      • Player - Player 1 (Red) types a chat message containing -test as An exact match
    • Conditions
    • Actions
      • Custom script: set udg_TestMap = CreateUnit(Player(0),'Hpal',0.,0.,0.)
  • Combo 20
    • Events
    • Conditions
    • Actions
      • Game - Display to (All players) for 30.00 seconds the text: Cool, you get 20 co...
      • Player - Set (Owner of System_ComboUnit) Current gold to 99999
  • Combo Any
    • Events
    • Conditions
    • Actions
      • Game - Display to (All players) for 30.00 seconds the text: (Your Combo: + (String(System_ComboCount)))
  • Combo Add
    • Events
      • Unit - A unit enters (Playable map area)
    • Conditions
    • Actions
      • Custom script: call UnitComboAdd(GetEnteringUnit(),1.,1.)
      • -------- ----------------------------------------- --------
      • Custom script: call TriggerRegisterComboEvent(udg_TestMap,20,gg_trg_Combo_20)
      • Custom script: call TriggerRegisterAnyComboEvent(udg_TestMap,gg_trg_Combo_Any)
      • -------- ----------------------------------------- --------
      • Game - Display to (All players) for 30.00 seconds the text: -remove to remove c...
      • Trigger - Turn on Combo Remove <gen>
      • Trigger - Turn on Combo Add Again <gen>
      • Trigger - Turn on Combo Change <gen>
      • Trigger - Turn on Remove Event <gen>
      • Trigger - Turn off (This trigger)
  • Combo Remove
    • Events
      • Player - Player 1 (Red) types a chat message containing -remove as An exact match
    • Conditions
    • Actions
      • Custom script: call UnitComboRemove(udg_TestMap)
  • Combo Add Again
    • Events
      • Player - Player 1 (Red) types a chat message containing -add as An exact match
    • Conditions
    • Actions
      • Custom script: call UnitComboAdd(udg_TestMap,1.,0.7)
  • Combo Change
    • Events
      • Player - Player 1 (Red) types a chat message containing -change as An exact match
    • Conditions
    • Actions
      • Custom script: call SetTextComboDecorateLeft(udg_TestMap,"|CffFFFF00=>|r")
      • Custom script: call SetTextComboDecorateRight(udg_TestMap,"|CffFFFF00<=|r")
      • Custom script: call SetTextComboText(udg_TestMap,"|Cff00CC99Combo: |r")
      • Custom script: call SetTextComboHeight(udg_TestMap,140.)
      • Custom script: call SetTextComboScale(udg_TestMap,0.9)
  • Remove Event
    • Events
      • Player - Player 1 (Red) types a chat message containing -removeevent as An exact match
    • Conditions
    • Actions
      • Custom script: call RemoveTriggerComboEvent(gg_trg_Combo_Any)
ScreenShot
untit278.jpg
Changelog
v1.0: First release version.
v1.1: Code optimized, add some function.
v1.2: Add some event.
v1.3: Add remove event, optimize function CheckEventIndex
v1.4: Use hashtable instead if indexing, minor thing changes
v1.5: Code optimized
v1.5.2: Code optimized
Keywords:
combo,system
Contents

Combo System (Map)

Reviews
12:48, 29th Aug 2014 PurgeandFire: (old) Long-awaited review: http://www.hiveworkshop.com/forums/2579635-post9.html It looks better. Approved.

Moderator

M

Moderator

12:48, 29th Aug 2014
PurgeandFire: (old) Long-awaited review:
http://www.hiveworkshop.com/forums/2579635-post9.html

It looks better. Approved.
 
Level 18
Joined
Sep 14, 2012
Messages
3,413
.031250000 -> .0312500 Because JASS can handle up to 8 digits (including the one before the .)

Use a unit indexer because linear searching can be a bit slow :/
(Or AVL and search ^^')

JASS:
private function CreateTextTagUnit takes unit whichUnit,real x,real y,string whichString returns texttag
        local texttag t = CreateTextTag()
        
        call MoveLocation(LOC,x,y)
        
        call SetTextTagPosUnit(t,whichUnit,GetUnitFlyHeight(whichUnit)+GetLocationZ(LOC))
        call SetTextTagText(t,whichString,TEXT_TAG_SIZE)
        call SetTextTagColor(t,255,255,255,255)
        call SetTextTagSuspended(t,true)
        call SetTextTagPermanent(t,false)
        
        return t
    endfunction
This leak a reference (except if they fix the leak with texttag I read something a while ago...)

Starts your index to -1 to use the 0 index.

That's all for the moment ;)
 
Level 10
Joined
Dec 15, 2012
Messages
650
The picture inside "ScreenShot" couldn't be seen :/

Your system seems cool :)
Gonna download it for testing.

EDIT :

1. The color of text tag will be changed when the combos are more than a number for extracting a set of COOL combos
2. More configurable settings. Exp :
--------The combo ends when the unit is being ordered to something else than attack
--------The combo won't stop after the unit was being ordered to do something else than attack (original).

It's a nice system :)
4/5
 
Last edited:
@Ah_Xian: Thanks for your suggestions.

The color of text tag will be changed when the combos are more than a number for extracting a set of COOL combos

Oh yes, it will more cooler, but I think I will let it depending on the users :).
Some methods will support for that:
call SetTextComboDecorateLeft() call SetTextComboDecorateRight() call SetTextComboText()

The combo ends when the unit is being ordered to something else than attack

I think attack is more accurate :)

The combo won't stop after the unit was being ordered to do something else than attack (original).

EDIT: If you want to do that, you can setting ComboDisableTimer very long :p...

@Mal: Ok my friend ^^~.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
IsUnitExist ---> DoesUnitExist, UnitExists, CheckUnit, .....

if GetUnitTypeId(ComboUnit[this]) > 0 then --> if GetUnitTypeId(ComboUnit[this]) != 0 then

static method textTag should be above static method damageDetection to avoid needless extra code generation. Furthermore it should be non static. You can refer to a struct instance like this: struct(index)/ thistype(index).

TriggerExecute should be TriggerEvaluate.

You could use Table + HandleId or UnitUserData in order to refer to the correct "Combo unit". It is much more neat than looping through every unit registered to the system.
Consider someone wants to register +100 units.
I also dislike the way you fire Events, like I said hashtable lookups can make ones life very easy.
 
Review

  • In the method "texttag", this:
    JASS:
    call SetTextTagVisibility(TextTag[this],false)
                    
    if GetLocalPlayer() == ComboPlayer[this] then
        call SetTextTagVisibility(TextTag[this],true)
    endif
    Can be compressed to:
    JASS:
    call SetTextTagVisibility(TextTag[this], GetLocalPlayer() == ComboPlayer[this])
  • debug call BJDebugMsg("This unit doesn't exists in the system.")
    Just a quick grammar fix, change "exists" to "exist".
  • call SetTextTagColor(TextTag[Index],255,255,255,255)
    I might be wrong, but I think the texttags are set to (255, 255, 255, 255) (r, g, b, a) by default. I don't think
    you need to add that line when you create the texttag.
  • JASS:
    /* excerpt of UnitAddComboEx */
    call MoveLocation(LOC,GetUnitX(ComboUnit[Index]),GetUnitY(ComboUnit[Index]))
    call SetTextTagPosUnit(TextTag[Index],ComboUnit[Index],GetUnitFlyHeight(ComboUnit[Index])+GetLocationZ(LOC))
    Just a little tip: always prefer the non-arrayed version over the array. The array has extra overhead of
    looking up the value at that index. As such, you should replace ComboUnit[Index] in this function
    with "whichUnit". It doesn't make a big
    difference, but it is good to know.
  • These functions can be merged:
    JASS:
        function UnitComboAdd takes unit whichUnit,real Scale,real ComboDisableTimer returns nothing
            if not DoesUnitExist(whichUnit) then
                set Index = Index + 1
                call SaveInteger(ComboHashtable,0,GetHandleId(whichUnit),Index)
                set ComboUnit[Index] = whichUnit
                set ComboCount[Index] = 0
                set ScaleValue[Index] = Scale
                set ComboTime[Index] = ComboDisableTimer
                set StructIndex[Index] = Index
                set DecorateLeft[Index] = DECORATE_TEXT_LEFT
                set DecorateRight[Index] = DECORATE_TEXT_RIGHT
                set ComboText[Index] = COMBO_TEXT
                set HeightSetting[Index] = HEIGHT_DEFAULT
                set ComboPlayer[Index] = GetOwningPlayer(ComboUnit[Index])
                
                set TextTag[Index] = CreateTextTag()
                call MoveLocation(LOC,GetUnitX(ComboUnit[Index]),GetUnitY(ComboUnit[Index]))
                call SetTextTagPosUnit(TextTag[Index],ComboUnit[Index],GetUnitFlyHeight(ComboUnit[Index])+GetLocationZ(LOC))
                call SetTextTagText(TextTag[Index],null,TEXT_TAG_SIZE)
                call SetTextTagColor(TextTag[Index],255,255,255,255)
                call SetTextTagSuspended(TextTag[Index],true)
                call SetTextTagPermanent(TextTag[Index],false)
            else
                debug call BJDebugMsg("This unit already exists in the system.")
            endif
        endfunction
        
        function UnitComboAddEx takes unit whichUnit,real Scale,real ComboDisableTimer,real whichHeight,string decorateLeft,string decorateRight,string comboText returns nothing
        
            if not DoesUnitExist(whichUnit) then
                set Index = Index + 1
                call SaveInteger(ComboHashtable,0,GetHandleId(whichUnit),Index)
                set ComboUnit[Index] = whichUnit
                set ComboCount[Index] = 0
                set ScaleValue[Index] = Scale
                set ComboTime[Index] = ComboDisableTimer
                set StructIndex[Index] = Index
                set DecorateLeft[Index] = decorateLeft
                set DecorateRight[Index] = decorateRight
                set ComboText[Index] = comboText
                set HeightSetting[Index] = whichHeight
                set ComboPlayer[Index] = GetOwningPlayer(ComboUnit[Index])
                
                set TextTag[Index] = CreateTextTag()
                call MoveLocation(LOC,GetUnitX(ComboUnit[Index]),GetUnitY(ComboUnit[Index]))
                call SetTextTagPosUnit(TextTag[Index],ComboUnit[Index],GetUnitFlyHeight(ComboUnit[Index])+GetLocationZ(LOC))
                call SetTextTagText(TextTag[Index],null,TEXT_TAG_SIZE)
                call SetTextTagColor(TextTag[Index],255,255,255,255)
                call SetTextTagSuspended(TextTag[Index],true)
                call SetTextTagPermanent(TextTag[Index],false)
            else
                debug call BJDebugMsg("This unit already exists in the system.")
            endif
            
        endfunction
    Change it to this:
    JASS:
        function UnitComboAddEx takes unit whichUnit,real Scale,real ComboDisableTimer,real whichHeight,string decorateLeft,string decorateRight,string comboText returns nothing
        
            if not DoesUnitExist(whichUnit) then
                set Index = Index + 1
                call SaveInteger(ComboHashtable,0,GetHandleId(whichUnit),Index)
                set ComboUnit[Index] = whichUnit
                set ComboCount[Index] = 0
                set ScaleValue[Index] = Scale
                set ComboTime[Index] = ComboDisableTimer
                set StructIndex[Index] = Index
                set DecorateLeft[Index] = decorateLeft
                set DecorateRight[Index] = decorateRight
                set ComboText[Index] = comboText
                set HeightSetting[Index] = whichHeight
                set ComboPlayer[Index] = GetOwningPlayer(ComboUnit[Index])
                
                set TextTag[Index] = CreateTextTag()
                call MoveLocation(LOC,GetUnitX(ComboUnit[Index]),GetUnitY(ComboUnit[Index]))
                call SetTextTagPosUnit(TextTag[Index],ComboUnit[Index],GetUnitFlyHeight(ComboUnit[Index])+GetLocationZ(LOC))
                call SetTextTagText(TextTag[Index],null,TEXT_TAG_SIZE)
                call SetTextTagColor(TextTag[Index],255,255,255,255)
                call SetTextTagSuspended(TextTag[Index],true)
                call SetTextTagPermanent(TextTag[Index],false)
            else
                debug call BJDebugMsg("This unit already exists in the system.")
            endif
            
        endfunction
    
        function UnitComboAdd takes unit whichUnit, real Scale, real ComboDisableTimer returns nothing
            call UnitAddComboEx(whichUnit, Scale, ComboDisableTimer, HEIGHT_DEFAULT, /*
                */ DECORATE_TEXT_LEFT, DECORATE_TEXT_RIGHT, COMBO_TEXT)
        endfunction
    Try to avoid repeating code whenever possible. ;)
  • This:
    JASS:
        private function DoesUnitExist takes unit whichUnit returns boolean
            set DataIndex=LoadInteger(ComboHashtable,0,GetHandleId(whichUnit))
        
            if DataIndex == 0 then
                return false
            endif
            
            return true
        endfunction
    Can become:
    JASS:
    private function DoesUnitExist takes unit whichUnit returns boolean
        set DataIndex = LoadInteger(ComboHashtable, 0, GetHandleId(whichUnit))
        return DataIndex != 0
    endfunction
  • You have some repeated code in TriggerRegister/Any/ComboEvent. Yet, they have some differences
    (such as the hashtable index, and the error msg). How do you remedy this? Use a private helper function. It should end up like this:
    JASS:
        private function TriggerRegisterComboEventHelper takes unit whichUnit, integer whichCombo, trigger whichTrigger, integer hashIndex returns nothing
            if DoesUnitExist(whichUnit) then
                set EventCount = EventCount + 1
                call SaveInteger(ComboHashtable,hashIndex,GetHandleId(whichUnit),EventCount)
                set EventTrigger[EventCount] = whichTrigger
                set EventUnit[EventCount] = whichUnit
                set ComboCounter[EventCount] = whichCombo
            else
                debug call BJDebugMsg("This unit doesn't exists in the system.")
            endif
        endfunction
    
        function TriggerRegisterComboEvent takes unit whichUnit,integer whichCombo,trigger whichTrigger returns nothing
            if whichCombo > 0 then
                call TriggerRegisterComboEventHelper(whichUnit, whichCombo, whichTrigger, 1)
            else
                debug call BJDebugMsg("Combo = "+I2S(whichCombo)+" ?????, what kind of your combo 0.0..")
            endif
        endfunction
        
        function TriggerRegisterAnyComboEvent takes unit whichUnit,trigger whichTrigger returns nothing
            call TriggerRegisterComboEventHelper(whichUnit, -1, whichCombo, 2)
        endfunction
    It only saves 4 lines, but it is good to avoid repetition.
 
I did read your code.
It will make some things look longer as you have to pass the handle id to the functions.

If you don't understand I will update your code when I get home and explain it better then.

I did understand, but I need to know how to optimize it :). Can you show some of your coding about that? (That will helped me alot >_^)..Plz... ^^
 
Last edited:

Kazeon

Hosted Project: EC
Level 34
Joined
Oct 12, 2011
Messages
3,449
  • JASS:
            private static real cos= TEXT_CENTER * Cos(.0174533)
            private static real sin= TEXT_CENTER * Sin(.0174533)
    should be static constant. And what value is that .0174533 anyway?
  • JASS:
        private function eventConditions takes integer whichCombo returns nothing
        
            set udg_System_ComboUnit = EventUnit[DataIndex]
            set udg_System_ComboCount = whichCombo
        
            if ComboCounter[DataIndex] == whichCombo or ComboCounter[DataIndex] == -1 then
                call TriggerExecute(EventTrigger[DataIndex])
            endif
        endfunction
        
        
        private function CheckEventCombo takes integer key,unit whichUnit,integer whichCombo returns nothing
            set DataIndex=LoadInteger(ComboHashtable,key,GetHandleId(whichUnit))
            call eventConditions(whichCombo)
        endfunction
    you could inline those two functions
  • Though, I still don't understand why do you ever need two udg_ variables whereas you have GetUnitComboCount(whichUnit)? If it's just for demonstration, then you should not include the variable creator there.
  • JASS:
            if DoesUnitExist(whichUnit) then
            
                    set hid=GetHandleId(whichUnit)
            
                    set DataIndex=LoadInteger(ComboHashtable,1,hid)
                    
                    if EventTrigger[DataIndex] != whichTrigger then
                        set DataIndex=LoadInteger(ComboHashtable,2,hid)
                        
                        if EventTrigger[DataIndex]!=whichTrigger then
                            debug call BJDebugMsg("This trigger doesn't exist in the system.")
                            
                            return
                        endif
                    endif
    ...
    Weird indentation :p
  • JASS:
            if DoesUnitExist(whichUnit) then
            
                    set hid=GetHandleId(whichUnit)
            
                    set DataIndex=LoadInteger(ComboHashtable,1,hid)
    You set DataIndex twice there
  • This is just my personal opinion:
    JASS:
        private function DoesUnitExist takes unit whichUnit returns boolean
            set DataIndex=LoadInteger(ComboHashtable,0,GetHandleId(whichUnit))
            return DataIndex != 0
        endfunction
    that function better returns the DataIndex and should be renamed to something like getDataIndex. Then just use DataIndex != 0 to check whether a unit exists or not:
    JASS:
        private function getDataIndex takes unit whichUnit returns boolean
            return LoadInteger(ComboHashtable,0,GetHandleId(whichUnit))
        endfunction
    call it:
    JASS:
            static method spellDetection takes nothing returns boolean
                set DataIndex = getDataIndex(GetTriggerUnit())
                if DataIndex != 0 then
                    set AttackDetector[DataIndex] = false
                endif
                
                return false
            endmethod
    Because, setting a variable at other function then use it, imo, is very not convincing.

Also I have a question: Are we allowed/able to determine actions for every combo? For example I want to deal 100 damage on the first combo, then I want to stun the target for the second, then I want to knock the target for the third, etc.
 
This made the multiple GetHandleId calling more efficient.

JASS:
//----------------------------------------------------------------------
//----------------------------------------------------------------------
//              C O M B O   S Y S T E M
//                  Made by: Nyuu
//                  Version: 1.5
//
//        What system does:
//              - This system will count the combo that a unit attack
//               enemies. Affected only when a unit attack without using spell.
//        
//        How to use:
//                   - To start counting combo from a unit, use this function:
//      function UnitComboAdd takes unit whichUnit,real Scale,real ComboDisableTimer returns nothing
//                   => whichUnit - Which unit you want to count
//                   => Scale - Scaling size of the unit (Make the height more accurate)
//                   => ComboDisableTimer - Timer of the combo, if the unit stop attacking the enemies,
//                      Combo will be destroyed and reset to 0.
//                   - To start counting combo from a unit, use this function (Ex):
//      function UnitComboAddEx takes unit whichUnit,real Scale,real ComboDisableTimer,real whichHeight,string decorateLeft,string decorateRight,string textCombo returns nothing
//                   => whichUnit - Which unit you want to count
//                   => Scale - Scaling size of the unit (Make the height more accurate)
//                   => ComboDisableTimer - Timer of the combo, if the unit stop attacking the enemies,
//                      Combo will be destroyed and reset to 0.
//                   => whichHeight: Your height value
//                   => decorateLeft: Your decorate left text
//                   => decorateRogjt: Your decorate right text
//                   => textCombo: Your text combo text
//                   - To stop counting combo from a unit, use this function:
//      function UnitComboRemove takes unit whichUnit returns nothing
//                   => whichUnit - Which unit you want to remove from this system
//                   - To get combo count of a unit, use this function:
//      function GetUnitComboCount takes unit whichUnit returns integer
//                   => whichUnit - Which unit you want to get combo count
//                   - To change scale value from a unit, use this function:
//      function SetTextComboScale takes unit whichUnit,real whichScale returns nothing
//                   => whichUnit - Which unit you want to change
//                   => whichScale - Your scale value
//                   - To change height value from a unit, use this function:
//      function SetTextComboheighttakes unit whichUnit,real whichHeight returns nothing
//                   => whichUnit - Which unit you want to change
//                   => whichHeight - Your height value
//                   - To change text decorate left from a unit, use this function:
//      function SetTextComboDecorateLeft takes unit whichUnit,string whichText returns nothing
//                   => whichUnit - Which unit you want to change
//                   => whichText - Your text
//                   - To change text decorate right from a unit, use this function:
//      function SetTextComboDecorateRight takes unit whichUnit,string whichText returns nothing
//                   => whichUnit - Which unit you want to change
//                   => whichText - Your text
//                   - To change text combo from a unit, use this function:
//      function SetTextComboText takes unit whichUnit,string whichText returns nothing
//                   => whichUnit - Which unit you want to change
//                   => whichText - Your text
//                   - To register unit combo event, use this function:
//      function TriggerRegisterUnitComboEvent takes unit whichUnit,integer whichCombo,trigger whichTrigger returns nothing
//                   => whichUnit - Which unit you want to register
//                   => whichCombo - Which combo you want to register
//                   => whichTrigger - Which trigger you want to register
//                   - To register unit any combo event, use this function:
//      function TriggerRegisterUnitAnyComboEvent takes unit whichUnit,trigger whichTrigger returns nothing
//                   => whichUnit - Which unit you want to register
//                   => whichTrigger - Which trigger you want to register
//                   - To remove any event combo event, use this function:
//      function RemoveTriggerComboEvent takes unit whihUnit, trigger whichTrigger returns nothing
//                   => whichTrigger - Which trigger you want to remove
//                   => whichUnit - Which unit you want to remove the triger event
//
//         How to import:
//                       - Copy Combo system code and play with the configuration below.
//                       - Go to Variables panel (Ctrl + B) copy variable System_ComboUnit, System_ComboCount and all the variables of GDD
//                       - Be sure "Automatically create unknown variables while pasting trigger data" is
//                         enabled in the World Editor general preferences.
//          
//          Credit:
//                  - Weep - Damage Detection System - [url]http://www.hiveworkshop.com/forums/spells-569/gui-friendly-damage-detection-v1-2-1-a-149098/?prev=search%3DDamage%2520Detection%26d%3Dlist%26r%3D20[/url]
//----------------------------------------------------------------------
//----------------------------------------------------------------------
library ComboSystem
    
    private module Init
        static method onInit takes nothing returns nothing
            call Init()
        endmethod
    endmodule
    
    globals
        //----------------------------------------------------------------
        //                     SYSTEM CONFIGURABLE
        //System period
        private constant real PERIODIC              = .0312500
        //Default size of the text tag
        private constant real TEXT_TAG_SIZE         = 10.
        //Limited size of the text tag when it increasing
        private constant real TEXT_TAG_SIZE_LIMIT   = 20.
        //Text tag size increase per period
        private constant real SIZE_INCREASE         = 2.
        //Text tag size decrease per period
        private constant real SIZE_DECREASE         = 3.
        //Height default of the text tag
        private constant real HEIGHT_DEFAULT        = 150.
        //
        private constant real TEXT_CENTER           = -65.
        //Text tag color decrease per period
        private constant integer TEXT_COLOR_DECREASE= 12
        
        private constant string COMBO_TEXT          = "Combo - "
        private constant string DECORATE_TEXT_LEFT  = "=>"
        private constant string DECORATE_TEXT_RIGHT = "<="
        //----------------------------------------------------------------
        
        //----------------------------------------------------------------
        //                       NON - CONFIGURABLE                     //
        /*-*/private integer Index                  = 0              /*-*/
        /*-*/private integer Counter                = 0              /*-*/
        /*-*/private integer EventCount             = 0              /*-*/
        /*-*/private integer DataIndex                               /*-*/
        
        /*-*/private integer array StructIndex                       /*-*/
        /*-*/private integer array ComboCount                        /*-*/
        /*-*/private integer array Color                             /*-*/
        /*-*/private integer array ComboCounter                      /*-*/
        
        /*-*/private real array SizeValue                            /*-*/
        /*-*/private real array ScaleValue                           /*-*/
        /*-*/private real array ComboTime                            /*-*/
        /*-*/private real array ComboTimeCounter                     /*-*/
        /*-*/private real array Height                               /*-*/
        /*-*/private real array HeightSetting                        /*-*/
        
        /*-*/private trigger array EventTrigger                      /*-*/
        
        /*-*/private player array ComboPlayer                        /*-*/
        
        /*-*/private unit array ComboUnit                            /*-*/
        /*-*/private unit array EventUnit                            /*-*/
        
        /*-*/private boolean array SizeBlock                         /*-*/
        /*-*/private boolean array AttackDetector                    /*-*/
        
        /*-*/private texttag array TextTag                           /*-*/
        
        /*-*/private string array Value                              /*-*/
        /*-*/private string array DecorateLeft                       /*-*/
        /*-*/private string array DecorateRight                      /*-*/
        /*-*/private string array ComboText                          /*-*/
        
        /*-*/private hashtable    ComboHashtable                     /*-*/
        
        /*-*/private constant timer TIMER = CreateTimer()            /*-*/
        
        /*-*/private constant location LOC = Location(0.,0.)         /*-*/
        //----------------------------------------------------------------
    endglobals
    
    private function eventConditions takes integer whichCombo returns nothing
    
        set udg_System_ComboUnit = EventUnit[DataIndex]
        set udg_System_ComboCount = whichCombo
    
        if ComboCounter[DataIndex] == whichCombo or ComboCounter[DataIndex] == -1 then
            call TriggerExecute(EventTrigger[DataIndex])
        endif
    endfunction
    
    
    private function CheckEventCombo takes integer key,integer whichCombo,integer id returns nothing
        set DataIndex=LoadInteger(ComboHashtable,key,id)
        call eventConditions(whichCombo)
    endfunction
    
    private function DoesUnitExist takes integer id returns boolean
        set DataIndex=LoadInteger(ComboHashtable,0,id)
        return DataIndex != 0
    endfunction
    
    private function removeKey takes boolean isAny,integer handleId1,integer handleId2 returns nothing
        local integer key
        
        if isAny then
            set key=1
        else
            set key=2
        endif
        
        call RemoveSavedInteger(ComboHashtable,key,handleId1)
        
        if DataIndex!=EventCount then
            call SaveInteger(ComboHashtable,key,handleId2,DataIndex)
        endif
    endfunction
    
    function RemoveTriggerComboEvent takes unit whichUnit, trigger whichTrigger returns nothing
        local integer id = GetHandleId(whichUnit)
        
        if DoesUnitExist(id) then
        
                set DataIndex=LoadInteger(ComboHashtable,1,id)
                
                if EventTrigger[DataIndex] != whichTrigger then
                    set DataIndex=LoadInteger(ComboHashtable,2,id)
                    
                    if EventTrigger[DataIndex]!=whichTrigger then
                        debug call BJDebugMsg("This trigger doesn't exist in the system.")
                        
                        return
                    endif
                endif
            
                set EventUnit[DataIndex] = EventUnit[EventCount]
                set EventUnit[EventCount] = null
                set EventTrigger[DataIndex] = EventTrigger[EventCount]
                set EventTrigger[EventCount] = null
                set ComboCounter[DataIndex] = ComboCounter[EventCount]
                
                call removeKey(ComboCounter[DataIndex]!=-1,id,GetHandleId(EventUnit[DataIndex]))
                
                set EventCount = EventCount - 1
            
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
    endfunction
    
    private function TriggerRegisterComboEventHelper takes unit whichUnit, integer whichCombo, trigger whichTrigger, integer hashIndex returns nothing
        local integer id = GetHandleId(whichUnit)
        if DoesUnitExist(id) then
            set EventCount = EventCount + 1
            call SaveInteger(ComboHashtable,hashIndex,id,EventCount)
            set EventTrigger[EventCount] = whichTrigger
            set EventUnit[EventCount] = whichUnit
            set ComboCounter[EventCount] = whichCombo
        else
            debug call BJDebugMsg("This unit doesn't exists in the system.")
        endif
    endfunction

    function TriggerRegisterComboEvent takes unit whichUnit,integer whichCombo,trigger whichTrigger returns nothing
        if whichCombo > 0 then
            call TriggerRegisterComboEventHelper(whichUnit, whichCombo, whichTrigger, 1)
        else
            debug call BJDebugMsg("Combo = "+I2S(whichCombo)+" ?????, what kind of your combo 0.0..")
        endif
    endfunction
    
    function TriggerRegisterAnyComboEvent takes unit whichUnit,trigger whichTrigger returns nothing
        call TriggerRegisterComboEventHelper(whichUnit,-1,whichTrigger, 2)
    endfunction
    
    function GetUnitComboCount takes unit whichUnit returns integer
        if DoesUnitExist(GetHandleId(whichUnit)) then
            return ComboCount[DataIndex]
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
            
        return 0
    endfunction
    
    function UnitAddComboEx takes unit whichUnit,real Scale,real ComboDisableTimer,real whichHeight,string decorateLeft,string decorateRight,string comboText returns nothing
        local integer id = GetHandleId(whichUnit)
        if not DoesUnitExist(id) then
            set Index = Index + 1
            call SaveInteger(ComboHashtable,0,id,Index)
            set ComboUnit[Index] = whichUnit
            set ComboCount[Index] = 0
            set ScaleValue[Index] = Scale
            set ComboTime[Index] = ComboDisableTimer
            set StructIndex[Index] = Index
            set DecorateLeft[Index] = decorateLeft
            set DecorateRight[Index] = decorateRight
            set ComboText[Index] = comboText
            set HeightSetting[Index] = whichHeight
            set ComboPlayer[Index] = GetOwningPlayer(ComboUnit[Index])
            
            set TextTag[Index] = CreateTextTag()
            call MoveLocation(LOC,GetUnitX(ComboUnit[Index]),GetUnitY(ComboUnit[Index]))
            call SetTextTagPosUnit(TextTag[Index],ComboUnit[Index],GetUnitFlyHeight(ComboUnit[Index])+GetLocationZ(LOC))
            call SetTextTagText(TextTag[Index],null,TEXT_TAG_SIZE)
            call SetTextTagColor(TextTag[Index],255,255,255,255)
            call SetTextTagSuspended(TextTag[Index],true)
            call SetTextTagPermanent(TextTag[Index],false)
        else
            debug call BJDebugMsg("This unit already exist in the system.")
        endif
        
    endfunction
    
    function UnitComboAdd takes unit whichUnit, real Scale, real ComboDisableTimer returns nothing
        call UnitAddComboEx(whichUnit, Scale, ComboDisableTimer, HEIGHT_DEFAULT, /*
            */ DECORATE_TEXT_LEFT, DECORATE_TEXT_RIGHT, COMBO_TEXT)
    endfunction
    
    function UnitComboRemove takes unit whichUnit returns nothing
        local integer id = GetHandleId(whichUnit)
        if DoesUnitExist(id) then
            set ComboUnit[DataIndex] = ComboUnit[Index]
            set ComboCount[DataIndex] = ComboCount[Index]
            set ScaleValue[DataIndex] = ScaleValue[Index]
            set ComboTime[DataIndex] = ComboTime[Index]
            set DecorateLeft[DataIndex] = DecorateLeft[Index]
            set DecorateRight[DataIndex] = DecorateRight[Index]
            set ComboText[DataIndex] = ComboText[Index]
            set HeightSetting[DataIndex] = HeightSetting[Index]
            set ScaleValue[DataIndex] = ScaleValue[Index]
            set ComboPlayer[DataIndex] = ComboPlayer[Index]
            call DestroyTextTag(TextTag[DataIndex])
            set TextTag[DataIndex] = TextTag[Index]
            set StructIndex[DataIndex] = StructIndex[Index]
            set StructIndex[Index] = 0
            set TextTag[Index] = null
            set ComboUnit[Index] = null
            
            call RemoveSavedInteger(ComboHashtable,0,id)
            
            if DataIndex!=Index then
                call SaveInteger(ComboHashtable,0,GetHandleId(ComboUnit[DataIndex]),DataIndex)
            endif
            
            set Index = Index - 1
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
        
    endfunction
    
    function SetTextComboHeight takes unit whichUnit,real whichHeight returns nothing
        if DoesUnitExist(GetHandleId(whichUnit)) then
            set HeightSetting[DataIndex] = whichHeight
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
    endfunction
    
    function SetTextComboText takes unit whichUnit,string whichText returns nothing
        if DoesUnitExist(GetHandleId(whichUnit)) then
            set ComboText[DataIndex] = whichText
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
    endfunction
    
    function SetTextComboDecorateLeft takes unit whichUnit,string whichText returns nothing
        if DoesUnitExist(GetHandleId(whichUnit)) then
            set DecorateLeft[DataIndex] = whichText
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
    endfunction
    
    function SetTextComboDecorateRight takes unit whichUnit,string whichText returns nothing
        if DoesUnitExist(GetHandleId(whichUnit)) then
            set DecorateRight[DataIndex] = whichText
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
    endfunction
    
    function SetTextComboScale takes unit whichUnit,real whichScale returns nothing
        if DoesUnitExist(GetHandleId(whichUnit)) then
            set ScaleValue[DataIndex] = whichScale
        else
            debug call BJDebugMsg("This unit doesn't exist in the system.")
        endif
    endfunction
    
    private struct ComboSystem extends array
    
        static method textTag takes string whichString,integer Indexx,real Scale returns nothing
                local thistype this = Indexx
                
                call SetTextTagText(TextTag[this],whichString,TEXT_TAG_SIZE)
                
                call SetTextTagVisibility(TextTag[this], GetLocalPlayer() == ComboPlayer[this])
                
                set ComboTimeCounter[this] = ComboTime[this]
                set SizeValue[this] = TEXT_TAG_SIZE
                set SizeBlock[this] = false
                set Value[this] = whichString
                set Color[this] = 255
                set Height[this] = HeightSetting[this]*ScaleValue[this]
                call SetTextTagColor(TextTag[this],255,255,255,255)
                
                set Counter = Counter + 1
                if Counter == 1 then
                    call TimerStart(TIMER,PERIODIC,true,function thistype.onPeriodic)
                endif
                
            endmethod
        
        static method damageDetection takes nothing returns boolean
            
            if IsUnitEnemy(udg_GDD_DamageSource,GetOwningPlayer(udg_GDD_DamagedUnit)) then
                if DoesUnitExist(GetHandleId(udg_GDD_DamageSource)) then
                    if AttackDetector[DataIndex] then
                        set ComboCount[DataIndex] = ComboCount[DataIndex] + 1
                        call textTag(DecorateLeft[DataIndex]+ComboText[DataIndex]+I2S(ComboCount[DataIndex])+DecorateRight[DataIndex],DataIndex,ScaleValue[DataIndex])
                        call CheckEventCombo(1,ComboCount[DataIndex],GetHandleId(ComboUnit[DataIndex]))
                        call CheckEventCombo(2,ComboCount[DataIndex],GetHandleId(ComboUnit[DataIndex]))
                    endif
                endif
            endif
            
            return false
        endmethod
        
        private static real cos= TEXT_CENTER * Cos(.0174533)
        private static real sin= TEXT_CENTER * Sin(.0174533)
        
       static method onPeriodic takes nothing returns nothing
            local integer i = 1
            local thistype this
            local real x
            local real y
            
            loop
                exitwhen i > Index
                set this = StructIndex[i]
                
                if GetUnitTypeId(ComboUnit[this]) != 0 then
                    if not SizeBlock[this] then
                        set SizeValue[this] = SizeValue[this] + SIZE_INCREASE
                        set SizeBlock[this] = SizeValue[this] >= TEXT_TAG_SIZE_LIMIT
                    else
                        if SizeValue[this] > TEXT_TAG_SIZE then
                            set SizeValue[this] = SizeValue[this] - SIZE_DECREASE
                        endif
                    endif
                    
                    set x = GetUnitX(ComboUnit[this])+cos
                    set y = GetUnitY(ComboUnit[this])+sin
                    
                    call MoveLocation(LOC,x,y)
                
                    call SetTextTagColor(TextTag[this],255,255,255,Color[this])
                    call SetTextTagText(TextTag[this],Value[this],SizeValue[this]*0.0023)
                    
                    call SetTextTagPos(TextTag[this],x,y,GetUnitFlyHeight(ComboUnit[this])+GetLocationZ(LOC)+Height[this])
                    
                    if ComboTimeCounter[this] > 0. and not IsUnitType(ComboUnit[this],UNIT_TYPE_DEAD) then
                        set ComboTimeCounter[this] = ComboTimeCounter[this] - PERIODIC
                    else
                        if Color[this] > 0 then
                            set Color[this] = Color[this] - TEXT_COLOR_DECREASE
                            set SizeValue[this] = SizeValue[this] + SIZE_INCREASE
                        else
                            set ComboCount[this] = 0
                            call SetTextTagVisibility(TextTag[this],false)
                            
                            set Counter = Counter - 1
                            
                            if Counter == 0 then
                                call PauseTimer(TIMER)
                            endif
                        endif
                    endif
                else
                    call UnitComboRemove(ComboUnit[this])
                    
                    set Counter = Counter - 1
                            
                    if Counter == 0 then
                        call PauseTimer(TIMER)
                    endif
                endif
                
                set i = i + 1
            endloop
        endmethod
        
        static method attackDetection takes nothing returns boolean
            if DoesUnitExist(GetHandleId(GetAttacker())) then
                set AttackDetector[DataIndex] = true
            endif
            
            return false
        endmethod
        
        static method spellDetection takes nothing returns boolean
            if DoesUnitExist(GetHandleId(GetTriggerUnit())) then
                set AttackDetector[DataIndex] = false
            endif
            
            return false
        endmethod
        
        static method Init takes nothing returns nothing
            local trigger damageTrigger = CreateTrigger()
            local trigger attackTrigger = CreateTrigger()
            local trigger spellTrigger = CreateTrigger()
            local integer i = 0
            
            set ComboHashtable=InitHashtable()
            
            loop
                exitwhen i > 15
                
                call TriggerRegisterPlayerUnitEvent(attackTrigger,Player(i),EVENT_PLAYER_UNIT_ATTACKED,null)
                call TriggerRegisterPlayerUnitEvent(spellTrigger,Player(i),EVENT_PLAYER_UNIT_SPELL_CAST,null)
                
                set i = i + 1
            endloop
            
            call TriggerRegisterVariableEvent(damageTrigger, "udg_GDD_Event", EQUAL, 0 )
            
            call TriggerAddCondition(damageTrigger,function thistype.damageDetection)
            call TriggerAddCondition(attackTrigger,function thistype.attackDetection)
            call TriggerAddCondition(spellTrigger,function thistype.spellDetection)
            
            set damageTrigger = null
            set attackTrigger = null
            set spellTrigger = null
            
        endmethod
        
        implement Init
    endstruct
    
endlibrary
 
should be static constant. And what value is that .0174533 anyway?

PI/180 :D

you could inline those two functions

Done!.

Though, I still don't understand why do you ever need two udg_ variables whereas you have GetUnitComboCount(whichUnit) ? If it's just for demonstration, then you should not include the variable creator there.

For more convenient :). For GUI user, they are very lazy to use Custom Script to get combocount, so Set System_ComboCount = 0 is born to serve.

You set DataIndex twice there
that function better returns the DataIndex and should be renamed to something like getDataIndex. Then just use DataIndex != 0 to check whether a unit exists or not:


Hmm, maybe not, set DataIndex is really cool here :p.

Weird indentation :p

Yup!, so weird :D. Fixed!

Because, setting a variable at other function then use it, imo, is very not convincing.

Don't worry. Today's computer run super-fast >_^, I think it's fine.

Also I have a question: Are we allowed/able to determine actions for every combo? For example I want to deal 100 damage on the first combo, then I want to stun the target for the second, then I want to knock the target for the third, etc.

Affected only when a unit attack without using spell.

As your example: First combo depend on unit attacking (The answer is Yes) or casting spell (The answer is No)

Second and third depend on unit using dummy (The answer is Yes) or self-casting. (The answer is No).

@deathismyfriend: Cool, you have done it for me?, thanks ;).
 
Top