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

TimedAbility v1.1

This bundle is marked as awaiting update. A staff member has requested changes to it before it can be approved.
TimedAbility v1.1
Allows to add an ability to unit for given duration.
function UnitAddTimedAbility(takes :
  1. unit <u>
  2. integer <abiId>
  3. integer <abiLevel>
  4. real <duration>
  5. boolean <removeOnDeath> - if "false" ability won't be removed upon death but when <duration> expires
  6. integer <buffId> - if specified, instance will be interrupted when no buf detected
  7. real <delay> - if <buffId> specified it will start checking after <delay> value
  8. string <eff> - speciall effect attached to unit <u>
  9. string <attachmentPoint>
(for one-level ability & without checking for buff & without effect):
function UnitAddTimedAbility_Simple(takes
  1. unit <u>
  2. integer <abiId>
  3. real <duration>
  4. boolean <removeOnDeath>

library
JASS:
//--------------------------------------------------------------------------------------
library TimedAbility // v.1.1 by ZibiTheWand3r3r. Allows to add an ability to unit for given duration.
//--------------------------------------------------------------------------------------
// API
// nothing UnitAddTimedAbility(unit <u>, integer <abiId>, integer <abiLevel>, real <duration>,
//      boolean <removeOnDeath>, integer <buffId>, real <delay>, string <eff>, string <attachmentPoint>)

// additional function (for one-level ability & without checking for buff & without effect):
// nothing UnitAddTimedAbility_Simple(unit <u>, integer <abiId>, real <duration>, boolean <removeOnDeath>)
//
// If unit already has ability <abiId> then this library will:
//      overwrite ability level / duration / effect - if previously added <abiId> comes from this library
//      overwrite ability level - if <abiId> do not comes from this library
//                                  it will set-back to old level (coming from non-library ability) after instance expires
// If <buffId> is specified and there's no buff on unit <u> - instance will be ended.
//                                  Checking for buff begins after <delay> value (sec); <delay> should be >=0.00
// Use <buffId>==0 and <delay>==0.00 to not checking for a buff.
// Use <eff>==null and <attachmentPoint>==null to not play speccial effect on unit <u>
//
// Do not use with hero-ability if this ability is used as normal, level-able ability by any hero in your map.
//--------------------------------------------------------------------------------------
native UnitAlive takes unit id returns boolean

//--------------------------------------------------------------------------------------
globals
    private constant boolean            DEBUG = true
    private constant real               INTERVAL = 0.10
    private integer                     maxId = 0
    private unit array                  instTarget
    private integer array               instAbilityId
    private real array                  instDuration
    private boolean array               instRemoveWhenUnitDies
    private integer array               instBuffId
    private real array                  instBuffCheckDelay
    private integer array               instCounter
    private boolean array               instBuffSet
    private effect array                instEffect
    private integer array               externalAbiLevel
    private timer                       t = CreateTimer()
endglobals
//-----------------------------------------------------------
private function Loop takes nothing returns nothing
    local integer id=1
    loop
        exitwhen id>maxId

        if instBuffSet[id] then
            set instCounter[id] = instCounter[id] - 1
            if instCounter[id] <= 0 and (GetUnitAbilityLevel(instTarget[id], instBuffId[id])==0) then
                set instDuration[id]=0.00
            endif       
        endif
        set instDuration[id] = instDuration[id] - INTERVAL
        if instDuration[id] <= 0.00 or (instRemoveWhenUnitDies[id] and (not UnitAlive(instTarget[id]))) then
            static if DEBUG then
                call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "ABI instance ended " + GetObjectName(instAbilityId[id]) + " on unit " + GetUnitName(instTarget[id]))
            endif
            //---
            call DestroyEffect(instEffect[id])
            if externalAbiLevel[id]==0 then //remove ability
                call UnitRemoveAbility(instTarget[id], instAbilityId[id])
            else    // set back to old level
                call SetUnitAbilityLevel(instTarget[id], instAbilityId[id], externalAbiLevel[id])
            endif
            //---
            //dealloc: /move data from max to current/
            set instTarget[id] = instTarget[maxId]
            set instAbilityId[id] = instAbilityId[maxId]
            set instDuration[id] = instDuration[maxId]
            set instRemoveWhenUnitDies[id] = instRemoveWhenUnitDies[maxId]
            set instBuffId[id] = instBuffId[maxId]
            set instBuffCheckDelay[id] = instBuffCheckDelay[maxId]
            set instCounter[id] = instCounter[maxId]
            set instBuffSet[id] = instBuffSet[maxId]
            set instEffect[id] = instEffect[maxId]
            set externalAbiLevel[id] = externalAbiLevel[maxId]
            //clean:
            set instTarget[maxId]=null
            set instEffect[maxId]=null
            //---
            set maxId=maxId-1
            set id=id-1
            if maxId==0 then
                call PauseTimer(t)
            endif

        endif

        set id=id+1
    endloop
endfunction
//----------------------------------------------------------------------------------------------
private function Update takes unit u, integer abiId, integer abiLevel, real duration, string eff, string attachmentPoint returns boolean
    local integer id=1
    loop
        exitwhen id>maxId
        if u==instTarget[id] and abiId==instAbilityId[id] then
            set instDuration[id] = duration //overwrite duration
            call SetUnitAbilityLevel(u, abiId, abiLevel) //overwrite ability level
            //overwrite effect:
            call DestroyEffect(instEffect[id])
            if not (eff==null or attachmentPoint==null or eff=="" or attachmentPoint=="") then
                set instEffect[id] = AddSpecialEffectTarget(eff, u, attachmentPoint)
            endif
            //---
            static if DEBUG then
                call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, GetUnitName(u) +" updated duration/level/efect on ability "+ GetObjectName(abiId))
            endif
            return true
        endif
        set id=id+1
    endloop
    return false
