• πŸ† 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!

Timed Ability/Stack v3.0.0.1

Timed Ability/Stack System

Version: 3.0.0.1

System is GUI-friendly.

Requirements:
- Standard World Editor

Why?
I decided to create this system due to lack of similar one and..
..because Blizzard didn't make Save Ability handle in Hashtables ever work!


Configurables:
- Refresh/no refresh option
- Customize duration
- Customize amount of stacks

Refresh option:
Enables user to choose if ability's duration should be reshreshable or not.

1) With refresh - abilities that do refresh with every action that applies them.
2) Without refresh - abilities that don't refresh with every new application, but each stack has it's own private duration.

Actions to apply:
  • Sample
    • Events
    • Conditions
    • Actions
      • Set TASability = (Ability being cast)
      • Set TASunit = (Triggering unit)
      • Set TASduration = 0.00
      • Set TASrefresh = True
      • Set TASmaxstack = 0
      • Trigger - Run Timed AS <gen> (ignoring conditions)
See System's folder in trigger editor for futher informations and documentation.
Check "Examples" in form of three spells for differend usage of system.


System's code:
JASS:
//***********************************************************************************
//*
//*       ____ __   ____        _______________________________________
//*      /_  _| _\ / __/        |      T I M E D   A B I L I T Y      |
//*        | || β€” \\__ \        |               A N D                 |
//*        |_||_|\_\___/        |        S T A C K   S Y S T E M      |
//*         By Spinnaker        'β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”'
//*                                                         v3.0.0.1
//*
//*    What is TAS?
//*    β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
//*
//*       There are lots situations when you want your spells/abilities to add some
//*       kind of timed buff/debuff which can be increased with new applications.
//*       Timed ability/stack system allows you to time any ability you want,
//*       and makes ability-stacking an easy job. 
//*
//*       System consists of one trigger. Script - depending on option chosen -
//*       divides abilities into two groups:
//*
//*       1) Without refreshing - runs abilities that don't refresh with
//*          new applications, but each stack has it's own private duration.
//*
//*       2) With refreshing - runs abilities that do refresh with every
//*          new application.
//*     
//*
//*    Reason - why?
//*    β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
//*
//*       I decided to create this system due to lack of such and the high
//*       frequency of adding timed stuff in various triggers.
//*       Great example where such stacking abilities exist is obviously DotA.
//*
//*
//*    How to implement?
//*    β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
//*
//*       1) Be sure "Automatically create unknown variables while pasting
//*          trigger data" is enabled in the World Editor general preferences.
//*
//*       2) Copy and paste trigger 'TAS Veriable Creator' into your map for
//*          automatical variable creation
//*
//*       3) Copy trigger "Timed AS" and paste it into your map.
//*          System requires no Object Editor stuff.
//*
//*       System is implemented. To run it properly see the next part.  
//*
//*
//*    What needs to be done?
//*    β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
//*
//*       Set TASability to ability you want to be timed. 
//*       Now, choose unit that a timed ability will be added to.
//*       Next step is setting duration of the ability in field TASduration.
//*
//*       Select the refresh option. Choosing 'True' will let your ability
//*       refresh even if maximum value of stacks has been reached, while 'False'
//*       converts abilities to private stacks which don't interfere with each other.
//*
//*       Enter maximum amount of applications in TASmaxstack.
//*       Abilities with maximum value won't be increased, but can refreash.
//*      
//*     Leave the rest for the system, enjoy!
//*     β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
//*
//*
//***********************************************************************************


//***************************************************************************
//*
//*  Global Variables
//*
//***************************************************************************


    //* udg_TAShash                           Hashtable for refreshable abilities issues
    //* udg_TASindex                          Counts all running instances
    //* udg_TAStimer                          Global timer for periodic issues
    //* udg_TASdur[]                          Stores duration of each instance
    //* udg_TASend[]                          Checks if given instance should be removed
    //* udg_TASids[]                          Contains all the ability data
    //* udg_TASref[]                          Checks if ability manipulated is refreshable or not
    //* udg_TASunits[]                        For unit storage
    
  //* User friendly parameters
  
    //* udg_TASability                        Gets new ability
    //* udg_TASunit                           Gets new unit
    //* udg_TASduration                       Gets duration for new ability
    //* udg_TASrefresh                        Sets instance type
    //* udg_TASmaxstack                       Maximum amount of stacks for given ability

    
//***************************************************************************
//* Constant interval function
//***************************************************************************

    constant function TASinterval takes nothing returns real
        return 0.031250000
    endfunction

//***************************************************************************
//*
//*  System itself
//*
//***************************************************************************


function TASallocate takes integer id returns nothing
    set udg_TASindex                     = udg_TASindex + 1
    set udg_TASids[udg_TASindex]         = id
    set udg_TASunits[udg_TASindex]       = udg_TASunit
    set udg_TASdur[udg_TASindex]         = udg_TASduration
    set udg_TASref[udg_TASindex]         = udg_TASrefresh
    set udg_TASend[udg_TASindex]         = false
