HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

[system] Time Warp

05-27-2010, 03:28 PM#1
Bribe
The newest system on the market, Time Travel will work in any situation. You can log a unit's coordinates for an infinite amount of time, and when you decide it's time the unit will quickly retrace its steps :)

Video

I have included the primary Time Travel library, as well as two sub-libraries that utilize some of the capabilities this system has. The first system, Time-Warp, is a spell. The second system, All-Time, will throw a unit backwards in time for a couple of seconds when you click on it.

The sub-libraries are optional and are templates for the grander scheme of things. You are not limited to using these pre-set libraries, and are more than welcome to extend the Time-Travel struct to develop cooler things than I can come up with

The vJass code, its manual, and extensions required to work:

Manual:
Collapse JASS:
//*********************************************************************************************************
//*
//*     Time-Travel Manual
//*     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//*     Looking over the main Time Travel library you'll notice many bits of code that seem testy. The pur-
//*     pose of this manual is to identify what I feel are the areas that need the most explanation, as I
//*     want the user to have the greatest understanding possible of how I built this system. People ex-
//*     plained GUI, JASS, then vJass to me -- I will explain this to you.
//*     
//*     First, this library will not function by itself. To get any component to work, you must use struct
//*     creation.
    
            local timetravel t = timetravel.create(GetTriggerUnit(), 10. )
            
//*     The above command will create an instance of the timetravel struct. This is directing the struct to
//*     make the "Triggering Unit" a time traveller, and this time traveller will fall "back in time" in 10
//*     seconds. Any creation of timetravel requires these two parameters.
            
            static method create takes unit whichUnit,real duration returns thistype
//*                         ^ function name     ^ unit       ^ specified time     ^ returns a new instance
        
//*     While I will not re-type the entire JASSHelper manual to demonstrate to you how to manipulate these
//*     instances, I will tell you about the qualities unique to the time-travel struct :)
//*     
//*     In the Time Travel Library, scroll down to line 221 where the first ############# block appears.
//*     Notice the values below the line - they are key. Everything prefixed with "private" I have done
//*     intentionally either to protect the value from being edited by you, or it's just so obscure I don't
//*     expect anyone to dawdle with it. Point is, the "private" values are not meant to be used, so I will
//*     not explain them.
//*     
//*     "Readonly" variables - like a readonly file, you can reference them, but you cannot change their
//*     value.
            
            call KillUnit(.get_Victim)  //this is valid.
            set .get_Victim = null      //this will give you the syntax error ".get_Victim is readonly".
            
//*     The reason I made so many variables "readonly" is because several things need to be done internally
//*     in the library in order to truly make changes. Changing the "victim" is not as simple as a variable
//*     "set". What if the unit is already in motion, and has had its pathing removed? If you just set the
//*     variable to another unit, that other guy is going to have some weird movement for the rest of the 
//*     game (walk over trees, walk through walls). Therefore, I built <Method Operators> to make changes
//*     you would otherwise do with a "set this=that" command.
            
            set .victim = GetTriggerUnit()  //this is valid
            call KillUnit(.victim)  //this is also valid :)
            
//*     The above functionality is made possible because <victim> is a unit operator. This unit operator
//*     calls a method within the library that instructs the old victim's pathing to be restored to normal
//*     (among other technical things). The new unit will resume where the old unit left off, and will even
//*     follow the "backwards-through-time" path that the old unit set for him. The method operators are
//*     there to protect you from experiencing in-game glitches - that's pretty much it.
//*     
//*     Here is the list of method operators at your disposal and an example of how to use each:
            
            unit operator    victim
                set .victim = bj_lastCreatedUnit
            
            player operator  extraPlayer            //This is used to establish which players get to see
                set .extraPlayer = GetLocalPlayer() //fade effects from the instance. If a unit is set to
                                                    //be a time traveller, the owner of that unit will see
                                                    //the fog fading. However, if this is a spell, I think
                                                    //you'll want to have the owner of the casting unit see
                                                    //the fading, too. Just set "extraPlayer" to "owner of 
                                                    //caster" and the player will see the effect, or, as is
                                                    //in this example, GetLocalPlayer() will set it to show
                                                    //fog for every player :)
            real operator    timeValue
                set .timeValue = 10.
                
            boolean operator forward
                set .forward = false
                
            boolean operator paused
                set .paused = true
                
            boolean operator wantAmbience
                set .wantAmbience = false
                
            boolean operator wantOnLoopEx
                set .wantOnLoopEx = true
                
//*     Public Methods
//*     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//*     You will find some of these very handy. Normally, if you want to store data to a unit, you'd need
//*     to make a new hashtable (subtracting from the 256 limit) or use someone's system to do it. Because
//*     I'm already using a hashtable for this system, I figured, since I am only using at most the first
//*     25,000 parent keys, why not also store handles, which are much higher values and will not overwrite
//*     the instances in the library?
//*
//*     Giving you direct access to the time-table can be hazardous (such as saving a null agent will over-
//*     write the first slot and wipe out an entire unit's x-coordinate history), so I've added three great
//*     methods that attach integer values (mostly for saving/loading a struct) to any handle-based object.
//*     ANY. Units, players, timers, whatever you want.
    
            static method SetAgentData takes agent a,string s,integer data returns nothing
                call .SetAgentData(GetTriggerUnit(), "index string", this )
            
            static method GetAgentData takes agent a,string s returns thistype
                local thistype this = .GetAgentData(GetFilterUnit() ,"index string" )
            
            static method FlushAgentData takes agent a returns nothing
                call .FlushAgentData(bj_lastCreatedGroup)
                
