System In Combat Status

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
This system will allow you to check whether a unit is in combat. A unit becomes in combat whenever it attacks, casts a spell, is attacked, or targetted by a spell, and will stay tagged as in combat for 4 seconds after attacks and spells stop.

Leak-less
Lag-less
MUI
MPI

Made in: vJASS


Usage:

Usage is pretty simple. If you want to use it in another JASS Code, use the functions SetUnitInCombat and GetUnitCombatState to get and set the combat state of the unit.

If you want to use it in GUI, you can use custom script to set a boolean variable to GetUnitCombatState(YOUR_UNIT). Then, that variable can be used to refer to the unit's combat status (True if it is in combat, False if it is not).

Requirements:

CSData v. 15.2
PUI v. 5.2

1 Trigger:
Trigger "InCombat"
--To put this in the map,
1. Create a new trigger.
2. Go to Edit -> Convert to Custom Text.
3. Rename it "InCombat"
4. Paste in the Code.

How it Works:

It's a very simple system. When a unit attacks, or casts a spell, it makes both the target as well as the triggering unit in combat. This is done by attaching a struct to the unit, and then running a timer which expires in 4 seconds. If the unit attacks again, that timer will start over again. If the timer expires, the attached struct is destroyed.

Lastly, if a user wants to know if a Unit is in combat or not, it simply returns whether any struct is attached to the unit.

System Code:
JASS:
library InCombat initializer Init requires PUI,CSData

globals
    private constant real COMBAT_DURATION = 4
endglobals

struct CombatState
    //! runtextmacro PUI()
    unit u
    timer t = CreateTimer()
    
    method onDestroy takes nothing returns nothing
        call SetCSData(this.t,0)
        call PauseTimer(this.t)
        call DestroyTimer(this.t)
        set this.u = null
        set this.t = null
    endmethod
endstruct

private function EndCombat takes nothing returns nothing
    local CombatState cs = GetCSData(GetExpiredTimer())
    call cs.release()
endfunction

function GetUnitCombatState takes unit u returns boolean
    local CombatState cs = CombatState<u>
    return cs != 0
endfunction

function SetUnitInCombat takes unit u returns nothing
    local CombatState cs = CombatState<u>
    
    if cs == 0 then
        set cs = CombatState.create()
        set CombatState<u> = cs
    endif
    
    call SetCSData(cs.t,cs)
    call TimerStart(cs.t,COMBAT_DURATION,false,function EndCombat)
endfunction

private function Conditions takes nothing returns boolean
    //Change these to whatever you wish.
    return IsUnitType(GetTriggerUnit(),UNIT_TYPE_HERO) == true or IsUnitType(GetAttacker(),UNIT_TYPE_HERO) == true or IsUnitType(GetSpellTargetUnit(),UNIT_TYPE_HERO) == true
endfunction

private function Actions takes nothing returns nothing
    local unit tar = GetAttacker()
    local unit temp = GetSpellTargetUnit()
    call SetUnitInCombat(GetTriggerUnit())
    if tar != null then
        call SetUnitInCombat(tar)
    elseif temp != null then
        call SetUnitInCombat(temp)
    endif
    set tar = null
    set temp = null
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_ATTACKED)
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t,Condition(function Conditions))
    call TriggerAddAction(t,function Actions)
endfunction

endlibrary</u></u></u>


The example map that is attached allows the user to repick his hero through dialogs. However, you are not allowed to repick your hero, unless he/she is out of combat. Try it out!
 

Attachments

  • In Combat System.w3x
    50.8 KB · Views: 354

Mr.Tutorial

Hard in the Paint.
Reaction score
42
Looks like a nice system, hopefully I can find a way to use this. I'm not a 100% sure of what it is and how it works though, if there is anyway you could show or something.

Overall it looks nice and easy to implant! :thup:
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
I updated the information for what it does:

This system will allow you to check whether a unit is in combat. A unit becomes in combat whenever it is attacked, or is hit by a spell, and will stay tagged as in combat for 4 seconds after attacks and spells stop.