endfunction

function TASdeallocate takes integer i returns nothing
    set udg_TASunits[i]                  = udg_TASunits[udg_TASindex]
    set udg_TASids[i]                    = udg_TASids[udg_TASindex]
    set udg_TASdur[i]                    = udg_TASdur[udg_TASindex]
    set udg_TASref[i]                    = udg_TASref[udg_TASindex]
    set udg_TASend[i]                    = udg_TASend[udg_TASindex]
    set udg_TASindex                     = udg_TASindex - 1
endfunction

function TASunitcheck takes unit u returns boolean
    return IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0
endfunction

function TAScallback takes nothing returns nothing
    local integer i = 1
    local integer h
    local integer n
    local boolean b
    local player p
    loop
        exitwhen i > udg_TASindex
        if udg_TASref[i] then
            set h = GetHandleId(udg_TASunits[i])
            set udg_TASdur[i] = LoadReal(udg_TAShash, h, udg_TASids[i])
        endif
        set udg_TASdur[i] = udg_TASdur[i] - TASinterval()
        set b = udg_TASref[i] or GetUnitAbilityLevel(udg_TASunits[i], udg_TASids[i]) < 2 
        if (udg_TASdur[i] <= 0. and b) or TASunitcheck(udg_TASunits[i]) then
            set udg_TASend[i] = true
            call UnitRemoveAbility(udg_TASunits[i], udg_TASids[i])
        elseif udg_TASdur[i] <= 0. and not b then
            set udg_TASend[i] = true
            set p = GetOwningPlayer(udg_TASunits[i])
            call SetPlayerAbilityAvailable(p, udg_TASids[i], false)
            call DecUnitAbilityLevel(udg_TASunits[i], udg_TASids[i])
            call SetPlayerAbilityAvailable(p, udg_TASids[i], true)
        elseif udg_TASref[i] then
            call SaveReal(udg_TAShash, h, udg_TASids[i], udg_TASdur[i])
        endif
        if udg_TASend[i] then
            if udg_TASref[i] then
                set n = LoadInteger(udg_TAShash, h, 0) - 1
                if 0 == n then
                    call FlushChildHashtable(udg_TAShash, h)
                else
                    call SaveInteger(udg_TAShash, h, 0, n)
                endif
            endif        
            call TASdeallocate(i)
            set i = i - i
            if 0 == udg_TASindex then
                call PauseTimer(udg_TAStimer)
            endif 
        endif
        set i = i + 1
    endloop
    set p = null
endfunction  

function TASexecute takes nothing returns nothing
    local integer id = udg_TASability
    local integer n = GetUnitAbilityLevel(udg_TASunit, id)
    local player p
    if 0 == udg_TASindex then
        call TimerStart(udg_TAStimer, TASinterval(), true, function TAScallback)
    endif
    if UnitAddAbility(udg_TASunit, id) then
        call TASallocate(id)
    elseif n < udg_TASmaxstack then
        set p = GetOwningPlayer(udg_TASunit)
        call SetPlayerAbilityAvailable (p, id, false)
        call IncUnitAbilityLevel (udg_TASunit, id)
        call SetPlayerAbilityAvailable (p, id, true)
        if not udg_TASrefresh then
            call TASallocate(id)
        endif
        set p = null
    endif    
    if udg_TASrefresh then
        set n = GetHandleId(udg_TASunit)
        if LoadReal(udg_TAShash, n, id) == 0. then
            call SaveInteger (udg_TAShash, n, 0, LoadInteger(udg_TAShash, n, 0) + 1)
        endif
        call SaveReal(udg_TAShash, n, id, udg_TASduration)
    endif
endfunction

//***************************************************************************    
function InitTrig_Timed_AS takes nothing returns nothing
    set gg_trg_Timed_AS        = CreateTrigger()
    set udg_TAStimer           = CreateTimer()
    set udg_TAShash            = InitHashtable()
    call TriggerAddAction(gg_trg_Timed_AS, function TASexecute)
endfunction
- System temp group creating has been changed. Thanks to Magtheridon96.
- Added credits to Weep for GDD.
- Documentation and instructions have been corrected and improved.
- Added Spellbook option for supporting spellbook abilities.
- Uploaded v2: User no longer has to fill custom script with rawcode of ability, system's efficiency has been greatly improved. HandleIds are now taken immediately from given unit (temporary groups are no longer required). Indexing system has been changed.
- Fixed issue with hidding spellbook abilities while adding them at the first time. Thanks to Falendor.