//*     "SetAgentData" attaches an integer value to an agent, which can be further indexed within the agent
//*     by inputting a string value.
//*
//*     "GetAgentData" retrieves the integer value.
//*
//*     "FlushAgentData" -- while this does not necessarily flush all of the agent's saved values, the time
//*     travel system keeps track of how many integers are attached to a given agent, and if that is 0, the
//*     data attached to the agent will be washed out. Please do not accidentally call this twice when it
//*     should have been called once, as that might trigger an unwanted purge of data that could really
//*     cause some annoying results.
//*     
            method destroyHistory takes integer delete returns nothing
                call .destroyHistory(10)
            
            method createHistory takes integer insert,real x,real y,real facing returns nothing
                call .createHistory(10, 1000., -500., 90. )
            
//*     These methods are testy and I suggest you use them only with the purest intentions. As you are
//*     hacking the library's history of your unit with these calls, you could end up with really crazy
//*     results. Due to their obscure nature, I will not elaborate on what they do further than
//*     "destroyHistory" can be used to deallocate data you know you will not need, and "createHistory"
//*     can be used to manually plot a course.
//*     
//*     Interface Methods
//*     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//*     Now here's where the real fun begins! The system-embedded interface creates some events that you
//*     can hack to your own needs.
//*     
            method onStart    takes nothing returns nothing defaults nothing
            //Triggered by the "create" call.
            
            method onFinish   takes nothing returns nothing defaults nothing
            //Triggered by the "destroy" call.
            
            method onCollapse takes nothing returns nothing defaults nothing
            //Triggered when the time-duration assigned to the unit expires -- will not be called if the
            //"override" boolean is set.
            
            method onLoop     takes nothing returns nothing defaults nothing
            //Each loop through history. To seperate forward looping from reverse looping, you will want to
            //add an "if/then/else" block:
                if .is_Forward then
                    //hai :)
                endif
            
            method onLoopEx   takes nothing returns nothing defaults nothing
            //This happens during every loop, too, except it will only happen for *one* time traveller per
            //loop. This is done so that if you want effects to appear during the looping part, you want 
            //them in the "onLoopEx" method because this will prevent effect pileup (so that 100 effects
            //aren't created every 0.04 seconds, this filters it so only 1 effect is created every 0.04
            //seconds.
//*     
//*     
//*     **
//*
//*     I will update this further upon request. Please let me know what areas you need me to explain more.
//*     
//*********************************************************************************************************
Main Library:
Collapse JASS:
library TimeTravel
//*********************************************************************************************************
//*
//*     WarCraft III: Time Travel Library
//*     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//*     Author:     Bribe
//*     Concept:    teuncreemers
//*     Version:    2.0
//*
//*     Note:
//*           Many thanks to Berbanog for letting me use this dynamic layout.
//*
//*     Special Community Recognition:
//*         • WC3Campaigns - For supplying the invaluable Jass New Gen and its components.
//*         • HiveWorkshop - For supporting & encouraging me through learning GUI to vJass.
//*         • TheHelper    - For hosting many tutorials that have helped me tremendously.
//*
//*     Requirements:
//*         • JassHelper 0.A.2.B
//*
//*     Description:
//*         • A powerful utility that disrupts the balance of time!  When the duration expires, the
//*         victims will be thrust backwards; retracing their steps to the start of the cycle :)
//*
//*     Changelog:
//*         • 2.0   A)  Added interface controls so that other structs can extend this parent struct.
//*                 B)  Created example child structs that demonstrate the enormous potential this has.
//*                 C)  Added many methods, method operators and configurable settings that eclipse the
//*                     functionality of all previous versions.
//*                 D)  The system now wants a real value for duration instead of an integer. The value is
//*                     the actual time you want the spell to last. A value of <2.> lasts 2 seconds. Prior
//*                     to this change, it was not clear what to specify for the duration.
//*                 E)  Fog-fade effects are localized to each player; if a player's camera is not in 1000
//*                     range, that player's fog will remain unchanged :)
//*
//*         • 1.00-1.05b
//*                 --  Versions I built and modified before I decided to make this its own system.
//*
//*
//*********************************************************************************************************
 
globals
//************************
//* Configation
//* ¯¯¯¯¯¯¯¯¯¯¯
//* I will add more features to the configuration as per request.
//*
    private     constant real               RVRS_TIMER_SPEED                = 0.04
    private     constant real               FWRD_TIMER_SPEED                = 0.16              //* Must be a multiple of RVRS_TIMER_SPEED.
//*
//* Fog Configuration
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    private     constant boolean            FADE_ON                         = true              //* <false> disables the ENTIRE fog system.
    private     constant real               FADE_DURATION                   = 2.00
    private     constant real               FADE_TIMER_SPEED                = 0.04
 
    private     constant boolean            FADE_FLICKER_ON                 = true
    private     constant real               FADE_FLICKER_MAGNITUDE          = 300.              //* Factors between Z-End + this & Z-End - this.
//*
//* Default Instance Values
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//* I have everything configured for demonstration purposes, but if you find yourself using a common
//* setting that differs from the constant variable, it's more efficient to change the constant variable
//* right here and use inlined settings for the exceptions.
//*
    private     constant boolean            preset___Override               = false
    private     constant boolean            preset___Ambience               = FADE_ON
    private     constant boolean            preset___Pause_Unit             = true
    private     constant boolean            preset___Do_Looping_Ex          = true
