1. The Aftermath has been revealed for the 19th Terraining Contest! Be sure to check out the Results and see what came out of it.
    Dismiss Notice
  2. Melee Mapping Contest #3 - Results are out! Congratulate the winners and check plenty of new 4v4 melee maps designed for this competition!
    Dismiss Notice
  3. The winners of our cinematic soundtrack competition have been decided! Step by the Music Contest #11 - Results to check the entries and congratulate the winners!
    Dismiss Notice

[System] Combat State

Discussion in 'JASS Resources' started by PurgeandFire, Jan 6, 2011.

  1. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Combat State

    A quick, awesome library I made to register when a unit "enters combat" or "leaves combat". The idea has been around for a while, but I haven't seen any libraries/systems for it except for this one:
    http://www.thehelper.net/forums/showthread.php/91529-In-Combat-Status

    Which uses CSData and PUI. So I decided to make my own, and modeled it after that library a bit, and added some other things I thought would be good.

    Requirements:

    Here is the code:
    Code (vJASS):
    library CombatState requires UnitIndexer, Event, optional TimerUtils
        //******** COMBAT STATE ******************************************//
        //  - This library registers whether or not some unit is in combat.
        //  - To be registered as in combat, the unit must attack or have been attacked by an enemy unit.
        //  - Any spell that is cast by an opposing player onto the unit will flag them as in combat.
        //  - Being in combat only lasts a specific duration, in this case 5 seconds, before the unit leaves combat.
        //  - Once a unit dies, the dying unit will be taken out of combat.
       
        //  Requirements:
        //    -- UnitIndexer by Nestharus
        //    -- Event by Nestharus
        //       - This will allow you to register when a unit enters or leaves combat.
        //    -- *OPTIONAL* TimerUtils by Vexorian
        //       - This will recycle timers instead of creating/destroying them, so it is a bit more optimal. As far as speed goes,
        //         it is pretty negligible. Without TimerUtils, it will use a hashtable.
       
        //  API:
        //    Configurables:
        //       - COMBAT_DURATION: This determines the time before a unit is considered to be out of combat, default 5 seconds.
        //         The unit must remain unattacked and attack no one for COMBAT_DURATION to leave combat.
       
        //    Data Modify/Retrieve:
        //        CombatState[whichUnit].inCombat -> returns boolean
        //        set CombatState[whichUnit].inCombat = flag
       
        //    Function Wrappers:
        //        function GetUnitCombatState takes unit whichUnit returns boolean
        //           - This returns the combat state of a unit. If it returns true, the unit is in combat. If it returns false, then the unit
        //             is not in combat.
        //        function SetUnitCombatState takes unit whichUnit, boolean flag returns nothing
        //           - This allows you to force a unit in or out of combat, and it will register the corresponding events.
       
        //    If you are using Event:
        //        call CombatState.EnterCombat.register(trigger)
        //           - Registers when some unit enters combat after being out of combat.
        //        call CombatState.LeaveCombat.register(trigger)
        //           - Registers when some unit leaves combat after having been just in combat.
        //        function GetTriggerCombatUnit takes nothing returns unit
        //           - When using registering an event, this will basically return the unit who entered or left combat. GetTriggerCombatUnit()
       
        //    Credits:
        //       - Nestharus for UnitIndexer, Event, and optimizations
        //       - Darthfett for the original combat library
        //       - Vexorian for TimerUtils
        //****************************************************************//

        globals
            private constant real COMBAT_DURATION = 5
        //**************DO NOT EDIT PAST THIS POINT***********************//
            private unit combatUnit = null
            private hashtable Hash
        endglobals
       
        function GetTriggerCombatUnit takes nothing returns unit
            return combatUnit
        endfunction
       
        private module Init
            private static method onInit takes nothing returns nothing
                local trigger t = CreateTrigger()
                static if not LIBRARY_TimerUtils then
                    set Hash = InitHashtable()
                endif
                set thistype.ENTER = Event.create()
                set thistype.LEAVE = Event.create()
                call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_ATTACKED)
                call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
                call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_DEATH)
                call UnitIndexer.DEINDEX.register(Condition(function thistype.deindex))
                call TriggerAddCondition(t,Condition(function thistype.CombatEnter))
            endmethod
        endmodule
       
        struct CombatState extends array
            private timer combatTimer
            private boolean inCombatV
           
            readonly static Event LEAVE = 0
            readonly static Event ENTER = 0
           
            static method operator [] takes unit u returns thistype
                return GetUnitUserData(u)
            endmethod
           
            private static method CombatLeave takes nothing returns nothing
                local timer t   = GetExpiredTimer()
                local unit prev = combatUnit
                static if LIBRARY_TimerUtils then
                    local integer id  = GetTimerData(t)
                    call ReleaseTimer(t)
                else
                    local integer id  = LoadInteger(Hash,GetHandleId(t),0)
                    call PauseTimer(t)
                    call DestroyTimer(t)
                    set t             = null
                endif
                set combatUnit               = GetUnitById(id)
                set thistype(id).inCombatV   = false
                set thistype(id).combatTimer = null
                call thistype.LEAVE.fire()
                set combatUnit               = prev
                set prev                     = null
            endmethod
           
            method operator inCombat takes nothing returns boolean
                return this.inCombatV
            endmethod
           
            method operator inCombat= takes boolean flag returns nothing
                local unit prev    = combatUnit
                set combatUnit     = GetUnitById(this)
                if flag then
                    if this.combatTimer == null then
                        set this.inCombatV = true
                        call thistype.ENTER.fire()
                        static if LIBRARY_TimerUtils then
                            set this.combatTimer = NewTimer()
                            call SetTimerData(this.combatTimer,this)
                        else
                            set this.combatTimer = CreateTimer()
                            call SaveInteger(Hash,GetHandleId(this.combatTimer),0,this)
                        endif
                    endif
                    call TimerStart(this.combatTimer,COMBAT_DURATION,false,function thistype.CombatLeave)
                elseif (this.inCombatV) then
                    set this.inCombatV = false
                    static if LIBRARY_TimerUtils then
                        call ReleaseTimer(this.combatTimer)
                    else
                        call PauseTimer(this.combatTimer)
                        call DestroyTimer(this.combatTimer)
                    endif
                    set this.combatTimer = null
                    call thistype.LEAVE.fire()
                endif
                set combatUnit    = prev
                set prev          = null
            endmethod
           
            private static method CombatEnter takes nothing returns boolean
                local unit u = GetAttacker()
                if GetTriggerEventId()==EVENT_PLAYER_UNIT_DEATH then
                    set thistype[GetTriggerUnit()].inCombat=false
                    return false
                elseif u == null then
                    set u = GetSpellTargetUnit()
                endif
                if u != null then
                    if IsUnitEnemy(u,GetTriggerPlayer()) then
                        set thistype[GetTriggerUnit()].inCombat=true
                        set thistype[u].inCombat=true
                    elseif CombatState[u].inCombat then
                        set thistype[GetTriggerUnit()].inCombat=true
                    endif
                endif
                set u = null
                return false
            endmethod
           
            private static method deindex takes nothing returns boolean
                set thistype(GetIndexedUnitId()).inCombat=false
                return false
            endmethod
           
            implement Init
        endstruct
       
        function GetUnitCombatState takes unit whichUnit returns boolean
            return CombatState[whichUnit].inCombat
        endfunction
        function SetUnitCombatState takes unit whichUnit, boolean flag returns nothing
            set CombatState[whichUnit].inCombat = flag
        endfunction
    endlibrary


    You don't have to give credits if you use it, but credits would be nice.

    JASS Usage:
    The library is nice in that it works for all units, so you don't have to manually add which units you want to track combat status of.

    The key function is this:
    Code (vJASS):
        function GetUnitCombatState takes unit whichUnit returns boolean
            return CombatState[whichUnit].inCombat
        endfunction

    Or you can retrieve it directly using this:
    CombatState[whichUnit].inCombat


    If it returns true, then the unit is in combat. If it returns false, then the unit is out of combat.

    Here is an example of the usage:
    Example
    Code (vJASS):
    function Example takes nothing returns boolean
        local unit u = GetTriggerUnit()
        if GetUnitCombatState(u) then
            call DisplayTextToPlayer(Player(0),0,0,"Unit is in combat!")
            call SetUnitExploded(u,true)
            call KillUnit(u)
        else
            call DisplayTextToPlayer(Player(0),0,0,"Unit is out of combat!")
            call SetUnitState(u,UNIT_STATE_LIFE,GetWidgetLife(u)+500)
        endif
        set u = null
        return false
    endfunction

    function InitTrig_TestSpell takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t,Condition(function Example))
    endfunction


    In that example, the function registers whenever a spell is cast. If the caster is in combat, he'll be exploded and it will say "Unit is in combat!", but if he is out of combat, it will heal him for 500 hit points and say "Unit is out of combat!".

    The only configurable is this:
    Code (vJASS):
            private constant real COMBAT_DURATION = 5


    Modifying this will control how long it takes to leave combat after being in combat. However, if you enter combat it doesn't necessarily mean that you will leave combat in 5 seconds. Each time you attack or get attacked, the timer will be refreshed to 5 seconds, so you must not attack or be attacked to successfully leave combat.

    You can also force a unit into or out of combat by doing this:
    Code (vJASS):
    set CombatState[whichUnit].inCombat = true //enter combat
    set CombatState[whichUnit].inCombat = false //leave combat

    Or:
    Code (vJASS):
    call SetUnitCombatState(whichUnit,flag)


    If you use the Event Library:

    If you use the Event library, then you can register when a unit enters or leaves combat. It is pretty neat. Example:
    Example
    Code (vJASS):
    function Example takes nothing returns boolean
        if GetUnitTypeId(GetTriggerCombatUnit())=='hfoo' then
            call SetUnitExploded(GetTriggerCombatUnit(),true)
            call KillUnit(GetTriggerCombatUnit())
        endif
        return false
    endfunction

    function InitTrig_TestEnterCombat takes nothing returns nothing
        local trigger t = CreateTrigger()
        call CombatState.ENTER.registerTrigger(t)
        call TriggerAddCondition(t,Condition(function Example))
    endfunction


    If you enter combat as a footman, it will explode you. Simple as that. :p
    To register entering and leaving, you simply use this:
    Code (vJASS):

    call CombatState.ENTER.registerTrigger(t)
    call CombatState.LEAVE.registerTrigger(t)
     


    GUI Usage:

    Yep, this is pretty easy to use in GUI as well.
    Example
    • Explode
      • Events
        • Unit - A unit Starts the effect of an ability
      • Conditions
        • (Ability being cast) Equal to Acid Bomb
      • Actions
        • Custom script: set udg_InCombat = GetUnitCombatState(GetTriggerUnit())
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • InCombat Equal to True
          • Then - Actions
            • Set TempPoint = (Position of (Triggering unit))
            • Special Effect - Create a special effect at TempPoint using Abilities\Spells\Human\ThunderClap\ThunderClapCaster.mdl
            • Special Effect - Destroy (Last created special effect)
            • Custom script: call RemoveLocation(udg_TempPoint)
          • Else - Actions
            • Unit - Explode (Triggering unit)


    In that example, if the unit casts Acid Bomb and is in combat, then it will create an effect at the position of the caster. If they cast Acid Bomb and are out of combat, it will, of course, explode them. lol. For this part:
    • Custom script: set udg_InCombat = GetUnitCombatState(GetTriggerUnit())

    It sets a boolean variable named InCombat to whatever the combat state is of (Triggering Unit).

    If you want to register when a unit enters combat, you'll have to do something along these lines.
    Example
    • AddCombatEvent
      • Events
        • Map initialization
      • Conditions
      • Actions
        • Set CombatTrigger = RegisterEnterCombat <gen>
        • Custom script: call CombatState.ENTER.register(udg_CombatTrigger)
        • Set CombatTrigger = RegisterLeaveCombat <gen>
        • Custom script: call CombatState.LEAVE.register(udg_CombatTrigger)

    • RegisterEnterCombat
      • Events
      • Conditions
      • Actions
        • Custom script: set udg_CombatUnit = GetTriggerCombatUnit()
        • Unit - Set life of CombatUnit to 100.00%

    • RegisterLeaveCombat
      • Events
      • Conditions
      • Actions
        • Custom script: set udg_CombatUnit = GetTriggerCombatUnit()
        • Unit - Explode CombatUnit


    The first trigger adds the events to the other triggers. Basically modify "CombatTrigger" to whatever you want and then just put the custom script afterward. :) For the trigger itself, make a variable named "CombatUnit" and then use the custom script at the top of the trigger. This will basically set "CombatUnit" to the unit that is entering combat. In this example, it will set the life of the combat unit to 100.00% once he enters combat, and it will explode him once he leaves combat.

    Demo Map:

    Attached below is a demo map of the combat system. Basically, it has some nifty spells that only work if you are in combat, out of combat, or change based on whether or not you are in combat. Press ESCAPE to swap between being in combat or out of combat.
     

    Attached Files:

    Last edited: Jan 2, 2012
  2. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Why AIDS when it is slower and less stable than UnitIndexer? >.<

    Also, you never have to unregister events, so why use Event lib from TH? : |

    Doesn't make sense ; D.

    Also this lib will break with struct onInits

    library CombatState initializer Init


    Just like AIDS, what a charm.

    problem with these lines too
    Code (vJASS):

            timer combatTimer
            boolean inCombat
     


    combatTimer should have a private keyword on it to make it internal

    inCombat should be private with a method operator for reading. Either that or put all of your stuff into the struct and onInit into a module implemented by the struct and then make inCombat readonly, and combatTimer private.

    These are unneeded as Event API can just as easily be used
    Code (vJASS):

            function TriggerRegisterEnterCombat takes trigger t returns nothing
                call Event(EnterCombat).register(t)
            endfunction
           
            function TriggerRegisterLeaveCombat takes trigger t returns nothing
                call Event(LeaveCombat).register(t)
            endfunction
     


    Just put the events into the struct and make them readonly. They should be initialized within a module initializer anyways.


    If you were using the Event at THW, you wouldn't need to make Event optional as it's so dern light that it doesn't make much of a dif.


    This is unstable, use a hashtable instead.
    Code (vJASS):

               local integer id = CombatId[GetHandleId(t)-OFFSET]
                static if LIBRARY_Event then
                    set combatUnit[0] = combatUnit[GetHandleId(t)-OFFSET]
                endif
     


    These should be of type Event and in a struct as readonly fields so you can reduce overall functions.
    Code (vJASS):

            private integer LeaveCombat
            private integer EnterCombat
     


    Also, while your events aren't a problem with leaving combat, they are a problem with entering combat as they aren't recursive.


    Those are all the problems I care to look for for right now ; P.
     
    Last edited: Jan 7, 2011
  3. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Idk. I just like the interface and I was going to submit this to TH eventually. For the latter, it just uses Event because I started off with it before the second event was introduced. Tbh, if you change .register(t) in my system to .registerTrigger(t) it should work for your Event lib too, unless I'm mistaken.

    They can use library initializers for theirs to fix it.. But okay, I'll look into fixing that.

    Will do.

    I guess so, I like making the interface blizzlike but I guess that is better.


    Yeah.

    Yeah, but the thing is that when I put static ifs in my globals, they produce a syntax error saying "expected a name." I don't remember them doing that before, so I had to make them integers. My JassHelper is updated.

    Besides, no difference? It is just the look of the code, I don't think I need to change that.

    I don't understand what you mean.
     
    Last edited: Jan 7, 2011
  4. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    ->I don't understand what you mean.
    If you were to do DamageUnit within your EnterCombat trigger, the combatUnit would be incorrect.

    Code (Text):

    -MyCode-
    local unit u = GetEventUnit or w/e
    DamageUnit
    if (u != DamageUnit) then
        --error //this will run
     
    Here is an example of proper recursion
    Code (vJASS):

    integer eventInt = 0

    ////////////////////////////////////
    local integer prev = eventInt
    set eventInt = new
    call EVENT.fire()
    set eventInt = prev
     


    It won't do that if they are in a struct. Static ifs don't work on globals.

    ->They can use library initializers for theirs to fix it.. But okay, I'll look into fixing that.
    Impossible to fix when using AIDS as AIDS has a problem with initialization too : |,
     
  5. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Okay, ty, I'll look to fixing them. :)
     
  6. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    On the note of initialization (since your thing will be messed up as long as it uses AIDS as I have a feeling AIDS is never going to get fixed)

    Perhaps you could bring up the initialization problems on the AIDS thread, or just move to UnitIndexer, lol. UnitIndexer has been scanned over by dozens of people and has been rewritten 4x as new techniques and systems come out, lol.

    Otherwise I look forward to seeing all the fixes ;D.

    I wonder where azlier is...

    Yoohoo, azlier, where are uuuuuuuuuuuuuu

    *azlier's says in a faint voice from the background, "I'm right heere" *flickers and fades away like a ghost*
     
  7. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Apparently, AIDS had an onInit method for that reason. lol, I just saw it a while ago.

    Yeah, I'll probably add a UnitIndexer version eventually. Just give me some time to make sure my libs work and some time to study/live life. :p Maybe I'll submit an AIDS version on TH and a UnitIndexer one here.

    I think I did everything you said. I might of forgotten something aside from switching to the other Event. (I guess mine "technically" supports yours now, it just requires you to register it differently, so maybe I'll just add some documentation on that later) But overall, it looks better now. :)
     
    Last edited: Jan 7, 2011
  8. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    AIDS only has a library initializer ;o. For stability, units aren't given ids until library initialization and previously units are enumerated over. But again, you run into the but I mentioned where GetUnitUserData(CreateUnit(...)) in a struct onInit or module onInit would return 0.

    I'll check over your code, just in the middle of something atm ;D.

    Also you are missing the Event requirement in your header if you didn't notice.

    Like i said, I doubt jesus4lyf will ever change the way AIDS initializes, preferring to say to not use onInit in modules or structs. That's just a bleh excuse. Instability is instability, y'know? : P

    Me 'n Bribe worked at proper initialization for unit indexing together in the UnitIndexer thread ^_^.


    edits
    If you're using the Event here, Event might as well not be optional because of how lightweight it is : |. I'm sure most scripts using this would run events anyways.

    Code (vJASS):

            static if LIBRARY_Event then
                static Event LeaveCombat
                static Event EnterCombat
            endif
     


    Yea ; P. These should also be initialized to zero in case they are used by a script. It's always good practice to initialize all of your public static variables so that they don't crash scripts that try to access them. Furthermore, these should be readonly. Also, since these are readonly (pretty much like constants), you should make them all uppercase (following normal JASS events). Also, JASS naming convention for events is EVENT_EVENTNAME. Since your events are within the UnitCombat struct, the Combat portion of the name is extraneous as that is already obvious. It could either be LEAVE and ENTER, or EVENT_LEAVE and EVENT_ENTER. Those are your best options.

    So

    Code (vJASS):

    readonly static Event LEAVE = 0
    readonly static Event ENTER = 0
     


    or
    Code (vJASS):

    readonly static Event EVENT_LEAVE = 0
    readonly static Event EVENT_ENTER = 0
     


    This is bad
    struct CombatState


    And you already know why : P. Not only do vjass structs have tons of crabby overhead, but you don't even need the allocation/deallocation in there for any reason since your thing's instantiation depends on unit indexing.

    Is there a reason these are public?
    Code (vJASS):

    static method SetUnitEnterCombat takes unit whichUnit returns nothing
    static method CombatEnter takes nothing returns boolean
     


    Your LeaveEvent stuff doesn't need recursion on the units as it's impossible to force a unit to leave combat outside of the timers, right? : P

    Well, I guess it would be nice to be able to set a unit's combat state (like if they enter town or something, force them out of combat). In that case, make inCombat a method operator rather than a readonly field where the getter just returns the field and the setter fires off your events and what not. If you were to add that feature, leaveCombat would need to be recursive. If not, leaveCombat doesn't need the recursion, that's just extra overhead that serves no purpose.
     
    Last edited: Jan 7, 2011
  9. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Whoops, lol.


    Yeah, I'll probably update it to be required.


    Okay.

    Whoops, I forgot that too. ;P

    No reason, I guess I forgot to privatize it. I used to refer to it outside the struct, until I moved everything in, that's why I forgot. :p


    Yeah, I will probably add some sort of forceOut or forceIn thing to allow you to make a unit in combat. For example, abilities like Vanish will force you out of combat, which is partially one of the reasons I made this library in the first place. :p

    Anyway, thanks for the review. :D I'll update it when I can and then hopefully it will be good to go.
     
  10. Maker

    Maker

    Joined:
    Mar 6, 2006
    Messages:
    9,193
    Resources:
    17
    Maps:
    2
    Spells:
    14
    Tutorials:
    1
    Resources:
    17
    There could be an option to check whether there are enemy units in close range when trying to exit combat. When the timer expires, do the check and refresh timer if true.
     
  11. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Yeah, it is a good idea and I might add that eventually, but for now, I'll keep it a bit WoWlike. ( at least from a pvp standpoint :p )

    Anyway, I updated it. Hopefully it should be good now. I updated it so it uses UnitIndexer, and it uses the Event and works like a charm.

    As for forcing a unit in or out of combat, I added that, BUT it is not a method operator because I need to get the unit and the boolean, and tbh it is a lot easier to have the user input than keep track of it just so it could become an operator. I can always have one for force in and one for forcing out, but oh well, whatever.
     
  12. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Since you have forcing, this has to be recursive ;|
    Code (vJASS):

            private static method CombatLeave takes nothing returns nothing
                local timer t = GetExpiredTimer()
                static if LIBRARY_TimerUtils then
                    local integer id = GetTimerData(t)
                    set combatUnit[0] = combatUnit[id]
                    call ReleaseTimer(t)
                else
                    local integer id = LoadInteger(Hash,GetHandleId(t),0)
                    set combatUnit[0] = LoadUnitHandle(Hash,GetHandleId(t),1)
                    call PauseTimer(t)
                    call DestroyTimer(t)
                    set t = null
                endif
                set thistype(id).inCombat = false
                set thistype(id).combatTimer = null
                call thistype.LEAVE.fire()
            endmethod
     


    Make this
    private static method SetUnitEnterCombat takes unit whichUnit returns nothing
    into
    method operator inCombat= takes boolean b returns nothing
    where the instance is the unit user data.

    This way this
    Code (vJASS):

                    call thistype.SetUnitEnterCombat(GetTriggerUnit())
                    call thistype.SetUnitEnterCombat(u)    
     

    can change to
    Code (vJASS):

    set thistype(GetUnitUserData(GetTriggerUnit()).inCombat = true
    set thistype(GetUnitUserData(u)).inComat = true
     


    Change this
    readonly boolean inCombat


    to
    Code (vJASS):

    private boolean inCombat_p
    method operator inCombat takes nothing returns boolean
        return inCombat_p
    endmethod
     


    This
    set thistype(id).inCombat = false
    to
    set thistype(id).inCombat_p = false


    And this
    set thistype(id).inCombat = true
    to
    set thistype(id).inCombat_p = true


    And boom, easier API :\.

    Aslo add
    Code (vJASS):

    static method operator [] takes unit u returns integer
        return GetUnitUserData(u)
    endmethod
     


    That way people can do like
    set CombatState[u].inCombat = true


    Then
    Code (vJASS):

    function GetUnitCombatState takes unit whichUnit returns boolean
    function SetUnitCombatState takes unit whichUnit, boolean inCombat returns nothing
     


    Which would just be function wrappers.

    Otherwise it's looking better ;D. The chaotic API and documentation made me dizzy though ;P.
     
  13. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Forgot about that one, done.

    Awesome, done. I completely overlooked indexing systems' other functions, doh. It looks a lot better now.

    UnitIndexer already added that to the struct, so it should work. :)

    ---

    Hopefully this will be the final version. It is tiring to update. ;o

    The primary API now looks like:
    Code (vJASS):

    set CombatState[whichUnit].inCombat = flag
    call CombatState.ENTER.registerTrigger(whichTrigger)
    call CombatState.LEAVE.registerTrigger(whichTrigger)
    call SetUnitCombatState(whichUnit,flag)
    call GetUnitCombatState(whichUnit)
    call GetTriggerCombatUnit()
     
     
  14. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Brilliant. Also didn't see that you implemented UnitIndexedStruct. I typically assume people do UnitIndexer.INDEX.register() etc, lol, but the module works too ;P.

    Now if only the JASS section had a mod ; (.

    And a question, now that I believe you've tried UnitIndexer, AutoIndex, and AIDS, which one is your fav? lol
     
  15. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    I didn't try AutoIndex yet so the world may never know. ;D
     
  16. watermelon_1234

    watermelon_1234

    Joined:
    Nov 18, 2007
    Messages:
    1,066
    Resources:
    10
    Spells:
    9
    JASS:
    1
    Resources:
    10
    Pretty neat system. =D

    • What does the local variable prev do in both CombatLeave and inCombat=? Wouldn't it just make combatUnit null?
    • Is it really necessary to use
      PauseTimer
      before starting the timer again? (This is in the inCombat= operator.)
    • I think your documentation is wrong about Event. CombatState.EnterCombat.register(trigger) and CombatState.LeaveCombat.register(trigger) should be CombatState.ENTER.registerTrigger(trigger) and CombatState.LEAVE.registerTrigger(trigger). Never mind, this is only in the demo map. =P
    • Not that important, but your demo spells bug if you learn them in different combat states. For example. having a disabled Charge would cause learning Charge to be level 1 again.
     
  17. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    ->What does the local variable prev do in both CombatLeave and inCombat=? Wouldn't it just make combatUnit null?
    http://www.hiveworkshop.com/forums/1810299-post172.html

    ->Is it really necessary to use PauseTimer before starting the timer again?
    no

    Purge, I've been making it a habit to not use the unit handle anymore. I immediately convert to integer and store. Just think, if your combatUnit was an integer and not a unit, you wouldn't have a local to null would you? : P

    It's been benched that nulling locals is slower than calling a function and using them as a parameter (Bribe says so, shh)

    This is what I did in UnitIndexer
    Code (vJASS):

        private integer indexedUnit = 0

        function GetIndexedUnitId takes nothing returns integer
            return indexedUnit
        endfunction
        function GetIndexedUnit takes nothing returns unit
            return units[indexedUnit]
        endfunction
     



    But this isn't a big deal. In fact, it's controversial. If you were to do that, then your GetEventCombatUnit or w/e would be an array read. But most systems do use the GetUnitUserData, so it would probably lower the overhead (no masses of GetUnitUserData calls).

    Your call.

    and your onInit needs to go into a module. As it stands, it's still unstable because of order of initialization.

    If I were to register events on it in a module initializer, it'd register to 0.
     
  18. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    1. Yeah, I added that for recursion as Nes said.
    2. I am not sure, I guess I can remove it, I just remember something about pausing a timer before interacting with it. I can remove it though, since I am probably just not remembering correctly. :ogre_haosis:
    3. Whoops, I guess I didn't update the demo map, lol. I'll fix that once I update it.
    4. Yep ;D I didn't want to bother to debug them, so I left it as is. I meant to learn the skill on initialization so that no one would notice, but I forgot.

    Thanks for the feedback. :D

    I could, but it is negligible. I am not too much of a speed freak; I will make optimizations when I can and improve code, but I won't go out of my way to save a call. :p

    Okay, I'll update that soon.
     
  19. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Code (vJASS):

            private method index takes nothing returns nothing
                set this.inCombat = false
            endmethod
     


    That is silly.. don't implement the UnitIndexStruct just for that >.>.

    on unit death, set inCombat = false (cuz obvioiusly left combat). Not sure what you want to do if the unit rezzes or reincarnates, but anyways, you can figure that out.

    As it stands, if a unit dies, it'll still show them as in combat (funny huh).

    You could use UnitEvent for the on death for all, or you could just do EVENT_PLAYER_UNIT_DEATH or w/e.
     
  20. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Whoops, I clearly don't proofread my code properly. Anyway, I removed that entirely since bools default false.

    Fixed.

    I also removed the PauseTimer() call.