1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The poll for Hive's 12th Concept Art Contest is up! Go cast your vote for your favourite genie!
    Dismiss Notice
  4. Travel to distant realms and encounter scenes unknown to the common folk. The Greatest of Adventures is upon us with the 8th Cinematic Contest. Join in on a fun ride.
    Dismiss Notice
  5. The 18th Icon Contest is ON! Choose any ingame unit and give him/her Hero abilities. Good luck to all.
    Dismiss Notice
  6. Contestants are to create a scene set in the Stone Age. Come and see what you can come up with. We wish you the best of luck!
    Dismiss Notice
  7. Colour outside the lines! Techtree Contest #13 is a go. The contest is optionally paired.
    Dismiss Notice
  8. Greetings cerebrates, our Swarm needs new spawners that will have numerous children. Join the HIVE's 31st Modeling Contest - Spawners and Spawned! The contest is optionally paired.
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[Snippet] Periodic Effect

Discussion in 'Graveyard' started by Krogoth, Aug 5, 2013.

  1. Krogoth

    Krogoth

    Joined:
    Apr 5, 2011
    Messages:
    247
    Resources:
    0
    Resources:
    0
    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:
    Code (vJASS):
    //======================
    //=== 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:
    Code (vJASS):
    //========================
    //=== 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: Aug 6, 2013
  2. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,427
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
  3. Krogoth

    Krogoth

    Joined:
    Apr 5, 2011
    Messages:
    247
    Resources:
    0
    Resources:
    0
    Very interesting, thanks. I will try this.
     
  4. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,860
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
  5. Krogoth

    Krogoth

    Joined:
    Apr 5, 2011
    Messages:
    247
    Resources:
    0
    Resources:
    0
    I thought they work w/o "unnecessary code" :/
     
  6. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    This is a glorified timer system that's harder to use than just using a timer system.
     
  7. Krogoth

    Krogoth

    Joined:
    Apr 5, 2011
    Messages:
    247
    Resources:
    0
    Resources:
    0
    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)
     
  8. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    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.
     
  9. Krogoth

    Krogoth

    Joined:
    Apr 5, 2011
    Messages:
    247
    Resources:
    0
    Resources:
    0
    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:
     
    Last edited: Aug 7, 2013
  10. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    That resource doesn't have any timers in it ;)
     
  11. Krogoth

    Krogoth

    Joined:
    Apr 5, 2011
    Messages:
    247
    Resources:
    0
    Resources:
    0
    Based on this extremely simple code:
    Code (vJASS):
    //-=-=-=-==-=-=--=-=-=-=-
    //-=-=-=-] 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?
     
  12. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    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 =).
     
  13. Krogoth

    Krogoth

    Joined:
    Apr 5, 2011
    Messages:
    247
    Resources:
    0
    Resources:
    0
    No no, overlap needed for periodic effects
    Here is the piece of code using this:
    Code (vJASS):
            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.
     
  14. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    >.>

    Buff buff = Buff.create(target, abilityId, level)
     
  15. Krogoth

    Krogoth

    Joined:
    Apr 5, 2011
    Messages:
    247
    Resources:
    0
    Resources:
    0
    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))
     
  16. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,860
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    You are not working for Blizzard :V
     
  17. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,659
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    I'm sorry but this has to be completely overhauled or graveyarded.

    1. The implementation of this is pretty bad.
    2. You're using dynamic triggers.
    3. Leaking timers as well.
    4. API is awkward.
    5. Requires a weird indexer
     
  18. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,659
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    ^^

    Why does this force user to apply the effect on a unit? Why not a point a well.

    Anyway, Graveyarding.