//*
//*
endglobals
 
interface timetravelinterface
//***************************
//* Time-Travel Interface
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//* Optional "event-triggered" methods that can be used to implement special effects, damage utilities,
//* data utilities, sound effects -- whatever you like.
//*
//* >> Events that accompany the <create> and <onDestroy> methods:
    method  onStart             takes nothing returns nothing           defaults nothing
    method  onFinish            takes nothing returns nothing           defaults nothing
//*
//* >> When the duration has been reached (right before it starts going backward):
    method  onCollapse          takes nothing returns nothing           defaults nothing
//*
//* >> <onLoop> happens during every loop, whereas <onLoopEx> is "special effects"-friendly & occurs less
//*    frequently depending on how many units are active.  Do not call <destroy> from these.
    method  onLoop              takes nothing returns nothing           defaults nothing
    method  onLoopEx            takes nothing returns nothing           defaults nothing
//*
//*
//*********************************************************************************************************
endinterface
 
globals
//********************
//* Time-Table
//* ¯¯¯¯¯¯¯¯¯¯
//* Here's how I divide the time-table:
//*     Parent keys:  2-D Array that stores each instance.
//*                   Higher keys can be used to attach data to any agent.
//*     Child keys:   Sub-arrays assigned to each instance that store X,Y and UnitFacing values.
//*
    private hashtable           timeTable       = InitHashtable()
//*
//* Internal Settings
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//* In order to make this as user-friendly as possible, I let the user input a real timescale. These
//* variables simply turn user-assigned values into integer values so the system can read them.
//* -------------------------------------------------------------------------------------------------------
    private constant real       duration_I_O    = 1./FWRD_TIMER_SPEED
    private constant integer    forward_Mark    = R2I(FWRD_TIMER_SPEED/RVRS_TIMER_SPEED)
//* -------------------------------------------------------------------------------------------------------
//*
//*
//*********************************************************************************************************
endglobals
    
private struct Fade extends array
//*******************************
//* Fog Utilities
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯
    static integer int      = R2I(FADE_DURATION/FADE_TIMER_SPEED)
    static timer   tim      = null
    static boolean to_Spell = true
    static boolean paused   = true
    static integer dex      = 0
    static integer count    = 0
//*
    real    set_Fog
    boolean player_Want
    integer player_Unit_Count
//*
//* Configurable Fade Values
//* =======================================================================================================
//*     0 and 6     >> Z Start:    0.  to  ????
//*     1 and 7     >> Z End:      0.  to  ????
//*     2 and 8     >> Density:    0.  to  1.
//*     3 and 9     >> Red:        0.  to  1.
//*     4 and 10    >> Green:      0.  to  1.
//*     5 and 11    >> Blue:       0.  to  1.
    private static method InitFadeValues takes nothing returns nothing
//*
//* >> Your fog settings:
        set Fade[ 0 ].set_Fog   =  3000.
        set Fade[ 1 ].set_Fog   =  5000.
        set Fade[ 2 ].set_Fog   =  0.5
        set Fade[ 3 ].set_Fog   =  1.
        set Fade[ 4 ].set_Fog   =  1.
        set Fade[ 5 ].set_Fog   =  1.
//*
//* >> The spell's fog settings:
        set Fade[ 6 ].set_Fog   =  0.
        set Fade[ 7 ].set_Fog   =  3000.
        set Fade[ 8 ].set_Fog   =  0.125
        set Fade[ 9 ].set_Fog   =  0.2
        set Fade[ 10 ].set_Fog  =  0.
        set Fade[ 11 ].set_Fog  =  0.35
    endmethod
//*
    private static method onInit takes nothing returns nothing
        local integer i=0
        static if FADE_ON then
            set Fade.tim=CreateTimer()
            call InitFadeValues()
            loop
                set Fade[i+6].set_Fog=(Fade[i].set_Fog-Fade[i+6].set_Fog)/Fade.int
                exitwhen i==5
                set i=i+1
            endloop
        endif
    endmethod
//*
//*
endstruct
//*
//* Fade Sequence
//* =======================================================================================================
    private function doFade takes nothing returns nothing
        local integer i=0
        if (Fade.to_Spell) then
            loop
                set Fade[i].set_Fog=Fade[i].set_Fog-Fade[i+6].set_Fog
                exitwhen i==5
                set i=i+1
            endloop
            set Fade.dex=Fade.dex+1
            if Fade.dex>=Fade.int then
                set Fade.dex=Fade.int
                set Fade.paused=true
                call PauseTimer(Fade.tim)
            endif
        else
            loop
                set Fade[i].set_Fog=Fade[i].set_Fog+Fade[i+6].set_Fog
                exitwhen i==5
                set i=i+1
            endloop
            set Fade.dex=Fade.dex-1
            if Fade.dex<=0 then
                set Fade.dex=0
                set Fade.paused=true
                call PauseTimer(Fade.tim)
            endif
        endif
        call SetTerrainFogEx(0,Fade[0].set_Fog,Fade[1].set_Fog,Fade[2].set_Fog,Fade[3].set_Fog,Fade[4].set_Fog,Fade[5].set_Fog)
    endfunction
 
native UnitAlive takes unit id returns boolean
 