endfunction
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
function UnitAddTimedAbility takes unit u, integer abiId, integer abiLevel, real duration, boolean removeOnDeath, integer buffId, real delay, string eff, string attachmentPoint returns nothing
    local integer oldLevel = GetUnitAbilityLevel(u, abiId) //remember ability level
    if oldLevel>0 and Update(u, abiId, abiLevel, duration, eff, attachmentPoint) then
        return //has ability [from this library] and ability was updated succefully
    endif
   
    set maxId = maxId + 1
    set instTarget[maxId] = u
    set instAbilityId[maxId] = abiId
    set instDuration[maxId] = duration
    set instRemoveWhenUnitDies[maxId] = removeOnDeath
    set instBuffId[maxId] = buffId
    set instBuffCheckDelay[maxId] = delay
    set instCounter[maxId] = 0
    if delay>0.00 then
        set instCounter[maxId] = R2I(delay/INTERVAL) + 1 //for delayed check buff
    endif
    set instBuffSet[maxId] = (buffId != 0)
    set instEffect[maxId] = null
    if not (eff==null or attachmentPoint==null or eff=="" or attachmentPoint=="") then
        set instEffect[maxId] = AddSpecialEffectTarget(eff, u, attachmentPoint)
    endif
    set externalAbiLevel[maxId] = oldLevel
   
    call UnitAddAbility(u, abiId)
    call SetUnitAbilityLevel(u, abiId, abiLevel)
   
    if maxId==1 then
        call TimerStart(t, INTERVAL, true, function Loop)
    endif   
endfunction
//---------------------------------------------------------------------------
function UnitAddTimedAbility_Simple takes unit u, integer abiId, real duration, boolean removeOnDeath returns nothing
    call UnitAddTimedAbility(u, abiId, 1, duration, removeOnDeath, 0, 0.00, null, null)
endfunction
endlibrary

hot to use
JASS:
function Trig_ef_Actions takes nothing returns nothing
    local string ef="Abilities\\Spells\\Undead\\UnholyFrenzy\\UnholyFrenzyTarget.mdl"
    local string ef1="Abilities\\Spells\\Other\\FrostDamage\\FrostDamage.mdl"
    local unit u = gg_unit_Emoo_0004
 
 
    call UnitAddTimedAbility(u, 'AHbz', 3, 25.00, true, 'BHad', 1.50, ef, "overhead")
    call UnitAddTimedAbility(u, 'A002', 2, 25.00, false, 0, 0.00, ef1, "origin")
    call UnitAddTimedAbility_Simple(u, 'AItg', 15.00, true)

endfunction

