• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Snippet] Periodic Effect

Level 7
Joined
Apr 5, 2011
Messages
245
Periodic Effect v1.1 (uses Indexer)
W.I.P.

Few words about:
- Allows dynamic register of periodic effects
(This means, you don't have to spend indexes on spells not present in the game while their codes are generated)
- No hashtables
(Less memory used)

Q: Why this is still W.I.P.?
A: This does not have internal overlap yet
(Version 10.0 coming :D)

Main code:
JASS:
//======================
//=== PeriodicEffect ===
//======= v1.100 =======

/*************************************************************************
* Modules
*     module PeriodicEffect
*           (Should be implemented after called methods)
* Local user interface (requires PeriodicEffect module)
*     static method RegisterPeriodicEffect takes nothing returns nothing
*          - Registers thistype periodic effect
*     static method UnitApplyPeriodicEffect takes unit target, unit caster, integer instances, real period, integer data returns nothing
*          - Applies thistype periodic effect to the target unit
*     static method onPeriodicEffect takes nothing returns nothing
*          - Called when thistype periodic effect proces
*     static method onEndPeriodicEffect takes nothing returns nothing
*          - Called when thistype periodic effect ends
* Global user interface
*     function GetPeriodicEffectTarget takes nothing returns unit
*          - Returns target unit of the periodic effect executed
*     function GetPeriodicEffectCaster takes nothing returns unit
*          - Returns caster of the periodic effect executed
*     function GetPeriodicEffectData takes nothing returns integer
*          - Returns data of the periodic effect executed
*     function GetPeriodicEffectTickCount takes nothing returns integer
*          - Returns current tick count of the periodic effect executed
* Local spec interface
*     readonly integer SPEC_PERIODIC_EFFECT_CODE
*          - Stores thistype periodic effect code
* Global spec interface
*     function UnitApplySpecPeriodicEffect takes integer CODE, unit target, unit caster, integer instances, real period, integer data returns nothing
*          - Applies specified periodic effect to the target unit
*************************************************************************/

library PeriodicEffect initializer Init requires Indexer

globals
    private integer Types
    private trigger array EffectTrigger
    private trigger array EndEffectTrigger
    private integer Indexer
    private integer array Code
    private unit array Target
    private unit array Caster
    private integer array Data
    private integer array TickCount
    private timer array TickTimer
    private integer WorkInteger
endglobals
    private function Init takes nothing returns nothing
        set Types = 0
        set Indexer = CreateIndexer()
    endfunction

    private function Proc takes nothing returns nothing
        local timer t = GetExpiredTimer()
        set WorkInteger = 0
        loop
            exitwhen TickTimer[WorkInteger] == t
            set WorkInteger = WorkInteger + 1
        endloop
        call TriggerEvaluate(EffectTrigger[Code[WorkInteger]])
        set TickCount[WorkInteger] = TickCount[WorkInteger] - 1
        if TickCount[WorkInteger] == 0 then
            call TriggerEvaluate(EndEffectTrigger[Code[WorkInteger]])
            call Deindex(Indexer, WorkInteger)
            set Target[WorkInteger] = null
            set Caster[WorkInteger] = null
            call PauseTimer(t)
        endif
        set t = null
    endfunction    

    //-=- Local interface -=-
    module PeriodicEffect
        readonly static integer SPEC_PERIODIC_EFFECT_CODE
        static if thistype.onPeriodicEffect.exists then
            private static method firePeriodicEffect takes nothing returns boolean
                call thistype.onPeriodicEffect()
                return false
            endmethod
        endif
        static if thistype.onEndPeriodicEffect.exists then
            private static method fireEndPeriodicEffect takes nothing returns boolean
                call thistype.onEndPeriodicEffect()
                return false
            endmethod
        endif
        static method RegisterPeriodicEffect takes nothing returns nothing
            static if thistype.onPeriodicEffect.exists then
                set EffectTrigger[Types] = CreateTrigger()
                call TriggerAddCondition(EffectTrigger[Types], Condition(function thistype.firePeriodicEffect))
            endif
            static if thistype.onEndPeriodicEffect.exists then
                set EndEffectTrigger[Types] = CreateTrigger()
                call TriggerAddCondition(EndEffectTrigger[Types], Condition(function thistype.fireEndPeriodicEffect))
            endif
            set thistype.SPEC_PERIODIC_EFFECT_CODE = Types
            set Types = Types + 1
        endmethod
        static method UnitApplyPeriodicEffect takes unit target, unit caster, integer instances, real period, integer data returns nothing
            call UnitApplySpecPeriodicEffect(thistype.SPEC_PERIODIC_EFFECT_CODE, target, caster, instances, period, data)
        endmethod
    endmodule
                       
    //-=- Global interface -=-
    function GetPeriodicEffectTarget takes nothing returns unit
        return Target[WorkInteger]
    endfunction

    function GetPeriodicEffectCaster takes nothing returns unit
        return Caster[WorkInteger]
    endfunction

    function GetPeriodicEffectData takes nothing returns integer
        return Data[WorkInteger]
    endfunction

    function GetPeriodicEffectTickCount takes nothing returns integer
        return TickCount[WorkInteger]
    endfunction

    function UnitApplySpecPeriodicEffect takes integer CODE, unit target, unit caster, integer instances, real period, integer data returns nothing
        set WorkInteger = Index(Indexer)
        set Code[WorkInteger] = CODE
        set Target[WorkInteger] = target
        set Caster[WorkInteger] = caster
        set TickCount[WorkInteger] = instances
        set Data[WorkInteger] = data
        if TickTimer[WorkInteger] == null then
            set TickTimer[WorkInteger] = CreateTimer()
        endif
        call TimerStart(TickTimer[WorkInteger], period, true, function Proc)
    endfunction
endlibrary

Example of usage:
JASS:
//========================
//=== SpellColdEmbrace ===
//======== v1.100 ========

library SpellColdEmbrace requires Assist, PeriodicEffect
//=== Settings ===
globals
    private constant integer ABILCODE = 'X038'
    private constant integer ARMOR_ABILITY = 'X039'
//----------------
    private constant integer INSTANCES = 4
    private constant real PERIOD = 1
    private constant boolean STOP_ANIMATION = true
    private constant boolean CHANGE_HEIGHT = true
endglobals
static if CHANGE_HEIGHT then
    globals
        private constant integer HOVER_ABILITY = 'Amrf'
//----------------
        private constant real HEIGHT_CHANGE_RATE = 200
    endglobals
endif
    struct SpellColdEmbrace extends array
        private static real array HEAL_ABSOLUTE
        private static real array HEAL_RELATIVE
        private static method onInit takes nothing returns nothing
            set HEAL_ABSOLUTE[1] = 20
            set HEAL_ABSOLUTE[2] = 20
            set HEAL_ABSOLUTE[3] = 20
            set HEAL_ABSOLUTE[4] = 20
            set HEAL_RELATIVE[1] = .03
            set HEAL_RELATIVE[2] = .04
            set HEAL_RELATIVE[3] = .05
            set HEAL_RELATIVE[4] = .06
//================
            call RegisterPeriodicEffect()
        endmethod
       
        static method onPeriodicEffect takes nothing returns nothing
            set Assist.workUnit = GetPeriodicEffectTarget()
            set Assist.workInteger = GetPeriodicEffectData()
            call SetWidgetLife(Assist.workUnit, GetWidgetLife(Assist.workUnit) + (HEAL_ABSOLUTE[Assist.workInteger] + GetUnitState(Assist.workUnit, UNIT_STATE_MAX_LIFE) * HEAL_RELATIVE[Assist.workInteger]) * PERIOD)
        endmethod
        
        static method onEndPeriodicEffect takes nothing returns nothing
            set Assist.workUnit = GetPeriodicEffectTarget()
            call UnitRemoveAbility(Assist.workUnit, ARMOR_ABILITY)
            static if STOP_ANIMATION then
                call SetUnitTimeScale(Assist.workUnit, 1)
            endif
            static if CHANGE_HEIGHT then
                set Assist.workReal = GetUnitDefaultFlyHeight(Assist.workUnit)
                if Assist.workReal > 0 then
                    call SetUnitFlyHeight(Assist.workUnit, Assist.workReal, HEIGHT_CHANGE_RATE)
                endif
            endif
        endmethod

        implement PeriodicEffect
        //----------------

        static method onSpellEffect takes nothing returns nothing
            if GetSpellAbilityId() == ABILCODE then
                set Assist.workUnit = GetSpellTargetUnit()
                call UnitApplyPeriodicEffect(Assist.workUnit, null, INSTANCES, PERIOD, GetUnitAbilityLevel(GetTriggerUnit(), ABILCODE))
                call UnitAddAbility(Assist.workUnit, ARMOR_ABILITY)
                static if STOP_ANIMATION then
                    call SetUnitTimeScale(Assist.workUnit, 0)
                endif
                static if CHANGE_HEIGHT then
                    if GetUnitDefaultFlyHeight(Assist.workUnit) > 0 then
                        call UnitAddAbility(Assist.workUnit, HOVER_ABILITY)
                        call UnitRemoveAbility(Assist.workUnit, HOVER_ABILITY)
                        call SetUnitFlyHeight(Assist.workUnit, 0, HEIGHT_CHANGE_RATE)
                    endif
                endif
            endif
        endmethod
    endstruct
endlibrary
 
Last edited:
Level 7
Joined
Apr 5, 2011
Messages
245
call RegisterPeriodicEffect()
function onPeriodicEffectDuration blabla
function onEndPeriodicEffectDuration blabla
init PeriodicEffect

That's all you need to use
("Harder" may mean max of 3 lines more, I guess there is no possible methods to do it with 0 lines used)
 
The only thing that this does for you is duration, which is very easy to do when using a timer system...

so tell me why a user should use this over a timer system when the only thing it gives them is something that they can code in 3 lines.

Keep in mind that this doesn't come close to matching the performance of a timer system and requires the user to learn a new API

edit
And your overlap thing is sily >.>. If you wanna combine effects, the best method is to output the summation of those effects every x period of time (for texttags or w/e). Apply them all individually, but add their values to some var that is read on a separate timer. Much simpler.
 
Level 7
Joined
Apr 5, 2011
Messages
245
The only thing that this does for you is duration, which is very easy to do when using a timer system...

so tell me why a user should use this over a timer system when the only thing it gives them is something that they can code in 3 lines.

Keep in mind that this doesn't come close to matching the performance of a timer system and requires the user to learn a new API

edit
And your overlap thing is sily >.>. If you wanna combine effects, the best method is to output the summation of those effects every x period of time (for texttags or w/e). Apply them all individually, but add their values to some var that is read on a separate timer. Much simpler.
You should know, there is no any reason to your TimerTools as well, until someone gets in fetish to your code
(Not trying to be rude, just a fact)
And there is no overlap yet, where did you find it
(Edit: Sorry, there is an overlap ofc, I meant override (cancels previous buff). This caused misunderstanding)

Anyway, gy request ^_^
I will post rework soon, too much difference with this one to update
(No sense to go further with this)

Edit:
"Summation every x period of time" is not acceptable as a general solution. There should be some data change upon stack. (Another mechanism that does not ruin independence possibility)
(That's why you better use tools)

Edit:
This is exactly what this would do when finished:
When applying buffs to a unit, the buff may already exist. Handle such collisions (overwrite if lower, stack, etc) and provide a list of buffs of type for a given unit, allowing external resources to iterate over these buffs in whatever way they'd like (for periodic, removal, checks, etc).
 
Last edited:
Level 7
Joined
Apr 5, 2011
Messages
245
Based on this extremely simple code:
JASS:
//-=-=-=-==-=-=--=-=-=-=-
//-=-=-=-] Buffy [-=-=-=-
//-=-=-=- v0.900 =-=-=-=-

/*******************************************************
* function InitBuffy takes integer n returns nothing
*    - Inits buff field for n buffs
*     (Call it when game starts)
* function UnitHasBuff takes unit u, integer CODE returns boolean
*    - Returns true if unit has specified custom buff
* function UnitAddBuff takes unit u, integer CODE returns nothing
*    - Adds custom buff to unit
* function UnitOverlapBuff takes unit u, integer CODE returns boolean
*    - Adds custom buff to unit and returns true if overlap happened
* function UnitRemoveBuff takes unit u, integer CODE returns nothing
*    - Removes custom buff from unit
*******************************************************/

library Buffy
globals
    private integer BUFFS
    private boolean array B
endglobals
    function InitBuffy takes integer n returns nothing
        set BUFFS = n
    endfunction
    
    function UnitHasBuff takes unit u, integer CODE returns boolean
        return B[GetUnitUserData(u) * BUFFS + CODE]
    endfunction

    function UnitAddBuff takes unit u, integer CODE returns nothing
        set B[GetUnitUserData(u) * BUFFS + CODE] = true
    endfunction

    function UnitOverlapBuff takes unit u, integer CODE returns boolean
        if B[GetUnitUserData(u) * BUFFS + CODE] then
            return true
        else
            set B[GetUnitUserData(u) * BUFFS + CODE] = true
            return false
        endif
    endfunction

    function UnitRemoveBuff takes unit u, integer CODE returns nothing
        set B[GetUnitUserData(u) * BUFFS + CODE] = false
    endfunction
endlibrary
No timers as you see
PS Am I right, all this 1-line shit always inlined?
 
If you use an argument only one time, then yes.

edit
Don't do the overlap thing. Either buffs stack or they don't. If they don't, the highest level/newest one takes precedence. Of course, resources need to know when buffs are destroyed too, but should do that without resorting to triggers.

Because a buff is only destroyed by a system in the case of the system applying a higher level buff when the buff already exists on the unit, then it stands to reason that the caller is the handler of that specific buff. This means that the caller itself can handle destruction, so long as you provide the instance of the destroyed buff to it =).
 
Level 7
Joined
Apr 5, 2011
Messages
245
No no, overlap needed for periodic effects
Here is the piece of code using this:
JASS:
        private static method onInit takes nothing returns nothing
            set HEAL_ABSOLUTE[1] = 20
            set HEAL_ABSOLUTE[2] = 20
            set HEAL_ABSOLUTE[3] = 20
            set HEAL_ABSOLUTE[4] = 20
            set HEAL_RELATIVE[1] = .03
            set HEAL_RELATIVE[2] = .04
            set HEAL_RELATIVE[3] = .05
            set HEAL_RELATIVE[4] = .06
//================
            call RegisterBuff(BUFFTYPE_NONSTACKABLE, INSTANCES)
            implement BuffConfig
                static method onTimedEffect takes nothing returns nothing
                bla bla bla
                endmethod
            implement EndBuffConfig
        endmethod
If BuffConfig includes onTimedEffect event, UnitApplyBuff function that takes BUFFCODE (storing onTimedEffect as trigger pseudo-action) creates periodic timer calling onTimedEffect method. Timer can be set to not periodic also, just to detect when buff ends, but I am not focusing on it now (working on poisons).
When buff gets overrided, previous timer is set to 1 tick, its last tick and first tick of next instance stack, and then previous gets removed. (This will be optional as well)
In case of generic buffs nothing like this happen.
 
Level 7
Joined
Apr 5, 2011
Messages
245
I wanna global functions like Blizzard ones :/ (Global system, structed spells)
If I do so ^^ I will have to implement indexing / recycling / blabla for buffs and custom timers both (and for something else that I have not made yet), now I use 1 indexer for both them (1 function < 2 static methods) (Indexer array potential is not used even on 20% yet)
This works similar to structs, but easier to code for me (And will be probably easier to use (As you see, only 3 lines needed to implement special (stackable/nonstackable) timed effect))
 
Top