struct timetravel extends timetravelinterface
//*******************************************
//* Time Travel
//* ¯¯¯¯¯¯¯¯¯¯¯
//* The more obscure or protected components I've set to <private> so you can't reference them. <public>
//* components I encourage you to use liberally.  Readonly components are mostly for calculations or for
//* reference, although the important ones can be modified by setting the linked <method operator>.
//*
//* #######################################################################################################
//*
//* Static Components
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    private static  integer         cacheIndex      = 0
    private static  thistype array  cacheStack
 
    private static  timer           loopTimer       = CreateTimer()
    private static  boolean array   reserved
 
    private static  integer         fx_Dex          = 0
    private static  thistype array  fx_Cache
//*
//* Dynamic Components
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    public      boolean             override        = preset___Override
    public      real                startx
    public      real                starty
 
    public      boolean             wantDestroy     = false     //* Setting this to <true> is the *only* way to destroy the instance from the
                                                                //  <onLoop> method. Calling <destroy> from that method will cause an error.
//*
    private     integer             saveDex         = 0         //* Hashtable save-point per loop.
    private     integer             realDex                     //* The private index of the unit.
    private     integer             tableDex                    //* The private hashtable index of the unit.
    private     integer             fx_Local_Dex    = 0         //* For stack-and-pop use with fx_Caches.
//*
    private     integer             diff_Delay      = 0         //* Regulates the difference between forward & reverse times.
//*
//* Method Operators **
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//* Many of the components of this spell require more than one action to be performed in order to truly make
//* the change, otherwise there will be errors.  I have incorporated several operators that apply necessary
//* setting & variable modifications for you.  Below is the list of operator methods and their corresponding
//* variables -- feel free to use any operator as if it were the actual variable.
//*
//  public   unit operator          victim             Because the fog is specific to each player, the system needs to be notified of change
    readonly unit                   get_Victim       //of ownership of this unit, so it must be set using <victim>.
 
//  public   player operator        extraPlayer        For spells where you want both the target's owner and the caster's owner to see fog
    readonly player                 get_Extra_Player //effects, use <set extraPlayer = GetOwningPlayer($caster$)>
 
//  public   real operator          timeValue          The duration of forward-mode until it collapses into reverse-mode.  The input value
    private  integer                sys_Duration     //will be *added* to the duration.  Negative values are also accepted, but if the value
//                                                     is too low the destroy method will be called and the unit will not travel back through
//                                                     time (so it can be used to cancel a spell).  To prevent this, do a real comparison to
//                                                     check if <timeValue> is higher than the value you're subtracting.
//                                                     ** If the unit is in reverse & duration is added, it will switch back to forward-mode.
 
//  public   boolean operator       forward            = true
    readonly boolean                is_Forward         = true
 
//  public   boolean operator       paused             = preset___Pause_Unit
    readonly boolean                is_Paused          = preset___Pause_Unit
 
//  public   boolean operator       wantAmbience       If the fog will change because of this unit.
    readonly boolean                haveAmbience
 
//  public   boolean operator       wantOnLoopEx       Let me explain this one: I have a filter that regulates how often <onLoopEx> is called;
    readonly boolean                haveOnLoopEx     //only one <onLoopEx> will be called per loop so effects don't pile up.  The system will
//                                                     use this boolean to filter out only units that want special effects; so if there are
//                                                     100 units that don't want <onLoopEx> and one unit that does, that one unit will have
//                                                     all of his effects appear, instead of only appearing 1/101 times :)
//*
//* #######################################################################################################
//*
//* Sub-System: Agent-Data Functions
//* =======================================================================================================
//* Instead of requiring more libraries, this indexing system is inlined and makes additional use of the
//* Time-Table.  Any handle-based object can be assigned any integer, and is backed-up by a second indexer;
//* this way, indexes can be easily saved and loaded without bumping into each other (and have names).
//*
    static method GetAgentData takes agent a,string s returns thistype
        return LoadInteger(timeTable,GetHandleId(a),StringHash(s))
    endmethod
    static method SetAgentData takes agent a,string s,integer data returns nothing
        local integer id=GetHandleId(a)
        if id>0 then
            call SaveInteger(timeTable,id,1,LoadInteger(timeTable,id,1)+1)
            call SaveInteger(timeTable,id,StringHash(s),data)
        else
            debug call BJDebugMsg("Error: cannot save data to null agent.")
        endif
    endmethod
    static method FlushAgentData takes agent a returns nothing
        local integer id=GetHandleId(a)
        local integer flushCount=LoadInteger(timeTable,id,1)-1
        if id>0 then
            if flushCount<=0 then
                call FlushChildHashtable(timeTable,id)
            else
                call SaveInteger(timeTable,id,1,flushCount)
            endif
        else
            debug call BJDebugMsg("Error: cannot flush data from null agent.")
        endif
    endmethod
//*
//* Custom History
//* =======================================================================================================
    method destroyHistory takes integer delete returns nothing
        call RemoveSavedReal(timeTable,.tableDex*3,delete)
        call RemoveSavedReal(timeTable,.tableDex*3+1,delete)
        call RemoveSavedReal(timeTable,.tableDex*3+2,delete)
    endmethod
    method createHistory takes integer insert,real x,real y,real facing returns nothing
        call SaveReal(timeTable,.tableDex*3,insert,x)
        call SaveReal(timeTable,.tableDex*3+1,insert,y)
        call SaveReal(timeTable,.tableDex*3+2,insert,facing)
    endmethod