I designed it for my repick system, and what is going to be part of my AI system. I didn't want players typing -repick to get out of combat, since it hides their unit, so I devised this, which requires them to not be in combat.

EDIT: I also updated it so that it works with spells, and only with enemies, to make sure the spells were not buffs.
 

Mr.Tutorial

Hard in the Paint.
Reaction score
42
I updated the information for what it does:



I designed it for my repick system, and what is going to be part of my AI system. I didn't want players typing -repick to get out of combat, since it hides their unit, so I devised this, which requires them to not be in combat.

EDIT: I also updated it so that it works with spells, and only with enemies, to make sure the spells were not buffs.
Yeah man, sounds awesome. I'll definitely find use in this. :shades:
 

darkbeer

Beer is Good!
Reaction score
84
hm, youre using gamecache, i think most people wont like that^^

and im not sure, but isnt therew a limit to how many things you can store in one gamecache? (thought about 256) so wont your system fail if you call it on more than 256 units?

and why dont you use libraries so youll only need one trigger and no map header?
that way you could also make it more customizeables (using globals for combat value ...)

and im not sure, but did you ever heard of cohadars PUI? its great i think you could improve your system greatly using it and stop using gamecache by just using ordinay global arrays / structs.

EDIT: btw. you really eplained the way how to use this well^^
 

Sim

Forum Administrator
Staff member
Reaction score
534
Would you mind making an example map for this system?
 

Sim

Forum Administrator
Staff member
Reaction score
534
Why is it so when you attack you aren't considered in combat?

I was staying at a range blasting ennemies with arrows with my dark ranger and I was never tagged. I know it is intended, but my hero is actually in combat. Even if I'm not being hit.
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
Thanks, I forgot to use both units. I fixed it so it will work with both.

To counter future questions about the new parts of the code, the second unit is set to the first initially, in case their is no spell target. I know that the conditions make it so that it only runs if their IS one, but if someone uses the system their map, and modifies the conditions to make it so all spells put a unit in combat (aoe spells, friendly buffing spells, healing spells, etc), it won't crash when these kinds of abilities are run. I wouldn't want it storing no unit, and crashing when there is no target. ;)
 

emjlr3

Change can be a good thing
Reaction score
395
local handle vars.... u serious???

u should alos mention u need to use the boolean w/o a wait or its not MUI
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
local handle vars.... u serious???

u should alos mention u need to use the boolean w/o a wait or its not MUI

I haven't gotten the hang of vJass yet. I've only read a tutorial or two, and haven't started trying it out. Local handle vars are the only way I know how to attach something to a unit.

Thanks, I'll mention that. :)
 

emjlr3

Change can be a good thing
Reaction score
395
i suggest learning vJASS rather then spending time making system no one will use
 

Sim

Forum Administrator
Staff member
Reaction score
534
I personally find this system cool. A year and a half ago I was looking for such a thing I recall. :D The one I made was scrap, it didn't even work.

People can learn from this and it's useful.
 

Ellimistrox

New Member
Reaction score
10
Ooooh very VERY handy! I could use this in so many ways.. the means are endless i've been waiting a long time for someone to make one after i started on my RPG.
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
I learned vJASS a little bit ago, I'm almost done with the system. I'm testing the attacker and attacked units right now, and I've only identified one bug so far (double free of a struct), which I'm trying to fix.

After I iron out that bug, I'll test it with the spells.

The newest version will actually use a timer instead of a wait action, just to improve its accuracy, and will also include more functionality. For those interested, here's the (unfinished, and a little buggy) code:

