• 🏆 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!

Simple Temporary Ability System

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
  • Like
Reactions: Seralai
Simple Temporary Ability System.
Version: 0.0.3


Now it make sense!


When a unit cast a certain spell, the system will add another spell that correspond to the spell casted.

This spell added will be in the hero spell a certain time, called expiration time.

Is usefull if you want create spell chain, combo, or next spell behaviour


How it works?

1. Create your spells, in this case will use one base, and two more. the flow will be:
1.1 when base you will get combo
1.2 when cast combo you will get wombo
1.3 If you set the icon for all the spell at same values, you will overwrite the spell.
1.4 If you set different pos(x,y) you will get multiple icon, take care about the expiration time, because if the NEXT spell have more time than the BASE you can keep spamming the NEXT spell.

2. In STAS LIBRARY save the globals variable of your spell id, need to the expiration time of the spell. this mean, the amount of time that temporary spell will remain in unit available spells.

3. Use the CreateTemporaryAbility to create the temporary chain between the spell.

4. Use the CastTemporaryAbility to use the system.

5. Enjoy!


Advantages

* Is MUI
* Use timers
* Easy configuration
* Now with Create and Cast function!

Version 0.0.3

JASS:
globals
    hashtable STASSkills = InitHashtable()
    hashtable STASIndexer = InitHashtable()

    integer ONE_SPELL          = 'A000'
    integer ONE_TO_TWO_SPELL   = 'A001'
    integer TWO_TO_THREE_SPELL = 'A002'

    real ONE_TO_TWO_SPELL_ET = 3.00
    real TWO_TO_ONE_SPELL_ET = 1.00

endglobals

library STAS
    function CreateTemporaryAbility takes integer bs, integer ns, real nset returns nothing
        call SaveInteger(STASSkills, bs, StringHash("NEXT"), ns)
        call SaveReal(STASSkills, bs, StringHash("EXPTIME"), nset)
    endfunction

    function STASTemporaryTimeHandler takes nothing returns nothing
        // USE: On timer expiration, remove the next spell

        // Get the expired timer
        local timer t = GetExpiredTimer()

        // Get the caster unit through timer ID
        local unit caster = LoadUnitHandle(STASIndexer, GetHandleId(t), StringHash("CASTER"))

        // Get the next_spell spell ID
        local integer NEXT = LoadInteger(STASIndexer, GetHandleId(t), StringHash("NEXT"))

        // Remove the spell
        call UnitRemoveAbility(caster, NEXT)

        // For avoid bugs of timer, pause it
        call PauseTimer(t)

        // Destroy the timer
        call DestroyTimer(t)

        // Remove leak
        set t = null
        set caster = null
    endfunction

    function CastTemporaryAbility takes unit cu, integer cs returns nothing
        local real et
        local timer t
        local integer ns = LoadInteger(STASSkills, cs, StringHash("NEXT"))

        if (ns != 0) then

            // Initialize the timer
            set t = CreateTimer()

            // Save expiration time
            set et = LoadReal(STASSkills, cs, StringHash("EXPTIME"))
  
            // Save to hashtable the unit that cast the spell
            call SaveUnitHandle(STASIndexer, GetHandleId(t), StringHash("CASTER"), cu)

            // Save the next_spell spell ID
            call SaveInteger(STASIndexer, GetHandleId(t), StringHash("NEXT"), ns)

            // Add new spell to caster
            call UnitAddAbility(cu, ns)

            // Start the timer
            call TimerStart(t, et, false, function STASTemporaryTimeHandler)
        else
             return
        endif
 
        // Avoid leaks
        set cu = null
        set t = null
    endfunction

endlibrary


JASS:
function sca takes nothing returns nothing

    call CreateTemporaryAbility(ONE_SPELL, ONE_TO_TWO_SPELL, ONE_TO_TWO_SPELL_ET)
    call CreateTemporaryAbility(ONE_TO_TWO_SPELL, TWO_TO_THREE_SPELL, TWO_TO_ONE_SPELL_ET)

endfunction

//===========================================================================
function InitTrig_STAS_Creation takes nothing returns nothing
    local trigger tg = CreateTrigger(  )
    call TriggerRegisterTimerEventSingle( tg, 0.00 )
    call TriggerAddAction( tg, function sca )
endfunction

JASS:
function STASActions takes nothing returns nothing
    // Get the caster unit
    local unit c = GetSpellAbilityUnit()

    // Get the casted spell
    local integer cs = GetSpellAbilityId()

    // Call the temporary system
    call CastTemporaryAbility(c, cs)

    set c = null
endfunction

function InitTrig_STAS_Cast takes nothing returns nothing
    local trigger tg = CreateTrigger()
    local integer i = 0

    call TriggerAddAction(tg, function STASActions)

    loop
        call TriggerRegisterPlayerUnitEvent(tg, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
        set i = i + 1
    exitwhen i == bj_MAX_PLAYERS
    endloop
endfunction

More Credits:
* Hate, code correction
Contents

Simple Temporal Ability System 0.0.1 (Map)

Simple Temporal Ability System 0.0.2 (Map)

Simple Temporal Ability System 0.0.3 (Map)

Reviews
MyPad
Hmm, neat. Might be useful for some people who are into heavy combos, especially anime-based combos. Perfectly functional, though there are improvements yet to be made. Once most of the things pointed out in the Code Review are addressed, I'll...
Level 14
Joined
Oct 19, 2014
Messages
187
Im still confuse of how the system works, but i have to say this

CAST

why not try to index everything just to prevent create and destroy timer

CONFIGURE

You had the uniqueness idead but much better to infunction those ability id like
JASS:
constant function BASE_SPELL takes nothing returns integer
    return 'A000'
endfunction
Also those saved by handles
 

V

V

Level 7
Joined
Jan 13, 2019
Messages
284
Im still confuse of how the system works, but i have to say this

CAST

why not try to index everything just to prevent create and destroy timer

CONFIGURE

You had the uniqueness idead but much better to infunction those ability id like
JASS:
constant function BASE_SPELL takes nothing returns integer
    return 'A000'
endfunction
Also those saved by handles

Thanks for the comment, i will update the trigger and the post :)
 