//*
//* Add Extra Player <player operator extraPlayer>
//* =======================================================================================================
    method operator extraPlayer takes nothing returns player
        return .get_Extra_Player
    endmethod
    method operator extraPlayer= takes player p returns nothing
        local integer i=GetPlayerId(.get_Extra_Player)
        local integer j=GetPlayerId(p)
        local boolean b=(.get_Extra_Player!=p)
        if p!=null and b then
            set Fade[j].player_Want=true
            set Fade[j].player_Unit_Count=Fade[j].player_Unit_Count+1
        endif
        if .get_Extra_Player!=null and b then
            set Fade[i].player_Unit_Count=Fade[i].player_Unit_Count-1
            if Fade[i].player_Unit_Count==0 then
                set Fade[i].player_Want=false
            endif
        endif
        set .get_Extra_Player=p
    endmethod
//*
//* Pause/Unpause Unit <boolean operator pause>
//* =======================================================================================================
    method operator paused takes nothing returns boolean
        return .is_Paused
    endmethod
    method operator paused= takes boolean b returns nothing
        if not .is_Forward then
            call SetUnitPathing(.get_Victim,not b)
        endif
        set .is_Paused=b
    endmethod
//*
//* Change Victim <unit operator victim>
//* =======================================================================================================
    method operator victim takes nothing returns unit
        return .get_Victim
    endmethod
    method operator victim= takes unit u returns nothing
        local integer i=GetPlayerId(GetOwningPlayer(.get_Victim))
        local integer j=GetPlayerId(GetOwningPlayer(u))
        local boolean b=(.get_Victim!=u)
        if u!=null and b then
            set Fade[j].player_Want=true
            set Fade[j].player_Unit_Count=Fade[j].player_Unit_Count+1
        endif
        if .get_Victim!=null and b then
            if not .is_Forward and .is_Paused then
                call SetUnitPathing(.get_Victim,true)
            endif
            set Fade[i].player_Unit_Count=Fade[i].player_Unit_Count-1
            if Fade[i].player_Unit_Count==0 then
                set Fade[i].player_Want=false
            endif
        endif
        set .get_Victim = u
        set .paused=.is_Paused
        set .wantAmbience=.haveAmbience
    endmethod
//*
//* Change "Going Forward" <boolean operator forward>
//* =======================================================================================================
    method operator forward takes nothing returns boolean
        return .is_Forward
    endmethod
    method operator forward= takes boolean b returns nothing
        if b and not .is_Forward then
            if (.is_Paused) then
                call SetUnitPathing(.get_Victim,true)
                call SetUnitPosition(.get_Victim,GetWidgetX(.get_Victim),GetWidgetY(.get_Victim))
            endif
        elseif not b and .is_Forward then
            call .onCollapse()
            if (.is_Paused) then
                call SetUnitPathing(.get_Victim,false)
            endif
        endif
        set .is_Forward=b
    endmethod
//*
//* Add Duration <real operator timeValue>
//* =======================================================================================================
    method operator timeValue takes nothing returns real
        return I2R(.sys_Duration-.saveDex)/duration_I_O
    endmethod
    method operator timeValue= takes real v returns nothing
        local integer m=R2I(v*duration_I_O)
        local integer n=.sys_Duration-m
        if .is_Forward then
            if (v<0.) then
                if -m>(.sys_Duration-.saveDex) then
                    call .destroy()
                    return
                endif
            endif
        else
            set .forward=true
        endif
        set .sys_Duration=.sys_Duration+m
    endmethod
//*
//* Fog Manipulation <boolean operator wantAmbience>
//* =======================================================================================================
    private static method camCheck takes real ox,real oy returns boolean
        local real x=GetCameraTargetPositionX()
        local real y=GetCameraTargetPositionY()
        return x>=ox-1000. and x<=ox+1000. and y>=oy-1000. and y<=oy+1000.
    endmethod
//*
    method operator wantAmbience takes nothing returns boolean
        return .haveAmbience
    endmethod
    method operator wantAmbience= takes boolean want returns nothing
        local boolean b=false
        static if FADE_ON then
            if want and not .haveAmbience then
                if Fade.count==0 and camCheck(.startx,.starty) then
                    set b=true
                endif
                set Fade.count=Fade.count+1
            elseif not want and .haveAmbience then
                set Fade.count=Fade.count-1
                if Fade.count==0 then
                    set b=true
                endif
            endif
            if b and Fade[GetPlayerId(GetLocalPlayer())].player_Want then
                if (Fade.paused) then
                    call TimerStart(Fade.tim,FADE_TIMER_SPEED,true,function doFade)
                    set Fade.paused=false
                endif
                set Fade.to_Spell=want
            endif
        endif
        set .haveAmbience=want
    endmethod
//*
//* Want onLoop Extra Method <boolean operator wantOnLoopEx>
//* =======================================================================================================
    method operator wantOnLoopEx takes nothing returns boolean
        return .haveOnLoopEx
    endmethod
    method operator wantOnLoopEx= takes boolean want returns nothing
        if want and not .haveOnLoopEx then
            set .fx_Local_Dex=.fx_Dex
            set .fx_Cache[.fx_Dex]=this
            set .fx_Dex=.fx_Dex+1
        elseif not want and .haveOnLoopEx then
            set .fx_Dex=.fx_Dex-1
            if (.fx_Dex>0) then
                set .fx_Cache[.fx_Local_Dex]=.fx_Cache[.fx_Dex]
            endif
        endif
        set .haveOnLoopEx = want
    endmethod