Just one drawback: It requires PUI and CSData. PUI uses UnitUserData (in GUI, known as a Unit's Custom Value), which means that systems that use this (besides PUI) will not work.

JASS:
library CombatBool initializer InitTrig requires PUI,CSData

    globals
        private constant real COMBAT_DURATION = 4.
        private constant boolean ALLIED_CASTS_MAKE_UNITS_IN_COMBAT = true
        private constant boolean ALLIED_ATTACKS_MAKE_UNITS_IN_COMBAT = true
    endglobals

    private struct CombatUnit
        unit c
        method onDestroy takes nothing returns nothing
            set this.c = null
        endmethod
    endstruct
    
    private struct CombatState
        boolean IsInCombat = false
        timer Duration
    endstruct

    globals
        private CombatState array CS
    endglobals
    
    private function GiveUnitCombatState takes unit u returns CombatState
        local CombatState cs = CS[GetUnitIndex(u)]
        if cs == 0 then
            set cs = CombatState.create()
            set CS[GetUnitIndex(u)] = cs
            set cs.Duration = CreateTimer()
        endif
        return cs
    endfunction
    
    private function SetUnitCombatState takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local CombatUnit cu = GetCSData(t)
        local CombatState cs = GiveUnitCombatState(cu.c)
        call BJDebugMsg(&quot;endcombat&quot;)
        set cs.IsInCombat = false
        //set cs.Duration = null
        call DestroyTimer(t)
        set t = null
        call CombatUnit.destroy(cu)
    endfunction
        
    function GetUnitCombatState takes unit u returns boolean
        local CombatState cs = CS[GetUnitIndex(u)]
        if cs == 0 then
            set cs = CombatState.create()
            set CS[GetUnitIndex(u)] = cs
        endif
        return cs.IsInCombat
    endfunction
    
    private function SetUnitInCombat takes unit u returns nothing
        local CombatState cs
        local CombatUnit cu = CombatUnit.create()
        call BJDebugMsg(&quot;2&quot;)
        if CS[GetUnitIndex(u)].Duration != null then
            call CombatUnit.destroy(GetCSData(CS[GetUnitIndex(u)].Duration))
        endif
        set cs = GiveUnitCombatState(u)
        set cs.IsInCombat = true
        set cu.c = u
        call SetCSData(cs.Duration,cu)
        call TimerStart(cs.Duration,COMBAT_DURATION,false,function SetUnitCombatState)
    endfunction
            
    private function Conditions takes nothing returns boolean
        local unit c = GetTriggerUnit()
        local unit t = GetAttacker()
        if t == null and GetSpellTargetUnit() != null then
            set t = GetSpellTargetUnit()
            if IsUnitAlly(c,GetOwningPlayer(t)) then
                return ALLIED_CASTS_MAKE_UNITS_IN_COMBAT
            endif
        elseif t != null and GetSpellTargetUnit() == null then
            if IsUnitAlly(t,GetOwningPlayer(c)) then
                return ALLIED_ATTACKS_MAKE_UNITS_IN_COMBAT
            endif
        endif
        return true
endfunction
    
    private function Actions takes nothing returns nothing
        local unit u = GetTriggerUnit()
        local unit c = GetSpellTargetUnit()
        local unit t = GetAttacker()
        call BJDebugMsg(&quot;entering&quot;)
        call SetUnitInCombat(u)
        if c != null then
            call SetUnitInCombat(c)
        elseif t != null then
            call SetUnitInCombat(t)
        endif
        set u = null
        set c = null
        set t = null
    endfunction
        
    private function InitTrig takes nothing returns nothing
        local trigger CombatBool = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(CombatBool,EVENT_PLAYER_UNIT_ATTACKED)
        call TriggerRegisterAnyUnitEventBJ(CombatBool,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(CombatBool,Condition(function Conditions))
        call TriggerAddAction(CombatBool,function Actions)
    endfunction
endlibrary
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
I just put one for a normal user to see and be able to make changes without accidentally changing something that makes the script malfunction. Plus, the second has an array of one of the structs, which means that the struct has to be above it, unless I put in a keyword. BTW, that was fast! :p

EDIT: Also, I noticed something very strange when I was uploading the code. I forgot to add CSData to the library requirements. It was working fine, and not giving any errors (although I was only using two units). Even though the code contains function calls from in the other library. Strange that the syntax checker didn't even catch that.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top