1. Join Texturing Contest #30 now in a legendary battle of mythological creatures!
    Dismiss Notice
  2. The Aftermath has been revealed for the 19th Terraining Contest! Be sure to check out the Results and see what came out of it.
    Dismiss Notice
  3. Melee Mapping Contest #3 - Results are out! Congratulate the winners and check plenty of new 4v4 melee maps designed for this competition!
    Dismiss Notice
  4. The winners of our cinematic soundtrack competition have been decided! Step by the Music Contest #11 - Results to check the entries and congratulate the winners!
    Dismiss Notice
  5. Check out the Staff job openings thread.
    Dismiss Notice

[System] Retro (time travel)

Discussion in 'JASS Resources' started by Bribe, May 24, 2010.

  1. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,759
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    image
    [​IMG]


    The coolest system on the market, Retro logs a unit's status for any amount of time, then restores them to the unit instant by instant. Stats include: x, y, z coordinates, optional facing, optional HP and optional MP. If you want more, ask, this system is very flexible and can easily handle new features..

    Video is old and will be updated soon.

    Video
    http://www.youtube.com/watch?v=_cqmRNwqj50


    To view the AIDS version (if you'd prefer it to AutoIndex), click here.

    Manual

    Code (vJASS):

    //    _  _ ___ _    _   ___ ___
    //   /__/ /  / |   / _  /  /  /
    //  /  / /__/  |/|/    /  /__/
    //                            Using Bribe's Retro Library

    How to use retro to make something of your own:

    1.  You'll need to make a struct.
       
        struct new
        endstruct
       
    2.  You need to turn this into a Retro struct, and the way to do that is to
        import a module called RETRO.  The struct needs to extend array because
        the module replaces the standard allocate/deallocate methods.
       
        struct new extends array
            implement RETRO
        endstruct
       
    3.  What now?
        You'
    ve just copied to your struct about 250 lines of script, so your
        struct has many new features that it has access to.
       
        Event-methods:
        ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        method onStart  takes nothing returns nothing
        method onFinish takes nothing returns nothing
        method onLoop   takes nothing returns nothing
        method onLoopForward takes nothing returns nothing
        method onLoopReverse takes nothing returns nothing
       
        How to use them:
        ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        By having a method by any of those names inside your struct.
       
        struct new extends array
            method onFinish takes nothing returns nothing
                call BJDebugMsg("Something has happened!")
            endmethod
           
            method onStart takes nothing returns nothing
                call BJDebugMsg("Something is coming...")
            endmethod
           
            implement RETRO
        endstruct
       
        Careful Syntax:
        ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        An event-method should always be placed <above> the implentation of the
        module.  Nothing like this:
       
        struct new extends array
            implement RETRO
           
            method onLoop takes nothing returns nothing
                call BJDebugMsg("I'm doing nothing...")
            endmethod
        endstruct
       
    4.  The things that trigger the methods are pretty much just commands found
        in the module itself. I'll tell you what those commands are:
       
        method initiate takes real duration returns nothing
        /
        This method is your point-and-click time-rewinding mechanism. Call this
        method and the unit will zap back in time for the duration you input...
        /
        call initiate(10.0) // Goes back in time for 10 seconds.
       
        Events called: onFinish
       
        method capture takes real duration returns nothing
        /
        This method is great for spell-casting.  In the simplest definition, it
        "bonds" a unit to its immediate position for the given time, then drags
        the unit back over its tracks until it reaches that location once again.
        Rewind-duration is 25 percent of the input duration.
        /
        call capture(10.0) -> Location memorized for 10 seconds, then goes back
                              through time over its steps for 2.5 seconds.
       
        Events called: onStart, onFinish
       
        method allocate takes unit whichUnit, boolean wantLoops, boolean wantExtraLoops returns thistype
        /
        To get this method, I'
    ve troubled you with needing to make your struct
        extend an array.  Here's what you get out of it: a regular unit is
        transformed into a working subject capable of travelling through time.
        /
        local thistype this = allocate(GetTriggerUnit(), true, true)
       
        What do the boolean variables do in this method?  They grant you instant
        access to three event-methods: onLoop, onLoopForward and onLoopReverse.
       
    5.  Now that you know what triggers the event methods, what do those events
        do?
       
        method onStart -> if you use .capture, onStart triggers after the 10
        second duration.
       
        method onFinish -> if you use .capture, onFinish triggers after the
        2.5 second duration.  If you use .initiate, onFinish triggers after
        the 10 second duration. Think of this like an onDestroy method, as
        it is automatically called by the deallocate method.
       
        method onLoop -> gets called every RETRO_TIMEOUT (I have it at a 0.03125
        second interval). Turned on or off initially by the first boolean param-
        eter in the allocate method. Can be toggled by setting the boolean value
        .noLoops = false (on) or .noLoops = true (off).
       
        method onLoopForward -> gets called for ONE struct per RETRO_TIMEOUT.
        Is turned on or off initially by the second boolean parameter in the
        allocate method.
       
        method onLoopReverse -> same as above, but only one of these two will be
        called per RETRO_TIMEOUT. Both are toggled on/off by the boolean value
        .noExtras = false (on) or .noExtras = true (off).
       
    6.  Oh yeah. Methods that come packaged in the struct (capture, initiate, etc.)
        should only be called by methods that are below the module implentation
        (the opposite of what I told you to do with each event-method). If you are
        in a pickle and absolutely must call the method from the wrong precedence,
        you can use (I discourage this approach) this.deallocate.evaluate(this).
       
    7.  Other variables at your disposal:
       
        .subject -> the unit you work with.
        .x, .y, .z -> coordinates and flyheight of .subject, respectively.
        .ang -> the unit'
    s facing at a given moment.
        .hp, .mp -> the unit's hit points/mana at a given moment.
       
    8.  Shortcuts:
       
        To get the unit'
    s <this> index, you can use:
       
        local thistype this = retro.create(GetTriggerUnit())
       
        else, if you want to inline it and not worry about whether or not the unit
        is actually qualified to be a time-traveller:
       
        local thistype this = GetUnitId(GetTriggerUnit())
       
        You can also just call the allocate method, but that will destroy what you
        had and start it brand new, as a double-allocate first calls deallocate...
       
        You don't need to destroy one of these kinds of structs, because the index
        is taken directly from AutoIndex (or AIDS, if you'
    re using that version).
       
    9.  Gotcha's:
       
        retro.create and thistype.allocate will often return 0 (usually if the unit
        is invalid).  If you want to be extra careful, you should check to make
        sure those methods didn'
    t give you 0 before doing anything with the struct.
       
    10. I'm pretty sure this does not answer all of your questions, or maybe it
        didn'
    t even answer any of them. Just ask me for clarification on something.
     



    Retro Library:

    Code (vJASS):

    library Retro requires AutoIndex, AutoEvents
    //******************************************
    //    ___  ___  ___  ___  ___
    //   /__/ /__   /   /__/ /  /
    //  /  | /__   /   /  \ /__/
    //                          Created by Bribe
    //******************************************
    //  Original AutoIndex Version
    //
    /*  Requirements
     *  ¯¯¯¯¯¯¯¯¯¯¯¯
     *  AutoIndex  by grim001 ~ [url]http://www.wc3c.net/showthread.php?t=105643[/url]
     *  AutoEvents by grim001 ~ [url]http://www.wc3c.net/showthread.php?t=106250[/url]
     *  
     *  Description
     *  ¯¯¯¯¯¯¯¯¯¯¯
     *  Retro provides data retrieval and callback operators for use in simulating a time-travel effect for units.
     *  While there is no promise that this is an easy system to implement, I will try to label everything as easy
     *  to read and as simple as I can.  If you have any questions, just ask.
     *
     *  To Know
     *  ¯¯¯¯¯¯¯
     *  On its own, the retro struct indexes every unit that passes through AutoEvents. It records a unit's data
     *  during each lapse of RETRO_TIMEOUT and saves it all in a three-dimensional hashtable. Simple array values
     *  record the current-moment's data aspects: x, y, z and, if you desire, facing angle, HP and MP.
     *  
     *  To allocate your own struct and have access to Retro's excellent callback features, the struct(s) you use
     *  must extend array and implement the module named RETRO. This module uses allocate and deallocate as method
     *  names to regulate those events.  You are welcome to call allocate/deallocate freely as they have built-in
     *  foolproofs. <deallocate> is also automatically called when a unit is loaded into a transport or dies.
     *
     *  You can only instanciate for a given unit once per struct-type.  You can create numerous structs which all
     *  implement the RETRO module.
     *
     *  Events
     *  ¯¯¯¯¯¯
        method onLoop           -> called during each iteration of RETRO_TIMEOUT.
        method onLoopForward    -> called for only one struct per iteration, if it's in forward-mode.
        method onLoopReverse    -> called for only one struct per iteration, if it's in reverse-mode.
     
        method onStart  ->  if you specify a "capture" real value, this will be called after that time has expired.
        method onFinish ->  is called by the deallocate method. deallocate is called automatically if you specify a
                            real value for "capture" or "initiate". It's basically your "onDestroy" method.
     *
     *  Syntax Example
     *  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        struct timeStruct extends array
       
            method onFinish takes nothing returns nothing
                call BJDebugMsg(GetUnitName(.subject)+"'s stats:\n")
                call BJDebugMsg("X-coordinate is "+R2S(.x))
                call BJDebugMsg("Y-coordinate is "+R2S(.y))
                call BJDebugMsg("Z-coordinate is "+R2S(.z))
                call BJDebugMsg("facing is "+R2S(.ang))
                call BJDebugMsg("hit points are "+R2S(.hp))
                call BJDebugMsg("mana points are "+R2S(.mp)+"\n")
            endmethod
       
            implement RETRO
           
            static method create takes unit newSubject returns thistype
                local thistype this = thistype.allocate(newSubject, true, false)
                return this
            endmethod
           
            static method onInit takes nothing returns nothing
                local thistype this = thistype.create(newSubject)
                call this.initiate(0.03)
            endmethod
           
        endstruct
     *
     *
     */

       
        globals
            // Values are in seconds;
            constant    real        RETRO_TIMEOUT       = 0.03125   /* This value is a tribute to Jesus4Lyf's helpful Timer32 */
            constant    real        RETRO_MEMORY_MAX    = 20.0      /* The hashtable will deallocate data past this threshold */
           
            // Static-if controllers;
            private     constant    boolean     BREAKS_CHANNEL      = true      /* <true> uses SetUnitPosition for smoother motion */
            private     constant    boolean     RECORD_FACING       = true      /* If you're an efficiency-freak and don't care, set to false */
            private     constant    boolean     RECORD_HP           = true      /* Can save the health-points of units */
            private     constant    boolean     RECORD_MP           = false     /* Can save the mana-points of units */
        endglobals
       
        private module Validate
            static method create takes unit whichSubject returns retro
           
                if GetUnitDefaultMoveSpeed(whichSubject) == 0.00 or GetUnitAbilityLevel(whichSubject, 'Aloc') > 0 or IsUnitType(whichSubject, UNIT_TYPE_STRUCTURE) then
                    return 0
                else
                    return GetUnitId(whichSubject)
                endif
               
            endmethod   // Anything that returns 0 will under no condition be added to the system.
        endmodule
       
        globals
            private     hashtable   retro_memory    = InitHashtable()  /* Serves as a three-dimensional array */
            private     trigger     retro_launch    = CreateTrigger()  /* So this system only needs one timer */
            private     trigger     retro_expire    = CreateTrigger()  /* RETRO uses 5 fewer handles this way */
            constant    integer     RETRO_ERASER    = R2I(RETRO_MEMORY_MAX/RETRO_TIMEOUT)
            private     keyword     RetroInitializer
        endglobals
       
        struct retro extends array
           
            private retro prev
            private retro next
           
            readonly unit subject
            readonly integer save
           
            real x
            real y
            real z
           
            static if RECORD_FACING then    // Static ifs for maximum efficiency.
                real ang
            endif
            static if RECORD_HP then
                real hp
            endif
            static if RECORD_MP then
                real mp
            endif
           
            implement Validate
           
            method destroy takes nothing returns nothing
                debug call BJDebugMsg("Error: You cannot manually destroy a retro struct.")
            endmethod
           
            private static integer tick = 0
           
            private static method periodic takes nothing returns nothing
                local retro this = retro(0).next
                set retro.tick = retro.tick + 1
                if (retro.tick == 4) then
                    set retro.tick = 0
                    loop
                        exitwhen this == 0
                        set .save = .save + 1
                       
                        set .x = GetUnitX(.subject)
                        set .y = GetUnitY(.subject)
                        set .z = GetUnitFlyHeight(.subject)
                        call SaveReal(retro_memory, this * 6 + 0, .save, .x)
                        call SaveReal(retro_memory, this * 6 + 1, .save, .y)
                        call SaveReal(retro_memory, this * 6 + 2, .save, .z)
                       
                        static if RECORD_FACING then
                            set .ang = GetUnitFacing(.subject)
                            call SaveReal(retro_memory, this * 6 + 3, .save, .ang)
                        endif
                        static if RECORD_HP then
                            set .hp = GetWidgetLife(.subject)
                            call SaveReal(retro_memory, this * 6 + 4, .save, .hp)
                        endif
                        static if RECORD_MP then
                            set .mp = GetUnitState(.subject, UNIT_STATE_MANA)
                            call SaveReal(retro_memory, this * 6 + 5, .save, .mp)
                        endif
                       
                        if (.save >= RETRO_ERASER) then
                            call RemoveSavedReal(retro_memory, this * 6 + 0, .save - RETRO_ERASER)
                            call RemoveSavedReal(retro_memory, this * 6 + 1, .save - RETRO_ERASER)
                            call RemoveSavedReal(retro_memory, this * 6 + 2, .save - RETRO_ERASER)
                            static if RECORD_FACING then
                                call RemoveSavedReal(retro_memory, this * 6 + 3, .save - RETRO_ERASER)
                            endif
                            static if RECORD_HP then
                                call RemoveSavedReal(retro_memory, this * 6 + 4, .save - RETRO_ERASER)
                            endif
                            static if RECORD_MP then
                                call RemoveSavedReal(retro_memory, this * 6 + 5, .save - RETRO_ERASER)
                            endif
                        endif
                        set this = .next
                    endloop
                endif
               
                // Fire things;
                call TriggerEvaluate(retro_launch)
            endmethod
           
            private static method onInit takes nothing returns nothing
                call TimerStart(CreateTimer(), RETRO_TIMEOUT, true, function retro.periodic)
            endmethod
           
            static retro removing
           
            private static method remove takes unit exitSubject returns nothing
                local retro this = retro.create(exitSubject)
                if .subject == exitSubject then
               
                    set .prev.next = .next
                    set .next.prev = .prev
                   
                    call FlushChildHashtable(retro_memory, this * 6 + 0)
                    call FlushChildHashtable(retro_memory, this * 6 + 1)
                    call FlushChildHashtable(retro_memory, this * 6 + 2)
                    static if RECORD_FACING then
                        call FlushChildHashtable(retro_memory, this * 6 + 3)
                    endif
                    static if RECORD_HP then
                        call FlushChildHashtable(retro_memory, this * 6 + 4)
                    endif
                    static if RECORD_MP then
                        call FlushChildHashtable(retro_memory, this * 6 + 5)
                    endif
                   
                    // Fire things;
                    set retro.removing = this
                    call TriggerEvaluate(retro_expire)
                   
                    set .subject = null
                endif
            endmethod
           
            private static method add takes unit newSubject returns nothing
                local retro this = retro.create(newSubject)
                if this != 0 then
                    set .subject = newSubject
                    set .save = 0
                    set retro(0).next.prev = this
                    set this.next = retro(0).next
                    set retro(0).next = this
                    set this.prev = retro(0)
                endif
            endmethod
           
            implement RetroInitializer
             
        endstruct
       
        private module RetroInitializer
            private static method onInit takes nothing returns nothing
                call OnUnitIndexed(retro.add)
                call OnUnitDeindexed(retro.remove)
                call OnUnitLoad(retro.remove)
                call OnUnitUnload(retro.add)
                call OnUnitDeath(retro.remove)
                call OnUnitReincarnationFinish(retro.add)
            endmethod
        endmodule
       
        native UnitAlive takes unit id returns boolean
       
        module RETRO    /* May only be implemented in <extends array> structs */
           
            boolean noLoops     /* To toggle a method on/off that gets called <every single> loop */
            boolean noExtras    /* To toggle the extra methods on/off (called for one instance per loop) */
           
            private thistype prev
            private thistype next
         
            private boolean active
            private boolean reverse
            private boolean captured
           
            private real timeLeft
            private real timePend
            private integer iSave
           
            static if RECORD_FACING then
                method operator ang takes nothing returns real  // Delegate ang from retro
                    return retro(this).ang
                endmethod
                method operator ang= takes real r returns nothing
                    set retro(this).ang = r
                endmethod
            endif
            static if RECORD_HP then
           
                boolean restoreGoodHP
                boolean restoreBadHP
                boolean restoreAnyHP
               
                method operator hp takes nothing returns real   // Delegate hp from retro
                    return retro(this).hp
                endmethod
                method operator hp= takes real r returns nothing
                    set retro(this).hp = r
                endmethod
               
            endif
            static if RECORD_MP then
           
                boolean restoreGoodMP
                boolean restoreBadMP
                boolean restoreAnyMP
               
                method operator mp takes nothing returns real   // Delegate mp from retro
                    return retro(this).mp
                endmethod
                method operator mp= takes real r returns nothing
                    set retro(this).mp = r
                endmethod
               
            endif
           
            method operator x takes nothing returns real        // Delegate x
                return retro(this).x
            endmethod
            method operator x= takes real r returns nothing
                set retro(this).x = r
            endmethod
               
            method operator y takes nothing returns real        // Delegate y
                return retro(this).y
            endmethod
            method operator y= takes real r returns nothing
                set retro(this).y = r
            endmethod
               
            method operator z takes nothing returns real        // Delegate z
                return retro(this).z
            endmethod
            method operator z= takes real r returns nothing
                set retro(this).z = r
            endmethod
               
            method operator subject takes nothing returns unit  // Readonly subject
                return retro(this).subject
            endmethod
           
            method deallocate takes nothing returns nothing
                if .active then
                    set .active = false
                    set .reverse = false
                    set .captured = false
                    set .prev.next = .next
                    set .next.prev = .prev
                    static if BREAKS_CHANNEL then
                        call SetUnitPathing(.subject, true)
                    endif
                    static if thistype.onFinish.exists then
                        call .onFinish()
                    endif
                endif
            endmethod
           
            method initiate takes real duration returns nothing
                set .iSave = retro(this).save
                set .timeLeft = .timeLeft + duration
                set .reverse = true
                static if BREAKS_CHANNEL then
                    call SetUnitPathing(.subject, false)
                endif
            endmethod   /* <duration> is the time the unit will be in reverse */
           
            method capture takes real duration returns nothing
                set .timeLeft = .timeLeft + duration
                set .captured = true
                if .reverse then
                    set .reverse = false
                    static if BREAKS_CHANNEL then
                        call SetUnitPathing(.subject, true)
                    endif
                endif
            endmethod   /* <duration> is the time the unit will be in forward. Reverse is 1/4 of that */
           
            method collapse takes nothing returns nothing
                if not .reverse and .captured then
                    set .timeLeft = (.timePend / 4)
                    set .iSave = retro(this).save
                    set .timePend = 0.00
                    set .captured = false
                    set .reverse = true
                    static if thistype.onStart.exists then
                        call .onStart()
                    endif
                    static if BREAKS_CHANNEL then
                        call SetUnitPathing(.subject, false)
                    endif
                endif
            endmethod
           
            private static method handler takes nothing returns boolean
                local thistype array choices
                local thistype this = thistype(0).next
                local thistype rand = 0
                local real r
                loop
                    exitwhen (this == 0)
                   
                    if .reverse then
                       
                        set .x = LoadReal(retro_memory, this * 6 + 0, .iSave)
                        set .y = LoadReal(retro_memory, this * 6 + 1, .iSave)
                        set .z = LoadReal(retro_memory, this * 6 + 2, .iSave)
                       
                        static if BREAKS_CHANNEL then
                            call SetUnitPosition(.subject, .x, .y)
                        else
                            call SetUnitX(.subject, .x)
                            call SetUnitY(.subject, .y)
                        endif
                        call SetUnitFlyHeight(.subject, .z, 0.00)
                       
                        static if RECORD_FACING then
                            set .ang = LoadReal(retro_memory, this * 6 + 3, .iSave)
                            call SetUnitFacing(.subject, .ang)
                        endif
                        static if RECORD_HP then  
                            set r = GetWidgetLife(.subject)
                            set .hp = LoadReal(retro_memory, this * 6 + 4, .iSave)
                            if .restoreAnyHP or (.restoreGoodHP and r < .hp - 1) or (.restoreBadHP and r > .hp + 1) then
                                call SetWidgetLife(.subject, .hp)
                            endif
                        endif
                        static if RECORD_MP then
                            set r = GetUnitState(.subject, UNIT_STATE_MANA)
                            set .mp = LoadReal(retro_memory, this * 6 + 5, .iSave)
                            if .restoreAnyMP or (.restoreGoodMP and r < .mp + 1) or (.restoreBadMP and r > .mp + 1) then
                                call SetUnitState(.subject, UNIT_STATE_MANA, .mp)
                            endif
                        endif
                       
                        set .iSave = .iSave - 1
                        set .timeLeft = .timeLeft - RETRO_TIMEOUT
                        if (.timeLeft <= 0.00 or .iSave <= 0) then
                            call .deallocate()
                        endif
                       
                    elseif .captured then
                        set .timePend = .timePend + RETRO_TIMEOUT
                        if (.timePend >= .timeLeft) then
                            call .collapse()
                        endif
                    endif
                   
                    static if thistype.onLoop.exists then
                        if not .noLoops then
                            call .onLoop()
                        endif
                    endif
                    if not .noExtras then
                        set rand:choices = this
                        set rand = rand + 1
                    endif
                    set this = .next
                endloop
               
                if (rand > 0) then
                    set rand = choices[GetRandomInt(0, rand - 1)]
                    if rand.reverse then
                        static if thistype.onLoopReverse.exists then
                            call rand.onLoopReverse()
                        endif
                    else
                        static if thistype.onLoopForward.exists then
                            call rand.onLoopForward()
                        endif
                    endif
                endif
               
                return false
            endmethod
           
            static method allocate takes unit theSubject, boolean wantLoop, boolean wantLoopEx returns thistype
                local thistype this = retro.create(theSubject)
               
                if this == 0 or retro(this).save == 0 then
                    return 0
                elseif .active then
                    call .deallocate()
                endif
               
                set .active   = true
                set .noLoops  = not wantLoop
                set .noExtras = not wantLoopEx
                set .timeLeft = 0.00
                set .timePend = 0.00
               
                static if RECORD_HP then
                    set .restoreGoodHP = false
                    set .restoreBadHP = false
                    set .restoreAnyHP = false
                endif
                static if RECORD_MP then
                    set .restoreGoodMP = false
                    set .restoreBadMP = false
                    set .restoreAnyMP = false
                endif
               
                set thistype(0).next.prev = this
                set this.next = thistype(0).next
                set thistype(0).next = this
                set this.prev = thistype(0)
               
                return this
            endmethod
           
            private static method onRemoval takes nothing returns boolean
                if thistype(retro.removing).active then
                    call thistype(retro.removing).deallocate()
                endif
                return false
            endmethod
           
            private static method onInit takes nothing returns nothing
                call TriggerAddCondition(retro_launch, Condition(function thistype.handler))    // Enables the handler event
                call TriggerAddCondition(retro_expire, Condition(function thistype.onRemoval))  // Enables the onRemoval event
            endmethod
           
        endmodule
       
        module RetroTimerModule
            private static method onInit takes nothing returns nothing
                call TriggerAddCondition(retro_launch, Condition(function thistype.handler))    // Basically for RetroFade
            endmethod
        endmodule
       
    endlibrary
     


    Addons

    Example:

    Code (vJASS):

    library RetroExample requires Retro
       
        private struct example extends array
        // Retro structs must <extend array>
           
       
            effect attach   // This effect gets attached to the unit for the duration of the spell.
           
            method onFinish takes nothing returns nothing
                call DestroyEffect(.attach)
            endmethod   // <onFinish> is the <onDestroy> method. You don't need to call <deallocate> from it.
           
        /*
         *  <onLoopReverse> or <onLoopForward> methods are called only once per cycle of RETRO_TIMEOUT,
         *  so they help to moderate high numbers of special effects to keep the frames-per-second low.
         */

            method onLoopReverse takes nothing returns nothing
                call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Other\\StrongDrink\\BrewmasterMissile.mdl", .x, .y))
                call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Other\\Transmute\\GoldBottleMissile.mdl", .x, .y))
            endmethod
           
           
            implement RETRO // The module is below <onFinish> and <onLoopReverse> while above <allocate>
           
           
            static method Selection takes nothing returns boolean
                local thistype this
               
                // <allocate> and <deallocate> are provided by the RETRO module.
                set this = allocate(GetFilterUnit(), false, true)
               
                // <allocate> will return <0> if a unit is invalid; I advise checking that before any continuation;
                if this > 0 and GetHeroProperName(.subject) != "Master of Time" then
               
                /*
                 *  <initiate> immediately launches a unit back through time for the
                 *  specified duration.  This won't do anything for units created in
                 *  the same instant; they haven't had time to record some movement.
                 */

                    call .initiate(2.00)
                 
                    // The unit you're manipulating is referenced as <.subject>
                    set .attach = AddSpecialEffectTarget("Abilities\\Spells\\Other\\Drain\\ManaDrainTarget.mdl", .subject, "overhead")
                   
                    // <.restoreGoodHP> will revive the unit if its former HP was greater.
                    set .restoreGoodHP = true
                   
                /*
                 *  <.restoreGoodMP> will do the same, but for MP. Un-commenting the line will provide a syntax
                 *  error, as I have the Retro constant boolean RECORD_MP set to <false>, preventing the variable
                 *  from even being compiled.
                 */

                    //set .restoreGoodMP = true
                   
                    call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\Monsoon\\MonsoonBoltTarget.mdl", .subject, "origin"))
                endif
                return false
            endmethod
           
           
            static method onInit takes nothing returns nothing
                local integer i = 0
                local trigger t = CreateTrigger()
                loop
                    call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SELECTED, Filter(function thistype.Selection))
                    exitwhen i == 11
                    set i = i + 1
                endloop
            endmethod   // A normal initialization method.
           
            /*
             *  Note the <onLoop>, <onLoopForward> and <onStart> methods are nowhere in sight. They are optional,
             *  and so are <onLoopReverse> and <onFinish>
             *  
             *  Final Notes
             *  ¯¯¯¯¯¯¯¯¯¯¯
             *  ~ You should only implement an event-method if you have a use for it.
             *  ~ You don't have to call <destroy> on a RETRO-implemented struct unless you wish to call your onFinish method.
             */

             
        endstruct
       
    endlibrary
     


    Retro-Fade:
    Code (vJASS):

    //! zinc

    library RetroFade {
    /*
     *  The current fade-functionality is very primitive at the moment.  If you have dynamic fog in your
     *  map that changes sometimes, I don't recommend this library for you.
     */

        constant real
       
            DURATION                = 2.00      ,
            MAX_DISTANCE            = 1000.0    ,   /* Players whose cameras are out of this range will not see the fog fade */
            FLICKER_MAGNITUDE       = 300.0     ;   /* Factors between Z-End + this & Z-End - this */
           
        constant boolean
       
            FLICKER                 = true      ;
       
        real fog[];
        integer units[];
        boolean paused = true, want = false, to = true;
        constant integer Fz = 18, FZ = 19, Fd = 20, Fr = 21, Fg = 22, Fb = 23;
       
        function onInit() {
            integer i;  /*    Your fog settings     Retro's fog settings
        Fog type                     |                       |                   Allowed values
        ¯¯¯¯¯¯¯¯                     V                       V                   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        |  Z-Start */
       fog[0]  = 3000.00   ;     fog[6]  = 1250.0    ; /*      0.00  to  ????
        ---------------------------------------------------------------------------------------
        |    Z-End */
       fog[1]  = 5000.00   ;     fog[7]  = 3250.00   ; /*      0.00  to  ????
        ---------------------------------------------------------------------------------------
        |  Density */
       fog[2]  = 0.50      ;     fog[8]  = 0.1       ; /*      0.00  to  1.00
        ---------------------------------------------------------------------------------------
        |      Red */
       fog[3]  = 1.00      ;     fog[9]  = 0.35      ; /*      0.00  to  1.00
        ---------------------------------------------------------------------------------------
        |    Green */
       fog[4]  = 1.00      ;     fog[10] = 0.35      ; /*      0.00  to  1.00
        ---------------------------------------------------------------------------------------
        |     Blue */
       fog[5]  = 1.00      ;     fog[11] = 0.00      ; /*      0.00  to  1.00
        ---------------------------------------------------------------------------------------
     */

            for (i = 0; i < 24; i+= 1)  i:units = 0;
            for (i = 0; i < 6;  i+= 1) {
           
                if (i:fog==fog[i + 6]) {
                    if (i:fog < 0.999)  i:fog+= 0.001;
                    else                i:fog-= 0.001;
                }
               
                fog[i + 12] = (i:fog - fog[i + 6])/(DURATION / RETRO_TIMEOUT);
                fog[i + 18] =  i:fog;
            }
        }
           
        public struct retrofade [] {
       
            private static method handler()-> boolean {
                integer i = 18;
               
                if (!want && paused)
                    return false;
             
                static if (FLICKER) {
                    if (GetRandomReal(0.0, 2.0)<=RETRO_TIMEOUT)
                        SetTerrainFogEx(0, Fz:fog, GetRandomReal(FZ:fog - FLICKER_MAGNITUDE, FZ:fog + FLICKER_MAGNITUDE), Fd:fog, Fr:fog, Fg:fog, Fb:fog);
                }
               
                if (paused)
                    return false;
               
                if (to) while (i < 24) { /* Fade into retro-fog */
               
                    if   ( i:fog <= fog[i - 12]) {
                           i:fog  = fog[i - 12];        paused = true;  break;
                    } else i:fog -= fog[i -  6];                        i+= 1;
                }
                else while (i < 24) { /* Fade into normal-fog */
               
                    if   ( i:fog >= fog[i - 18]) {
                           i:fog  = fog[i - 18];        paused = true;  break;
                    } else i:fog += fog[i -  6];                        i+= 1;
                }
               
                SetTerrainFogEx (0, Fz:fog, FZ:fog, Fd:fog, Fr:fog, Fg:fog, Fb:fog);
               
                return false;
            }
       
            module RetroTimerModule;
           
            static method CamCheck (real centerX, real centerY) -> boolean {
             real   cam_x= GetCameraTargetPositionX(), cam_y= GetCameraTargetPositionY();
             return(cam_x >= centerX - MAX_DISTANCE && cam_y >= centerY - MAX_DISTANCE &&
                    cam_x <= centerX + MAX_DISTANCE && cam_y <= centerY + MAX_DISTANCE );
            }
           
            static method yes (player whichPlayer, real x, real y) {
                integer id = GetPlayerId(whichPlayer);
                if (GetLocalPlayer() != whichPlayer || whichPlayer == null || id > 11)
                    return;
                   
                if (CamCheck(x, y)) {
                    to = true;
                    want = true;
                    if (paused)
                        paused = false;
                }
               
                units[id]+= 1;
            }
           
            static method no (player whichPlayer) {
                integer id = GetPlayerId(whichPlayer);
                if (GetLocalPlayer() != whichPlayer || whichPlayer==null || id > 11)
                    return;
                   
                units[id]-= 1;
                if (units[id] <= 0) {
                    units[id] = 0;
                    to = false;
                    want = false;
                    if (paused)
                        paused = false;
                }
            }
           
        }
    }
    //! endzinc
     


    Time-Warp:
    Code (vJASS):

    library TimeWarp requires Retro, Projectile, optional Knockback, optional RetroFade, optional Recycle, optional GroupUtils
    //************************************************************************************************************************
    //   ___ ___ _   _  ___   _    _  _   ___  ___
    //   /   /   /| /  /__    |   / /__/ /__/ /__/
    //  /  _/_  / |/| /__     |/|/ /  / /  | /
    //                                            Created by Bribe
    //************************************************************
    /*
     *  Requirements
     *  ¯¯¯¯¯¯¯¯¯¯¯¯
     *  Projectile by Berb ~ [url]http://www.hiveworkshop.com/forums/jass-functions-413/custom-projectiles-162121/[/url]
     *  Vector by Anitarf  ~ [url]http://www.wc3c.net/showthread.php?t=87027[/url]
     *  Retro (provided)
     *  
     *  Optional Requirements
     *  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
     *  Knockback Lite by Berb    ~ [url]http://www.hiveworkshop.com/forums/jass-functions-413/knock-back-lite-161831/[/url]
     *  GroupUtils by Rising_Dusk ~ [url]http://www.wc3c.net/showthread.php?t=104464[/url]
     *  Recycle by Nestharus      ~ [url]http://www.thehelper.net/forums/showthread.php?t=136087[/url]
     *  RetroFade (provided)
     *  
     */

        globals
            private constant integer    SPELL_ID    = 'A003'    /* The rawcode for the Time Travel spell */
            private constant integer    EXTRA_ID    = 'A001'    /* An ability that collapses the caster's portals */
            private constant integer    DUMMY_ID    = 'n000'    /* This should use the DUMMY.mdx model from XE */
       
    //* >> Knockback Values;
            private constant real   KB_DURATION         = 1.125     /* Duration is in seconds */
            private constant real   KB_MIN_DISTANCE     = 225.0     /* Value in WC3 gridspace */
            private constant real   KB_MAX_DISTANCE     = 525.0
                                   
    //* >> TimeWarp Values;        
            private constant real   BASE_DURATION       = 3.00      /* Base value at level 1 */
            private constant real   LVL_INC_DURATION    = 1.15      /* Level increment value */
                                   
            private constant real   BASE_DAMAGE         = 10.0
            private constant real   LVL_INC_DAMAGE      = 4.00
                                   
            private constant real   BASE_AOE            = 150.0
            private constant real   LVL_INC_AOE         = 40.0
           
            private   group array   casterGroup
        /*
         *  If you have the xebasic library in your map, remove these two lines so they won't conflict:
         */
    constant integer    XE_DUMMY_UNITID         = DUMMY_ID
            constant real       XE_MAX_COLLISION_SIZE   = 60.00
        endglobals

        static if LIBRARY_RetroFade then
            globals
                private sound callback = CreateMIDISound("FlashBack1Second", 12700, 12700)
            endglobals
        endif
       
        static if LIBRARY_Knockback then
            struct retroKB extends knockback
           
                effect attach
                static constant string fx = "Abilities\\Spells\\Orc\\AncestralSpirit\\AncestralSpiritCaster.mdl"
               
                private method onDestroy takes nothing returns nothing
                    call DestroyEffect(this.attach)
                endmethod
               
                private static method onInit takes nothing returns nothing
                    call Preload(fx)
                endmethod
               
            endstruct
        endif
       
        struct retroproj extends projectile
           
            static constant string launchSkin = "Abilities\\Spells\\Undead\\Possession\\PossessionMissile.mdl"
            static constant string attackSkin = "Abilities\\Spells\\Undead\\DarkSummoning\\DarkSummonMissile.mdl"
            static constant string simpleSkin = "Abilities\\Spells\\NightElf\\Barkskin\\BarkSkinTarget.mdl"
           
            static constant string simpImpact = "Abilities\\Spells\\Items\\AIil\\AIilTarget.mdl"
            static constant string painImpact = "Abilities\\Spells\\Demon\\DemonBoltImpact\\DemonBoltImpact.mdl"
           
            private static method onInit takes nothing returns nothing
                call Preload(launchSkin)
                call Preload(attackSkin)
                call Preload(simpleSkin)
                call Preload(simpImpact)
                call Preload(painImpact)
            endmethod
           
            effect      skin
            group       units
           
            boolean     primary = true
           
            real        damage
            real        Time
           
            vector      carryVec
            timewarp    carryWarp
           
            private method onFinish takes nothing returns nothing
                call DestroyEffect(.skin)
                call SetUnitExploded(.toUnit, true)
                if .primary then
                    if UnitAlive(.target) then
                        call timewarp.Initiate(this)
                    endif
                    call .carryVec.destroy()
                else
                    set .carryWarp.go = true
                    if UnitAlive(.target) and not .carryWarp.to then
                        call DestroyEffect(AddSpecialEffectTarget(painImpact, .target, "overhead"))
                        call UnitDamageTarget(.carryWarp.caster, .target, .carryWarp.damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
                    elseif UnitAlive(.source) then
                        call DestroyEffect(AddSpecialEffectTarget(simpImpact, .source, "origin"))
                    endif
                endif
            endmethod
           
        endstruct
       
        struct timewarp extends array
           
            unit        caster
            unit        oriUnit
            group       units
           
            player      casterOwner
            player      victimOwner
           
            effect      attach
            effect      gloomy
            effect      spooky
           
            boolean     go
            boolean     to
            vector      oriVec
            real        damage
           
            static constant string finalFX      = "Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageDeathCaster.mdl"
            static constant string callbackFX1  = "Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl"
            static constant string callbackFX2  = "Abilities\\Spells\\Other\\Monsoon\\MonsoonBoltTarget.mdl"
           
            static constant string forwardLoopFX  = "Abilities\\Weapons\\FlyingMachine\\FlyingMachineImpact.mdl"
            static constant string reverseLoopFX1 = "Abilities\\Spells\\Other\\Transmute\\GoldBottleMissile.mdl"
            static constant string reverseLoopFX2 = "Abilities\\Spells\\Other\\StrongDrink\\BrewmasterMissile.mdl"
           
            static constant string caughtFX = "Abilities\\Spells\\Items\\AIso\\AIsoTarget.mdl"
            static constant string gloomyFX = "Abilities\\Spells\\Human\\Banish\\BanishTarget.mdl"
            static constant string spookyFX = "Abilities\\Spells\\NightElf\\shadowstrike\\shadowstrike.mdl"
            static constant string attachFX = "Abilities\\Spells\\Undead\\Possession\\PossessionTarget.mdl"
           
            static constant string epicenterFX  = "Objects\\Spawnmodels\\Undead\\UCancelDeath\\UCancelDeath.mdl"
            static constant string recallFXCast = "Abilities\\Spells\\Items\\TomeOfRetraining\\TomeOfRetrainingCaster.mdl"
            static constant string recallFXTarg = "Abilities\\Spells\\Human\\MarkOfChaos\\MarkOfChaosTarget.mdl"
           
            private static method PreloadStrings takes nothing returns nothing
                call Preload(finalFX)
                call Preload(callbackFX1)
                call Preload(callbackFX2)
                call Preload(forwardLoopFX)
                call Preload(reverseLoopFX1)
                call Preload(reverseLoopFX2)
                call Preload(caughtFX)
                call Preload(gloomyFX)
                call Preload(spookyFX)
                call Preload(attachFX)
                call Preload(epicenterFX)
                call Preload(recallFXCast)
                call Preload(recallFXTarg)
            endmethod
           
            private method onFinish takes nothing returns nothing
                call DestroyEffect(AddSpecialEffect(finalFX, .x, .y))
                call DestroyEffect(.spooky)
                call DestroyEffect(.gloomy)
                call DestroyEffect(.attach)
                call ExplodeUnitBJ(.oriUnit)
                call GroupRemoveUnit(.units, .subject)
                call SetUnitTimeScale(.subject, 1.00)
               
                if .oriVec > 0 then
                    call .oriVec.destroy()
                    set .oriVec = 0
                endif
                static if LIBRARY_RetroFade then
                    call retrofade.no(.casterOwner)
                    call retrofade.no(.victimOwner)
                endif
               
            endmethod
           
            private method onStart takes nothing returns nothing
                call DestroyEffect(AddSpecialEffectTarget(callbackFX1, .subject, "origin"))
                call DestroyEffect(AddSpecialEffectTarget(callbackFX2, .subject, "origin"))
                call SetUnitTimeScale(.subject, 4.00)
               
                set .restoreBadHP = true
                //set .restoreBadMP = true  <- would throw a syntax error because I have RESTORE_MP set to <false>
               
                static if LIBRARY_RetroFade then
                    if retrofade.CamCheck(.x, .y) then  // Only plays the sound if a player's camera is near.
                        call StartSound(callback)
                    endif
                endif
               
            endmethod
           
            private method onLoopForward takes nothing returns nothing
                call DestroyEffect(AddSpecialEffectTarget(forwardLoopFX, .subject, "overhead"))
            endmethod
            private method onLoopReverse takes nothing returns nothing
                call DestroyEffect(AddSpecialEffectTarget(reverseLoopFX1, .subject, "origin"))
                call DestroyEffect(AddSpecialEffectTarget(reverseLoopFX2, .subject, "origin"))
            endmethod
           
           
            static vector regVec    // This gets <vector.create> during map init.
           
            static method operator bigVec takes nothing returns vector
                return 8191
            endmethod
           
           
            private method onLoop takes nothing returns nothing
                local real x2
                local real y2
                local retroproj pro
                // Will not launch until the other missile has expired.
                // Check the origin vector to make sure it's not a dud.
                if .go and .oriVec!=0 then
                    /* Check if the distance is far enough */
                    set x2 = .x - .oriVec.x
                    set y2 = .y - .oriVec.y
                    if ((x2*x2 + y2*y2) > 0xF000) then
                        /* Yet another important check */
                        if UnitAlive(.subject) and UnitAlive(.oriUnit) then
                            /* bigVec = Location(.x, .y) */
                            call bigVec.getTerrainPoint(.x, .y)
                            /* Add flyheight and offset to vector.Z */
                            set  bigVec.z = bigVec.z + this.z + 90.0
                            if .to then
                                /* If flying toward the subject unit */
                                set .to = false
                                set pro = retroproj.create(CreateUnit(Player(15), DUMMY_ID, .oriVec.x, .oriVec.y, bj_RADTODEG * Atan2(.y - .oriVec.y, .x - .oriVec.x)))
                                set pro.target = .subject
                                /* Control the missile-art of the projectile */
                                set pro.skin = AddSpecialEffectTarget(retroproj.attackSkin, pro.toUnit, "origin")
                                /* Control the dimensions and launch-properties of the projectile */
                                call SetUnitScale(pro.toUnit, 0.65, 0.65, 0.65)
                                call pro.doLaunch(.oriVec, bigVec, 900.0, 0.10)
                            else
                                /* If gliding towards the origin point */
                                set .to = true
                                set pro = retroproj.create(CreateUnit(Player(15), DUMMY_ID, .x, .y, bj_RADTODEG * Atan2(.oriVec.y - .y, .oriVec.x - .x)))
                                set pro.source = .oriUnit
                                /* Control the missile-art of the projectile */
                                set pro.skin = AddSpecialEffectTarget(retroproj.simpleSkin, pro.toUnit, "origin")
                                /* Control the dimensions and launch-properties of the projectile */
                                call SetUnitScale(pro.toUnit, 0.85, 0.85, 0.85)
                                call pro.doLaunch(bigVec, .oriVec, 300.0, 0.10)
                            endif
                           
                            set .go             = false
                            set pro.primary     = false
                            set pro.carryWarp   = this
                           
                        endif
                    endif
                endif
            endmethod
           
            implement RETRO
           
            static method Initiate takes retroproj h returns nothing
                local real dist
                local real x
                local real y
                local thistype this = allocate(h.target, true, true)
               
                if this == 0 then   /* The allocate method will return 0 for stationary units (buildings, wards) */
                    return
                endif
               
                set .caster = h.source
                set .units  = h.units
                set .damage = h.damage
               
                static if LIBRARY_RetroFade then
                    set .casterOwner = GetOwningPlayer(.caster)
                    set .victimOwner = GetOwningPlayer(.subject)
                    call retrofade.yes(.casterOwner, .x, .y)
                    call retrofade.yes(.victimOwner, .x, .y)
                endif
               
                if .oriVec == 0 then
                    set .oriVec = vector.createTerrainPoint(.x, .y)
                    set .oriVec.z = .oriVec.z + this.z + 60.00
                endif
               
                set .oriUnit = CreateUnit(Player(15), DUMMY_ID, .oriVec.x, .oriVec.y, bj_RADTODEG * Atan2(.oriVec.y - .y, .oriVec.x - .x))
                set .go = true
                set .to = false
                call SetUnitScale(.oriUnit, 0.80, 0.80, 0.80)
                call GroupAddUnit(.units, .subject)
                call .capture(h.Time)
               
                call DestroyEffect(AddSpecialEffectTarget(caughtFX, .oriUnit, "origin"))
                set .gloomy = AddSpecialEffectTarget(gloomyFX, .oriUnit, "chest")
                set .spooky = AddSpecialEffectTarget(spookyFX, .oriUnit, "overhead")
                set .attach = AddSpecialEffectTarget(attachFX, .subject, "overhead")
               
                if IsUnitType(.subject, UNIT_TYPE_FLYING) then
                    call UnitAddAbility(.oriUnit, 'Amrf')
                    call UnitRemoveAbility(.oriUnit, 'Amrf')
                    call SetUnitFlyHeight(.oriUnit, .z, 0.00)
                endif   /* The origin turns into a floating menace for air victims */
               
                static if LIBRARY_Knockback then
                    set x = .x - h.carryVec.x
                    set y = .y - h.carryVec.y
                    set dist = KB_MAX_DISTANCE - (SquareRoot(x*x + y*y) / 2.0)
                    if dist <= KB_MIN_DISTANCE then
                        set dist = KB_MIN_DISTANCE
                    endif
                    set retroKB.create(.subject, Atan2(y, x), dist, KB_DURATION).attach = AddSpecialEffectTarget(retroKB.fx, h.target, "chest")
                endif
               
            endmethod
           
            static method enumAOE takes nothing returns boolean
                local retroproj p
                local unit u = GetFilterUnit()
                if UnitAlive(u) and IsUnitEnemy(u, thistype(0).casterOwner) and thistype(GetUnitId(u)).subject == u then
                   
                    call bigVec.getTerrainPoint(GetUnitX(u), GetUnitY(u))
                    set bigVec.z    = bigVec.z + 60.0
                    set p           = retroproj.create(CreateUnit(Player(15), DUMMY_ID, regVec.x, regVec.y, bj_RADTODEG * Atan2(bigVec.y - regVec.y, bigVec.x - regVec.x)))
                    set p.skin      = AddSpecialEffectTarget(retroproj.launchSkin, p.toUnit, "origin")
                    set p.target    = u
                    set p.source    = thistype(0).caster
                    set p.units     = thistype(0).units
                    set p.damage    = thistype(0).damage
                    set p.Time      = retroproj(0).Time
                    set p.carryVec  = vector.create(regVec.x, regVec.y, regVec.z)
                    call p.doLaunch(regVec, bigVec, 900.0, 0.10)
                   
                endif
                set u = null
                return false
            endmethod
           
            static method forceCollapse takes nothing returns boolean
                call thistype(GetUnitId(GetEnumUnit())).collapse()
                call DestroyEffect(AddSpecialEffectTarget(recallFXTarg, GetEnumUnit(), "origin"))
                return false
            endmethod
           
            static method onCast takes nothing returns boolean
                local real factor
                local integer id = GetSpellAbilityId()
               
                if (id == SPELL_ID) then
               
                    set thistype(0).caster = GetTriggerUnit()
                    set thistype(0).casterOwner = GetTriggerPlayer()
                    set thistype(0).units = casterGroup[GetUnitId(thistype(0).caster)]
                   
                    call regVec.getTerrainPoint(GetSpellTargetX(), GetSpellTargetY())
                    set regVec.z = regVec.z + 60.00
                   
                    call DestroyEffect(AddSpecialEffect(epicenterFX, regVec.x, regVec.y))
                   
                    set factor = GetUnitAbilityLevel(thistype(0).caster, SPELL_ID) - 1.00
                    set thistype(0).damage = BASE_DAMAGE   + LVL_INC_DAMAGE   * factor
                    set retroproj(0).Time  = BASE_DURATION + LVL_INC_DURATION * factor
                   
                    static if LIBRARY_GroupUtils then
                        if GetRandomInt(1, 10) == 1 then
                            call GroupRefresh(thistype(0).units)
                        endif
                        call GroupEnumUnitsInArea(ENUM_GROUP, regVec.x, regVec.y, BASE_AOE + LVL_INC_AOE * factor, Filter(function thistype.enumAOE))
                    else
                        call GroupEnumUnitsInRange(bj_lastCreatedGroup, regVec.x, regVec.y, BASE_AOE + LVL_INC_AOE * factor, Filter(function thistype.enumAOE))
                    endif
                   
                elseif (id == EXTRA_ID) then
                    call ForGroup(casterGroup[GetUnitId(GetTriggerUnit())], function thistype.forceCollapse)
                    call DestroyEffect(AddSpecialEffectTarget(recallFXCast, GetTriggerUnit(), "origin"))
                endif
               
                return false
            endmethod
           
            static method learnSkill takes nothing returns boolean
                if GetLearnedSkill() == SPELL_ID and GetLearnedSkillLevel() == 1 then
                    call UnitAddAbility(GetTriggerUnit(), EXTRA_ID)
                   
                    static if LIBRARY_Recycle then
                        set casterGroup[GetUnitId(GetTriggerUnit())] = Group.get()
                    elseif LIBRARY_GroupUtils then
                        set casterGroup[GetUnitId(GetTriggerUnit())] = NewGroup()
                    else
                        set casterGroup[GetUnitId(GetTriggerUnit())] = CreateGroup()
                    endif
                   
                endif
                return false
            endmethod
           
            static method cleanup takes unit caster returns nothing
                local integer id
                static if LIBRARY_AIDS then
                    set id = AIDS_GetDecayingIndex()
                else
                    set id = GetUnitId(caster)
                endif
                if casterGroup[id] != null then
                    static if LIBRARY_Recycle then
                        call Group.release(casterGroup[id])
                    elseif LIBRARY_GroupUtils then
                        call ReleaseGroup(casterGroup[id])
                    else
                        call GroupClear(casterGroup[id])
                        call DestroyGroup(casterGroup[id])
                    endif
                    set casterGroup[id] = null
                endif
            endmethod
           
            static if LIBRARY_AIDS then
                private static method AIDS_OnDeallocate takes nothing returns boolean
                    call cleanup(null)
                    return false
                endmethod
            endif
           
            private static method onInit takes nothing returns nothing
                local trigger t
               
                set t = CreateTrigger()
                call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_SKILL)
                call TriggerAddCondition(t, Condition(function thistype.learnSkill))
               
                set t = CreateTrigger()
                call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
                call TriggerAddCondition(t, Condition(function thistype.onCast))
               
                set regVec = vector.create(0.0, 0.0, 0.0)
                call PreloadStrings()
               
                static if LIBRARY_AIDS then
                    call AIDS_RegisterOnDeallocate(Filter(function thistype.AIDS_OnDeallocate))
                else
                    call OnUnitDeindexed(thistype.cleanup)
                endif
            endmethod
           
        endstruct
       
    endlibrary
       
    library_once xebasic
    /* AutoIndex and GroupUtils will interpret this to set some default values */
    endlibrary
     

     

    Attached Files:

    Last edited: Jul 15, 2010
  2. Berb

    Berb

    Joined:
    Jan 21, 2006
    Messages:
    2,539
    Resources:
    2
    JASS:
    2
    Resources:
    2
    I just saw this now. Wow this looks almost exactly like my code. In fact it almost looks better. I like some of your naming prefixes, such as
    fade_
    . It unifies a group of members nicely.

    Edit -- continued reading, Jesus man. Lmao.

    Well, I've read the code and I do not have a damn idea what it does. I got confused when there were fog settings.

    Oh I see. I think.

    /facepalm -- didn't see the attached test-map.
     
    Last edited: May 25, 2010
  3. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,759
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Has been updated with a long (likely not even long enough) manual to explain components of this system :)
     
  4. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,759
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Updated to version 2.01 with some great new features on the child structs.
     
  5. Berb

    Berb

    Joined:
    Jan 21, 2006
    Messages:
    2,539
    Resources:
    2
    JASS:
    2
    Resources:
    2
    I'll get around to checking this out soon. So busy.
     
  6. Viikuna

    Viikuna

    Joined:
    Aug 6, 2008
    Messages:
    430
    Resources:
    0
    Resources:
    0
    So, you changed fog effect to be local now. Looks neat. Thats pretty cool system indeed.
     
  7. Berb

    Berb

    Joined:
    Jan 21, 2006
    Messages:
    2,539
    Resources:
    2
    JASS:
    2
    Resources:
    2
    Very stable. I tested it on a bunch of individual units at various intervals (including units that had already been time warped) and it worked fine. Good.
     
  8. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,759
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Major updates in this Version 2.02, including a video demonstration of its immense capability. Thanks to Berbanog for the Projectile system, which adds the best part of the eye candy.

    Demo map has also been updated.
     
  9. WherewolfTherewolf

    WherewolfTherewolf

    Joined:
    Jul 18, 2008
    Messages:
    2,821
    Resources:
    3
    Models:
    3
    Resources:
    3
    Wow, I tried something like this a while ago to no avail, nice system
     
  10. Miss_Foxy

    Miss_Foxy

    Joined:
    Oct 15, 2008
    Messages:
    2,821
    Resources:
    0
    Resources:
    0
    So this actually manages to get a huge AOE of people back in time? Wow.
     
  11. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,759
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Huge update - this library is now known as Retro and will knock your socks off.

    Perfect framerate while recording the coordinates of 130+ units while calling dozens back through time, no errors after many hours of bug testing.
     
  12. Berb

    Berb

    Joined:
    Jan 21, 2006
    Messages:
    2,539
    Resources:
    2
    JASS:
    2
    Resources:
    2
    Your code is coming along really nicely, Bribe.
     
  13. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,759
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Thanks, Berbanog

    Did you read that bit about the 0-vector? Where did you get the idea that a 0-vector shouldn't be used for the projectile launch method?
     
  14. Berb

    Berb

    Joined:
    Jan 21, 2006
    Messages:
    2,539
    Resources:
    2
    JASS:
    2
    Resources:
    2
    A 0-vector can be used as a launch vector; it's just that a vector that is "null" cannot be.
     
  15. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,759
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Code (vJASS):
    if (start == 0) or (finish == 0) or (.active) then
        return false


    Start vector I have at 8191, finish vector I wanted to be at 0 (to avoid occupying slots on the array). This is what I meant by 0-vector; the system rejects it. The vector isn't null, those x, y and z values were obtained by using
    vec.getTerrainPoint(x, y)
    .
     
  16. Berb

    Berb

    Joined:
    Jan 21, 2006
    Messages:
    2,539
    Resources:
    2
    JASS:
    2
    Resources:
    2
    If your vector "v" is
    v == 0
    then that means it has no yet been created; you cannot have a vector that has values and is equal to 0, since the stack starts at 1.

    If you have a vector "v" such that
    v.x == 0
    and
    v.y == 0
    then that is not the same thing; the actual vector will be an integer from 1 to 8191.

    Since
    vector.getTerrainPoint(x, y)
    returns an allocated vector, it will never return
     
    .

    It isn't possible to have a finish vector at 0, the smallest vector that may contain values is 1.

    Just to clarify,
    vector.getTerrainPoint(0, 0)
    isn't going to return 0. If it is the very first vector being created then it will return 1; there is no way of having a vector that is equal to 0 and contains data. It would be like
    CreateUnit(null, 'hfoo', 0, 0, 0)
    - you can't have a
    null
    player so that would cause the function to return null, rather than creating a unit for a null player. Similarly, launching a projectile that has a null start/finish doesn't make sense, and as such the function will return false.
     
  17. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,759
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Code (vJASS):
    vector(0).getTerrainPoint(.x, .y)
    vector(8191).getTerrainPoint(.x, .y)


    Both fill the array indexes. I'm not really sure why you even bothered to implement a check to make sure the vectors aren't zero, since if the user knows what vectors are that user will definitely know what values he's putting in. I am just trying to use minimal vectors because I only use these in that one instant, so there is no need for continuity or allocation/deallocation.

    Besides, you only use these launch vectors as reference points, you just copy those x, y, z values onto your system's vectors, so the array index that you're putting into the system to read those should be irrelevant to the system altogether. Just my thoughts.
     
  18. Berb

    Berb

    Joined:
    Jan 21, 2006
    Messages:
    2,539
    Resources:
    2
    JASS:
    2
    Resources:
    2
    The struct index 0 is used as a null object; by using
    vector(0)
    you're going to create a lot of problems for yourself, and the value of that vector is not reliable. I don't really see what you're doing, it seems like you're trying to handle everything JassHelper does with structs by yourself; you shouldn't be blindly typecasting integers into struct types.

    Because a vector that has been destroyed will be equal to 0; the purpose of the
    doLaunch
    method returning a boolean is so that users can debug to see whether or not their projectiles are being executed properly.

    Then use a global vector. Do not use typecast values.

    Okay; look at the code:

    Code (vJASS):
            if (start == 0) or (finish == 0) or (.active) then
                return false
                //There are rules to launching a projectile. If any of these rules are broken, the method will
                //return false. A projectile cannot be launched if it has already been launched.
            endif
           
            set .posVec.x   = start.x    
            set .posVec.y   = start.y      
            set .posVec.z   = start.z
            set .tarVec.x   = finish.x
            set .tarVec.y   = finish.y
            set .tarVec.z   = finish.z
            set .strVec.x   = start.x
            set .strVec.y   = start.y
            set .strVec.z   = start.z
            set .speed      = speed
            set .arc        = arc


    If you pass this method null vectors, then you're going to run into errors when you're "copying" the values. If you pass a null (0) vector, then the components of that vector are not necessarily always going to be
    x=0
    and
    y=0
    . Rather than launch the projectile improperly and possibly causing malfunctions, I disallow the projectile from being launched.
     
  19. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,759
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Ah, got it. That actually makes sense now that you've explained why you have that check. Though I can't deny I like typecasting real values :p

    The idea behind me using zero was to maximize the number of vectors that can exist (even by so minimal as 2, since I'm also using 8191, I figure, hey, why not). Since I don't need that vector past the instant I use it, I couldn't care less what happens to it after that time, because I set it fresh each time.
     
  20. Berb

    Berb

    Joined:
    Jan 21, 2006
    Messages:
    2,539
    Resources:
    2
    JASS:
    2
    Resources:
    2
    Actually it is as minimal as 1 index. Struct indexes use every integer from 1 to the default array size, 8191. This means that there are 8190 possible indexes and therefore 8190 possible instances at any given time.