//*
//* Destructor
//* =======================================================================================================
    private method onDestroy takes nothing returns nothing
        call FlushChildHashtable(timeTable,.tableDex*3)
        call FlushChildHashtable(timeTable,.tableDex*3+1)
        call FlushChildHashtable(timeTable,.tableDex*3+2)
        set .reserved[.tableDex]=false
 
        call .onFinish()
        set .forward        = true  // boolean operator
        set .wantAmbience   = false // boolean operator
        set .wantOnLoopEx   = false // boolean operator
        set .victim         = null  // unit operator
        set .extraPlayer    = null  // player operator
 
        set .cacheIndex=.cacheIndex-1
        if (.cacheIndex==0) then
            call PauseTimer(.loopTimer)
        else
            set .cacheStack[.realDex]=.cacheStack[.cacheIndex]
            set .cacheStack[.realDex].realDex=.realDex
        endif
    endmethod
//*
//* Time Machine
//* =======================================================================================================
    private static method doLoop takes nothing returns nothing
        local boolean toDestroy=false
        local boolean do
        local thistype flag=.fx_Cache[GetRandomInt(0,.fx_Dex-1)]
        local thistype w
        local integer i=0
        loop
            set w=.cacheStack[i]
            set do=false
            if (w.is_Forward) then
//*
//* Forward
//*
                set w.diff_Delay=w.diff_Delay+1
                if w.diff_Delay==forward_Mark then
                    set do=true
                    set w.diff_Delay=0
                    set w.saveDex=w.saveDex+1
                    call SaveReal(timeTable,w.tableDex*3,w.saveDex,GetWidgetX(w.get_Victim))
                    call SaveReal(timeTable,w.tableDex*3+1,w.saveDex,GetWidgetY(w.get_Victim))
                    call SaveReal(timeTable,w.tableDex*3+2,w.saveDex,GetUnitFacing(w.get_Victim))
                    if w.saveDex>=w.sys_Duration and not w.override then
                        set w.forward=false
                    endif
                endif
            else
//*
//* Reverse
//*
                set do=true
                if HaveSavedReal(timeTable,w.tableDex*3,w.saveDex) then
                    if (w.is_Paused) then
                        call SetUnitPosition(w.get_Victim,LoadReal(timeTable,w.tableDex*3,w.saveDex),LoadReal(timeTable,w.tableDex*3+1,w.saveDex))
                    else
                        call SetUnitX(w.get_Victim,LoadReal(timeTable,w.tableDex*3,w.saveDex))
                        call SetUnitY(w.get_Victim,LoadReal(timeTable,w.tableDex*3+1,w.saveDex))
                    endif
                    call SetUnitFacing(w.get_Victim,LoadReal(timeTable,w.tableDex*3+2,w.saveDex))
                endif
                set w.saveDex=w.saveDex-1
                if w.saveDex<=0 and not w.override then
                    set toDestroy=true
                endif
            endif
//*
//* Shared
//*
            if (do) then
                call w.onLoop()
                if (flag==w) then
                    call w.onLoopEx()
                endif
            endif
            if toDestroy or w.wantDestroy or not UnitAlive(w.get_Victim) then
                if w.reserved[w.tableDex] then
                    call w.destroy()
                endif
                set toDestroy=false
            else
                set i=i+1
            endif
            exitwhen i>=.cacheIndex
        endloop
        static if FADE_ON and FADE_FLICKER_ON then
            if (Fade.count>0) then
                if Fade[GetPlayerId(GetLocalPlayer())].player_Want then
                    if GetRandomReal(0.,2.)<=RVRS_TIMER_SPEED then
                        call SetTerrainFogEx(0,Fade[0].set_Fog,GetRandomReal(Fade[1].set_Fog-FADE_FLICKER_MAGNITUDE,Fade[1].set_Fog+FADE_FLICKER_MAGNITUDE),Fade[2].set_Fog,Fade[3].set_Fog,Fade[4].set_Fog,Fade[5].set_Fog)
                    endif
                endif
            endif
        endif
    endmethod
//*
//* Creation
//* =======================================================================================================
    static method create takes unit whichUnit,real duration returns thistype
        local thistype w        = thistype.allocate()
 
        set w.victim            = whichUnit
        set w.startx            = GetWidgetX(whichUnit)
        set w.starty            = GetWidgetY(whichUnit)
        set w.sys_Duration      = R2I(duration*duration_I_O)
 
        set w.wantAmbience      = preset___Ambience
        set w.wantOnLoopEx      = preset___Do_Looping_Ex
//* >>
        set w.realDex           = .cacheIndex
        set w.tableDex          = .cacheIndex
        loop
            exitwhen .reserved[w.tableDex]==null
            exitwhen not .reserved[w.tableDex]
            set w.tableDex=w.tableDex+1
        endloop
        set .reserved[w.tableDex]=true
//* >>
        if (.cacheIndex==0) then
            call TimerStart(.loopTimer,RVRS_TIMER_SPEED,true,function thistype.doLoop)
        endif
        set .cacheStack[.cacheIndex]=w
        set .cacheIndex=.cacheIndex+1
 
        call w.onStart()
        return w
    endmethod
//*
//*
//*********************************************************************************************************
endstruct
 
endlibrary
Pre-made templates for extending Time Travel:
Time Warp:
Collapse JASS:
library TimeWarp requires TimeTravel,GroupUtils
 
private struct filt extends array
    static integer dex = 0
    static real    Real
    static unit    Unit
    static unit    Caster
    static player  Play
    static integer Int
    group grp
    unit  cast
endstruct
 