- Version 3.0.0.1 released. System has been converted into Jass. User is no forced to initialize hashtable. Spellbook option has been removed due to complications with increasing spellbook's level. Afterall user can increase level of abilities inside it (thats all about "increasing spellbook") what provides much smoother solution. Making ability invisible by disabling - for given player - spellbook in which it is should be done at map initialization by performing player action, instead of doing so during system's execution - it's not what this system was created for.

Important
NOTE: For each action you want add a timed ability to, it is recommended to create unique abilities. Don't ever use the same ability with 'Refresh' option in one action and 'No refresh' option in another.

NOTE: There are few exceptions. Some abilities can not be manipulated at all, like bonuses for hero statistics (eg. from Cirlet of Nobility).
It's not the system fault, they are just hardcoded.


I'm sure, most of you know that abilities (especialy if they're custom ones) should be preloaded to prevent lag at the first use of it. Just to ensure you remember that :) If you don't know how to do it, see map initialization trigger for an egzample.

It'd would be great if you give me credits after using system in your map and note to not edit system without asking me first.

Keywords:
System, Ability, Stack, Time, Timed, Refresh, Spinnaker
Contents

Timed Ability/Stack System (Map)

Reviews
19th Oct 2011 Bribe: This has its uses. I would expand on the description a bit better to show what situations you'd want to use this in. I can figure out some but it's not really clear why to use this.

Moderator

M

Moderator

19th Oct 2011
Bribe: This has its uses. I would expand on the description a bit better to show what situations you'd want to use this in. I can figure out some but it's not really clear why to use this.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
There is a documentantion in map. If any new one opens map he would quickly get what to do.
If you didn't bother to check map, or try to look at it, i'm disappointed.
I know that abilities have IDs, but i see you both get me wrong.

Blizzard never ended Editor in full, most Hashtable actions just crush your Editor, thats why i said Abilities Handle in Hashtables don't work, because they never did.
 
Last edited:
I think its good if you can add a check if the unit still has the ability or if he's still alibe, if not, proceed with the recycling of the indexes... this is good in cases that the user forces the removal of the ability of when a unit dies, so that it won't loop thru useless indexes...

also, I think its better to loop thru the units (meaning you need to save the units into indexed variables), instead of the abilities and then picking every unit which has it (which leads you to creating a group and destroying it afterwards, every loop thru an ability, every .05 seconds)...
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
I think its good if you can add a check if the unit still has the ability or if he's still alibe, if not, proceed with the recycling of the indexes... this is good in cases that the user forces the removal of the ability of when a unit dies, so that it won't loop thru useless indexes...

also, I think its better to loop thru the units (meaning you need to save the units into indexed variables), instead of the abilities and then picking every unit which has it (which leads you to creating a group and destroying it afterwards, every loop thru an ability, every .05 seconds)...

I guess you didn't read triggers carefully. There are conditions that chcecks if unit is alive or if it still has given ability.

Man: units are saved in variables, so they go through in loop. Picking is for hashtables to eliminate bugs when finding refreshable ability. I wouldn't do that if i didn't have to.

System is combination of hashtable and indexing thats why it doesn't look as normal MUI spell/system when usually you use only one of them. It was needed for refresh option, and trust me, it's reliable this way. I've minimalized the hashtable part.

I recommend to look at it once again, because in my eyes you didn't look at it.
If i miss understood you, appologizes.
 
as the time here is pretty late, (almost 11pm), it seems that the not so good set-up of the trigger image made a confusion to my eyes... sorry for that...

though for the temporary groups, I think its better to use set bj_wantdestroygroup = true or something like that which is placed before a group is enumerated, that way you won't need to destroy it yourself, and you can enumerate the group directly in the Unit Group - Pick every unit in blahblah, instead of setting it in a variable...
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
The reason is:
System was made about 1 year ago, and if you want to know it was totaly in GUI. I'm learning vJass right now.

Jass is for sure much more efficient thats why most of actions were converted from BJ into natives. However, system won't see its Jass version untill I do it properly and efficent in vJass.
 
Level 3
Joined
Aug 21, 2009
Messages
73
Thanks for the help :) Tbh this system looks good, didn't know that that part of WE didn't work :p not that I've tried to use it then.
 
Level 4
Joined
Jan 19, 2008
Messages
69
The system is splendid, however, I encountered a problem using "refresh" option. I stacked Neutralization Field on a group of enemies and waited for the effect to wear off. Afterwards whenever I tried to stack Neutralization Field up on enemy units, the effect of the ability would be removed instantly making the ability useless. I hope you'll fix the bug if you find the time.
 
Level 2
Joined
Feb 24, 2012
Messages
12
asked me how to write negative for the ability, I can not write, for example such as -30 amor, sorry,my english not very well!
 
First make sure you have allowed negative values via the preferences...

then to put a nega value

HOLD SHIFT then double click the part where you will input the negative value (armor value for example)
RELEASE SHIFT then input the negative number (-30 for example)
press enter...

if it turns out 0, HOLD SHIFT then enter instead of just enter
 
Top