for GUI use
1. variable creator
  • GUITimedAbilityVars
    • Events
    • Conditions
    • Actions
      • Set TAabi = TAabi
      • Set TAabiLvl = 0
      • Set TAattachPoint = <Empty String>
      • Set TAbuff = TAbuff
      • Set TAbuffCheckDelay = 0.00
      • Set TAduration = 0.00
      • Set TAeffect = <Empty String>
      • Set TAremoveOnDeath = False
      • Set TAunit = No unit
  • GUITimedAbiExec
    • Events
    • Conditions
    • Actions
      • -------- there's nothing to configure here --------
      • Custom script: call UnitAddTimedAbility(udg_TAunit, udg_TAabi, udg_TAabiLvl, udg_TAduration, udg_TAremoveOnDeath, udg_TAbuff, udg_TAbuffCheckDelay, udg_TAeffect, udg_TAattachPoint)
      • -------- reset some variables for next use (if user decides to not use them) --------
      • Custom script: set udg_TAbuff=0
      • Custom script: set udg_TAbuffCheckDelay=0.00
      • Custom script: set udg_TAeffect=null
      • Custom script: set udg_TAattachPoint=null
use in GUI
  • wave version1
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Healing Wave
    • Actions
      • -------- these parameters must be set before running trigger --------
      • Set TAunit = (Target unit of ability being cast)
      • Set TAabi = Evasion (custom)
      • Set TAabiLvl = 1
      • Set TAduration = 20.00
      • Set TAremoveOnDeath = True
      • Trigger - Run GUITimedAbiExec <gen> (ignoring conditions)
  • roar version2
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Roar
    • Actions
      • Unit Group - Pick every unit in (Units within 500.00 of (Position of (Triggering unit)) matching (((Matching unit) is A structure) Equal to False)) and do (Actions)
        • Loop - Actions
          • Set TAunit = (Picked unit)
          • Set TAabiLvl = 1
          • Set TAabi = Item Life Bonus (Greater)
          • Set TAduration = 15.00
          • Set TAremoveOnDeath = True
          • Set TAbuff = Roar
          • Set TAbuffCheckDelay = 0.70
          • Trigger - Run GUITimedAbiExec <gen> (ignoring conditions)
  • holy version3
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Holy Light
    • Actions
      • Set TAunit = (Target unit of ability being cast)
      • Set TAabi = Evasion (custom)
      • Set TAabiLvl = 3
      • Set TAduration = 25.00
      • Set TAremoveOnDeath = True
      • Set TAbuff = Devotion Aura
      • Set TAbuffCheckDelay = 0.00
      • Set TAeffect = Abilities\Spells\Undead\AntiMagicShell\AntiMagicShell.mdl
      • Set TAattachPoint = overhead
      • Trigger - Run GUITimedAbiExec <gen> (ignoring conditions)
Contents

TimedAbility v1.1 (Map)

Reviews
Flux
Some thoughts: - I think you overdo the number of input arguments. I think the only important ones are unit u, integer abilityId and real duration. Based on experience, functions with lots of input arguments are a pain to work with. You offered an...
Level 18
Joined
Nov 21, 2012
Messages
835
I'm going to use this lib in UtimateBattle project to simplify many spells, I'm very interested in your opinions / checks
edit 06-07-2017
updated to version 1.1

in function UnitAddTimedAbility I replaced
JASS:
    local integer oldLevel = 0
    if GetUnitAbilityLevel(u, abiId)>0 then
        if Update(u, abiId, abiLevel, duration, eff, attachmentPoint) then //has ability /from this library/ and updated succefully
            return
        else                                                        //has ability but *not* from this library
            set oldLevel = GetUnitAbilityLevel(u, abiId) //remember ability level
        endif
    endif
to
JASS:
    local integer oldLevel = GetUnitAbilityLevel(u, abiId) //remember ability level
    if oldLevel>0 and Update(u, abiId, abiLevel, duration, eff, attachmentPoint) then
        return //has ability [from this library] and ability was updated succefully
    endif
 
Last edited:
Level 5
Joined
May 2, 2015
Messages
109
My quick review :)
-I think UnitAddTimedAbility should returns integer as user will be able to to store the id
-
JASS:
            set instTarget[id] = instTarget[maxId]
            set instAbilityId[id] = instAbilityId[maxId]
            set instDuration[id] = instDuration[maxId]
            set instRemoveWhenUnitDies[id] = instRemoveWhenUnitDies[maxId]
            set instBuffId[id] = instBuffId[maxId]
            set instBuffCheckDelay[id] = instBuffCheckDelay[maxId]
            set instCounter[id] = instCounter[maxId]
            set instBuffSet[id] = instBuffSet[maxId]
            set instEffect[id] = instEffect[maxId]
            set externalAbiLevel[id] = externalAbiLevel[maxId]
            //clean:
            set instTarget[maxId]=null
            set instEffect[maxId]=null
            //---
            set maxId=maxId-1
            set id=id-1