struct timewarp extends timetravel
//********************************
//* Time Warp
//* ¯¯¯¯¯¯¯¯¯
    static constant integer SPELL_ID = 'A003'
    static constant integer EXTRA_ID = 'A001'
    
    real    damage
    unit    caster
    effect  return_Fx
    effect  attach_Fx
//*
//* End of Spell:
//* =======================================================================================================
    method onFinish takes nothing returns nothing
        call DestroyEffect(.return_Fx)
        call DestroyEffect(.attach_Fx)
        call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Items\\WandOfNeutralization\\NeutralizationMissile.mdl",.get_Victim,"origin"))
        
        call GroupRemoveUnit(filt[GetAgentData(.caster,"time warp")].grp,.get_Victim)
        call FlushAgentData(.get_Victim)
        call SetUnitTimeScale(.get_Victim,1.)
        call SetUnitVertexColor(.get_Victim,255,255,255,255)
    endmethod
//*
//* Portal-Collapse Behavior:
//* =======================================================================================================
    method onCollapse takes nothing returns nothing
        call SetUnitTimeScale(.get_Victim,4.)
        call SetUnitVertexColor(.get_Victim,175,150,155,115)
 
        call DestroyEffect(.attach_Fx)
        call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\Monsoon\\MonsoonBoltTarget.mdl",.get_Victim,"origin"))
        set .attach_Fx = AddSpecialEffectTarget("Abilities\\Spells\\Undead\\Possession\\PossessionMissile.mdl",.get_Victim,"overhead")
    endmethod
//*
//* Looping Behavior
//* =======================================================================================================
    method onLoopEx takes nothing returns nothing
        if (.is_Forward) then
            call DestroyEffect(AddSpecialEffectTarget("Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl",.get_Victim,"origin"))
        else
            call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\Transmute\\GoldBottleMissile.mdl",.get_Victim,"origin"))
            call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\StrongDrink\\BrewmasterMissile.mdl",.get_Victim,"origin"))
            call UnitDamageTarget(.caster,.get_Victim,.damage,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
        endif
    endmethod
//*
//* Configurable Filter
//* =======================================================================================================
    static method GroupFilter takes nothing returns boolean
        local thistype dat
        set filt.Unit=GetFilterUnit()
        if IsUnitInGroup(filt.Unit,filt[filt.Int].grp) then
            set GetAgentData(filt.Unit,"time warp unit").timeValue=filt.Real
        elseif IsUnitEnemy(filt.Unit,filt.Play) and not IsUnitType(filt.Unit,UNIT_TYPE_STRUCTURE) and UnitAlive(filt.Unit) then
            set dat             = .create(filt.Unit,filt.Real)
            set dat.extraPlayer = filt.Play
            set dat.caster      = filt.Caster
            set dat.damage      = filt.Real
            set dat.attach_Fx   = AddSpecialEffectTarget("Abilities\\Spells\\NightElf\\shadowstrike\\shadowstrike.mdl",filt.Unit,"overhead")
            set dat.return_Fx   = AddSpecialEffect("Abilities\\Spells\\Human\\MagicSentry\\MagicSentryCaster.mdl",dat.startx,dat.starty)
 
            call GroupAddUnit(filt[filt.Int].grp,filt.Unit)
            call SetAgentData(filt.Unit,"time warp unit",dat)
        endif
        return false
    endmethod
//*
//* Open Portal
//* =======================================================================================================
    static method OpenPortal takes unit u returns nothing
        local real factor = GetUnitAbilityLevel(u,SPELL_ID)-1.
        local real x      = GetSpellTargetX()
        local real y      = GetSpellTargetY()
 
        set filt.Play     = GetTriggerPlayer()
        set filt.Real     = factor*1.+4.
        set filt.Caster   = u
        set filt.Int      = GetAgentData(u,"time warp")
 
        call GroupEnumUnitsInRange(ENUM_GROUP,x,y,factor*50.+150.,Filter(function thistype.GroupFilter))
        call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl",x,y))
    endmethod
//*
//* Early-Collapse Method
//* =======================================================================================================
//* All of the caster's targets immediately enter reverse if they are in forward-mode:
//*
    static method CollapseEach takes nothing returns nothing
        set GetAgentData(GetEnumUnit(),"time warp unit").forward=false
    endmethod
//*
//* Conditions
//* =======================================================================================================
    static method Conditions takes nothing returns boolean
        local integer id=GetSpellAbilityId()
        if id==SPELL_ID then
            call .OpenPortal(GetTriggerUnit())
        elseif id==EXTRA_ID then
            call ForGroup(filt[GetAgentData(GetTriggerUnit(),"time warp")].grp,function thistype.CollapseEach)
        endif
        return false
    endmethod
//*
//* Add Bonus Skill
//* =======================================================================================================
    static method NewSkill takes nothing returns boolean
        if GetLearnedSkill()==SPELL_ID and GetLearnedSkillLevel()==1 then
            set filt[filt.dex].cast=GetTriggerUnit()
            set filt[filt.dex].grp=NewGroup()
 
            call SetAgentData(filt[filt.dex].cast,"time warp",filt.dex)
            call UnitAddAbility(filt[filt.dex].cast,EXTRA_ID)
            set filt.dex=filt.dex+1
        endif
        return false
    endmethod
//*
//*
//* =======================================================================================================
    static method CasterDeath takes nothing returns boolean
        local integer data=GetAgentData(GetTriggerUnit(),"time warp")
        if data>0 then
            call ReleaseGroup(filt[data].grp)
            set filt.dex=filt.dex-1
            if filt.dex>0 then
                call SetAgentData(filt[filt.dex].cast,"time warp",data)
                set filt[data].cast=filt[filt.dex].cast
                set filt[data].grp=filt[filt.dex].grp
            endif
        endif
        call FlushAgentData(GetTriggerUnit())
        return false
    endmethod
//*
//*
//* Initializer
//* =======================================================================================================
    static method onInit takes nothing returns nothing
        local trigger t=CreateTrigger()
        call TriggerAddCondition(t,Condition(function thistype.NewSkill))
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_HERO_SKILL)
 
        set t=CreateTrigger()
        call TriggerAddCondition(t,Condition(function thistype.CasterDeath))
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_DEATH)
 
        set t=CreateTrigger()
        call TriggerAddCondition(t,Condition(function thistype.Conditions))
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    endmethod
//*
//*
//*********************************************************************************************************
endstruct
 