V

V

Level 7
Joined
Jan 13, 2019
Messages
284
Im still confuse of how the system works, but i have to say this

CAST

why not try to index everything just to prevent create and destroy timer

CONFIGURE

You had the uniqueness idead but much better to infunction those ability id like
JASS:
constant function BASE_SPELL takes nothing returns integer
    return 'A000'
endfunction
Also those saved by handles

Now it make sense!, see the restructuration of the library :D
 
Level 14
Joined
Oct 19, 2014
Messages
187
hahaha yeah, i learn too fast, is one of my strengths. Thanks :D. i got another post of simple system!
Done testing your system,, i have even test in multiple unit,, and it work so, but one that keeps bugging my mind, or even experts will tell you this,, why not try to avoid creating and destroying timers, instead use one and ingroup those casters as well
 

V

V

Level 7
Joined
Jan 13, 2019
Messages
284
Done testing your system,, i have even test in multiple unit,, and it work so, but one that keeps bugging my mind, or even experts will tell you this,, why not try to avoid creating and destroying timers, instead use one and ingroup those casters as well

i see this on internet: one timer with time 1 and every expiration set a integer to integer - 1 , and this integer is assigned to a hashtable that have the unit id?
 
Level 38
Joined
Feb 27, 2007
Messages
4,951
i see this on internet: one timer with time 1 and every expiration set a integer to integer - 1 , and this integer is assigned to a hashtable that have the unit id?
In general, yes. However when using a single timer your margin of error for any given duration is +-(timerperiod) so timing for a duration of 9s could actually take as long as 9.999... or as short as 8.000..1. I would use a shorter timer period like 0.2, so then instead of storing 9 as the integer to count down you store 9*1/(timerperiod) = 9*5 = 45
 

V

V

Level 7
Joined
Jan 13, 2019
Messages
284
In general, yes. However when using a single timer your margin of error for any given duration is +-(timerperiod) so timing for a duration of 9s could actually take as long as 9.999... or as short as 8.000..1. I would use a shorter timer period like 0.2, so then instead of storing 9 as the integer to count down you store 9*1/(timerperiod) = 9*5 = 45

Thanks for that suggestion, can you check my others system too please?. in my profile page you can get the link :D
 
Level 7
Joined
Feb 9, 2021
Messages
301
Pending call FlushChildHashtable(STASIndexer, GetHandleId(t)) for remove the unit data for the hashtable, this inside the STASIndexer function
I think replacing the old spell with the new spell (new icon appears at the place of the old icon) is better for this type of spells, so they don't take too much space.
 
Hmm, neat. Might be useful for some people who are into heavy combos, especially anime-based combos. Perfectly functional, though there are improvements yet to be made.

  • STAS
    • I think the globals (currently above the library) should be contained within the library itself. On top of that, the global variables used by this system should be marked private.

    • The StringHash function is used far too many times, in the context of the functions CreateTemporaryAbility and CastTemporaryAbility. Instead, you can use a module initializer to pre-compute and store the outputs of the StringHash function as integer keys for your hashtable. Personally, I do not like to use StringHash in the context of JASS.

      JASS:
      library // ...
      
      globals
          // Some variables here
          private integer nextKey = 0
          private integer expKey = 0
          private integer casterKey = 0
      endglobals
      
      private module M
          private static method onInit takes nothing returns nothing
              set nextKey = StringHash("NEXT")
              set expKey = StringHash("EXPTIME")
              set casterKey = StringHash("CASTER")
          endmethod
      endmodule
      private struct S extends array
          implement M
      endstruct
      
      // Replace all instances of StringHash(value) with their respective keys above.

    • In function CastTemporaryAbility(unit cu, integer cs), there is no need to null "cu", since it's properly dereferenced by the end of the call. You only need to do this to local variables declared within the function that deal with dynamic objects, such as units, items, special effect, and so on.

    • The following variables do not need to be stored within the trigger script itself:
      • ONE_SPELL
      • ONE_TO_TWO_SPELL
      • TWO_TO_THREE_SPELL
      • ONE_TO_TWO_SPELL_ET
      • TWO_TO_THREE_SPELL_ET

        Just declare them within the example trigger script STAS Creation.
  • STAS Cast
    • This can be merged with the main STAS library, since all it does is invoke CastTemporaryAbility with an arbitrary spell id (GetSpellAbilityId()). Besides, a failsafe block has already been implemented within CastTemporaryAbility. Ideally, the trigger that will run upon spell effect will have to be created with the use of a module initializer.

Once most of the things pointed out in the Code Review are addressed, I'll re-evaluate the status of this bundle.
 
Top