I don't think this kind of indexing is suitable for this kind of system.
Imagine, the maxId is 20 then user destroy 15. So,
JASS:
            set instTarget[15] = instTarget[20]
            set instAbilityId[15] = instAbilityId[20]
            set instDuration[15] = instDuration[20]
            ...
            //clean:
            set instTarget[20]=null
            set instEffect[20]=null
            //---
            set maxId=maxId-1 //==19
 
Level 18
Joined
Nov 21, 2012
Messages
835
instance nr 15 expires
data from instance 20 (maxID) has been moved to variables with index 15
there are 19 active instances,
IF new instance will be created it will be with number 20
I cant see anything abnormal here.. or I did not understand what you mean.

original native UnitAddAbility returns boolean. What for user should need integer id? It is ability added for given duration, or when speciic buff dissaperars, instantce is not supposed to be destroy-able by a user.
 
Level 5
Joined
May 2, 2015
Messages
109
:D
JASS:
if instCounter[id] <= 0 and (GetUnitAbilityLevel(instTarget[id], instBuffId[id])==0) then
I'd miss that part, XP.
So, everything looks fine but like I always say, better with struct

Btw, is there any system like this out there?
 

EdgeOfChaos

E

EdgeOfChaos

You could simply this system immensely by using TimerUtils with a struct that remembers the ability added and the unit, plus whatever other fields you want. For example:
JASS:
struct TempAbilityData
    unit whichUnit
    integer whichAbility
endstruct
And in place of that big timer loop:
JASS:
local TempAbilityData data = TempAbilityData.create()
set data.whichUnit = ...
set data.whichAbility= ...
call TimerStart(NewTimerEx(data),duration,false,function removeAbility)
Then in your removeAbility, just grab the data and do whatever you need
JASS:
local timer t = GetExpiredTimer()
local TempAbilityData data = GetTimerData(t)
call ReleaseTimer(t)
//now use data.whichUnit and data.whichAbility
 
Level 18
Joined
Nov 21, 2012
Messages
835
TimedHandles - Wc3C.net
TimedHandles Use this to destroy a handle after X amount of seconds.
I see examples to use TimedHandles for timed effects and lightning, I dont know how to extend this for timed abilities. Sorry Reventhous, I still don't get structs ;) And it uses hashtable.

This lib has no requirements, no hashtable. With each instance it is possible to set boolean <removeOnDeath>, you can (not obligatory) specify a buff, and add special effect (if you wish to). I guess I described function UnitAddTimedAbility parameters well at the top.

I just uploaded this to gather users opinions. I already use this lib in UB project after many tests. If you decide it is useless - no problem for me to remove this library.
 
Level 22
Joined
Feb 6, 2014
Messages
2,466
Some thoughts:
- I think you overdo the number of input arguments. I think the only important ones are unit u, integer abilityId and real duration. Based on experience, functions with lots of input arguments are a pain to work with. You offered an alternative which is "UnitAddTimedAbility_Simple" but I think this one should be the main API and just add "UnitAddTimedAbilityEx" that includes all your other stuffs (buff, effects, attachments) if you really want to include those.
- I haven't thoroughly check the code, but I think applying this sequence of actions will reveal a bug.
> Add ability X for 5 seconds
> Wait 3 seconds
> Add ability X (same ability) for 10 seconds
> Wait 2 seconds
> Ability gets removed when it should not be. (Bug)
> Wait 8 seconds
> This is the part when ability should be removed.
 
TimedHandles - Wc3C.net

I see examples to use TimedHandles for timed effects and lightning, I dont know how to extend this for timed abilities. Sorry Reventhous, I still don't get structs ;) And it uses hashtable.

This lib has no requirements, no hashtable. With each instance it is possible to set boolean <removeOnDeath>, you can (not obligatory) specify a buff, and add special effect (if you wish to). I guess I described function UnitAddTimedAbility parameters well at the top.

I just uploaded this to gather users opinions. I already use this lib in UB project after many tests. If you decide it is useless - no problem for me to remove this library.

I'm not updated to Hive's JASS policy afaik hashtables are of no concern. TimedHandle can perform with or without the hashtable.

Anyways, I can't say if i'm right or wrong, I have been inactive in this community for like 2 years lol
 
Top