endlibrary
All Time
Collapse JASS:
library AllTime requires TimeTravel,GroupUtils
 
struct alltime extends timetravel
//*******************************
//* The way this module works is that it grabs every non-structure unit on the map and turns it into a time
//* traveller.  Each time that time traveller attacks, it is thrown backwards through time :P
//* You can use this as a template to do "instant" time travel.
//*
    static constant integer time_Limit = 40
//*
    effect  attach_Fx = null
    integer countdown = 0
//*
//* onLoop Extra Method
//* =======================================================================================================
    method onLoopEx takes nothing returns nothing
        if not .is_Forward then
            call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\Transmute\\GoldBottleMissile.mdl",.get_Victim,"origin"))
            call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\StrongDrink\\BrewmasterMissile.mdl",.get_Victim,"origin"))
        endif
    endmethod
//*
//* onLoop Behavior
//* =======================================================================================================
    method onLoop takes nothing returns nothing
        if (.is_Forward) then
            if .countdown<time_Limit then
                set .countdown=.countdown+1
            else
                if .countdown>time_Limit then
                    set .countdown=time_Limit
                endif
                call .destroyHistory(.countdown-time_Limit-1)
            endif
        else
            set .countdown=.countdown-1
            if (.countdown<=0) then
                set .forward=true
                set .wantOnLoopEx=false
                call DestroyEffect(.attach_Fx)
                call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Items\\WandOfNeutralization\\NeutralizationMissile.mdl",.get_Victim,"origin"))
            endif
        endif
    endmethod
//*
//* On-Finish Method
//* =======================================================================================================
    method onFinish takes nothing returns nothing
        call DestroyEffect(.attach_Fx)
        call FlushAgentData(.get_Victim)
    endmethod
//*
//* Add All Units
//* =======================================================================================================
    static method addUnit takes unit u returns nothing
        local thistype dat
        if not IsUnitType(u,UNIT_TYPE_STRUCTURE) then
            set dat              = .create(u,1.)
            set dat.override     = true
            set dat.wantOnLoopEx = false
            set dat.wantAmbience = false
            call SetAgentData(u,"all time",dat)
        endif
    endmethod
    static method doEnum takes nothing returns boolean
        call .addUnit(GetFilterUnit())
        return false
    endmethod
    static method addNew takes nothing returns boolean
        if GetAgentData(GetTriggerUnit(),"all time")==0 then
            call .addUnit(GetTriggerUnit())
        endif
        return false
    endmethod
//*
//* Time Travel Instant-Launch
//* =======================================================================================================
    static method onSelection takes nothing returns boolean
        local thistype dat=GetAgentData(GetTriggerUnit(),"all time")
        if dat>0 then
            if dat.is_Forward and dat.countdown>=10 then
                set dat.forward=false
                set dat.wantOnLoopEx=true
                set dat.attach_Fx=AddSpecialEffectTarget("Abilities\\Spells\\Other\\Drain\\ManaDrainTarget.mdl",dat.get_Victim,"overhead")
                call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\Invisibility\\InvisibilityTarget.mdl",GetWidgetX(dat.get_Victim),GetWidgetY(dat.get_Victim)))
            endif
        endif
        return false
    endmethod
//*
//*
//* Initialization
//* =======================================================================================================
    static method onInit takes nothing returns nothing
        local trigger t=CreateTrigger()
        local integer i=0
        call TriggerAddCondition(t,Condition(function thistype.addNew))
        call TriggerRegisterEnterRectSimple(t,bj_mapInitialPlayableArea)
        call GroupEnumUnitsInRect(ENUM_GROUP,bj_mapInitialPlayableArea,Filter(function thistype.doEnum))
 
        set t=CreateTrigger()
        call TriggerAddCondition(t,Condition(function thistype.onSelection))
        loop
            call TriggerRegisterPlayerUnitEvent(t,Player(0), EVENT_PLAYER_UNIT_SELECTED, null)
            exitwhen i==11
            set i=i+1
        endloop
    endmethod
 
endstruct
 
endlibrary
    
Attached Images
File type: jpgspells_3355_screenshot.jpg (82.0 KB)
Attached Files
File type: w3xTimeWarp.w3x (64.1 KB)
05-27-2010, 03:43 PM#2
Rising_Dusk
Are you actually submitting this or just posting it as a demo? I ask because you've posted it in both forums, and depending upon your intent, it belongs in only one of them.
05-27-2010, 04:01 PM#3
Bribe
This is a system submission, I posted it in the wrong forum yesterday.