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

Time Travel - WC3

Status
Not open for further replies.
Level 3
Joined
Oct 28, 2009
Messages
35
I've been thinking of making a time traveling devise in WC3. There would be a waygate with an ability to set the time traveled (into the past) and when the unit walked into the waygate he would find himself at the spot he was in [waygate ability amount] seconds ago, with the same health and stats, doing the same animation, attacking the same unit, walking to the same place (etc.) as he was that many seconds ago. All the units and trees and destructibles and triggers [everything] should be as it was at the time. Any ideas on how to do this? I know I could do it myself - but I'm lazy and want to see if you can figure out how.
 
Level 3
Joined
Oct 28, 2009
Messages
35
If nobody helped eachother on THW it woulden't still be up and running...
I can do it myself easily, it will just take a while...
I also wanted to see what all of you thaught about the idea and if you wanted to help...
I am not "noob"...
 
Level 10
Joined
Aug 15, 2008
Messages
720
Only way I could see it doing is to attach variables to everything and use either JASS locals and all that stuff or Hashtables and as baassee mentioned it could cause more bad than more good.

Also why this is in this section? It is obviously seen that you need to trigger/script in this thread, so why this is not in Trigger/Script section?
 
Last edited by a moderator:
My Opinion: forget it. It's practical impossible to do.
Why? You create a variable or something else EVERY second and save almost the entire map inside.
If you want to travel through time make the user save the map at some points before and tell him to load those.

My Idea: same as those above.

I would like to hear YOUR solution since you
I know I could do it myself - but I'm lazy and want to see if you can figure out how.
already know how to do it.
 
Level 9
Joined
Nov 28, 2008
Messages
704
Impossible? It wouldn't be that difficult at all, just ridiculously inefficient.

Make a trigger for when a unit enters the map. Loop, save his position, health, etc. Wait 1 second. Repeat (after checking if the unit is removed or alive - if so, clear hashtable, exit loop).

Pretty freaking simple. You can then load from your hash table whenever you wanted a second ago.

That's the *basic concept*. If you want a variable amount of time before, just do slightly more complicated array stuff.

This wouldn't be that bad at all if you were to do it for just a couple units - in which case, make it a function.

But doing it for every unit on the freaking map is ridiculous.
 
Level 9
Joined
Nov 28, 2008
Messages
704
Do you want to reset just one unit, or *every* unit? Both are well within the limits of JASS. It might lag the map to hell (then again - it might not), but I could make the script quite easily.
 
Level 12
Joined
Dec 10, 2008
Messages
850
I'd label it "impossible" becuase it breaks all the rules of even THINKING about triggering. You'd need to save ALOT of things EVERY second, things like:
HP
Mana
Location
Items
Doodads
Cooldowns
Research
Number of units
Unit Que
Items on the ground
Alot more
 
Level 9
Joined
Nov 28, 2008
Messages
704
You can't save all of those Cool. But you can save most.

Cooldowns is a no. Current order, no. Scaling, tinting, no. Research, no.

The rest should be easy.

Anyways, here is my basic time travel system.

In:

JASS:
library TimeTravel initializer Init:

    #define private StringHashS(name) = StringHash(name + I2S(Seconds))
    #define private StringHashT(name, seconds) = StringHash(name + I2S(seconds))

    globals:
        private hashtable Hash
        private group EveryUnit
        private timer SecondCounter
        public int Seconds = 0
        
    private function IsUnitDead takes unit u returns boolean //Link: [url]http://www.hiveworkshop.com/forums/jass-functions-413/isunitdead-135811/[/url]
      return IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0
    endfunction
    
    private function SaveUnit takes unit U returns nothing:
        int ID = GetHandleId(U)
        if IsUnitDead(U):
            SaveBoolean(Hash, ID, StringHashS("dead"), true)
        else:
            SaveBoolean(Hash, ID, StringHashS("dead"), false)
            SaveReal(Hash, ID, StringHashS("x"), GetUnitX(U))
            SaveReal(Hash, ID, StringHashS("y"), GetUnitY(U))
        
    public function RegisterUnit takes unit U returns nothing:
        SaveInteger(Hash, GetHandleId(U), StringHash("existed"), Seconds)
        GroupAddUnit(EveryUnit, U)
        SaveUnit(U)
        
    private function SaveUnitPre takes void returns void:
        SaveUnit(GetEnumUnit())
            
    private function CountSecond takes nothing returns nothing:
        Seconds++
        ForGroup(EveryUnit, function SaveUnitPre)
    
    public function ResetUnit takes unit U, int ToWhen returns nothing:
        int ID = GetHandleId(U)
        int Existed = LoadInteger(Hash, ID, StringHash("existed"))
        if Existed > ToWhen:
            //The unit did not exist at this time. How sad.
            RemoveUnit(U)
            return
        if LoadBoolean(Hash, ID, StringHashT("dead", ToWhen)):
            KillUnit(U)
        SetUnitX(U, LoadReal(Hash, ID, StringHashT("x", ToWhen)))
        SetUnitY(U, LoadReal(Hash, ID, StringHashT("y", ToWhen)))
        
    private function ResetUnitPre takes nothing returns nothing:
        ResetUnit(GetEnumUnit(), Seconds)
        
    public function TimeTravelBack takes int HowFar returns nothing:
        Seconds = Seconds - HowFar
        ForGroup(EveryUnit, function ResetUnitPre)
    
    private function Init takes void returns void:
        Hash = InitHashtable()
        SecondCounter = CreateTimer()
        EveryUnit = CreateGroup()
        TimerStart(SecondCounter, 1.0, true, function CountSecond)

JASS:
library TimeTravel initializer Init
    #define private StringHashS(name) = StringHash(name + I2S(Seconds))
    #define private StringHashT(name, seconds) = StringHash(name + I2S(seconds))
    globals
        private hashtable Hash
        private group EveryUnit
        private timer SecondCounter
        public int Seconds = 0
    endglobals
    private function IsUnitDead takes unit u returns boolean 
      return IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0
    endfunction
    private function SaveUnit takes unit U returns nothing
        int ID = GetHandleId(U)
        if IsUnitDead(U) then
            SaveBoolean(Hash, ID, StringHashS("dead"), true)
        else
            SaveBoolean(Hash, ID, StringHashS("dead"), false)
            SaveReal(Hash, ID, StringHashS("x"), GetUnitX(U))
            SaveReal(Hash, ID, StringHashS("y"), GetUnitY(U))
        endif
    endfunction
    public function RegisterUnit takes unit U returns nothing
        SaveInteger(Hash, GetHandleId(U), StringHash("existed"), Seconds)
        GroupAddUnit(EveryUnit, U)
        SaveUnit(U)
    endfunction
    private function SaveUnitPre takes void returns void
        SaveUnit(GetEnumUnit())
    endfunction
    private function CountSecond takes nothing returns nothing
        Seconds++
        ForGroup(EveryUnit, function SaveUnitPre)
    endfunction
    public function ResetUnit takes unit U, int ToWhen returns nothing
        int ID = GetHandleId(U)
        int Existed = LoadInteger(Hash, ID, StringHash("existed"))
        if Existed > ToWhen then
            RemoveUnit(U)
            return
        endif
        if LoadBoolean(Hash, ID, StringHashT("dead", ToWhen)) then
            KillUnit(U)
        endif
        SetUnitX(U, LoadReal(Hash, ID, StringHashT("x", ToWhen)))
        SetUnitY(U, LoadReal(Hash, ID, StringHashT("y", ToWhen)))
    endfunction
    private function ResetUnitPre takes nothing returns nothing
        ResetUnit(GetEnumUnit(), Seconds)
    endfunction
    public function TimeTravelBack takes int HowFar returns nothing
        Seconds = Seconds - HowFar
        ForGroup(EveryUnit, function ResetUnitPre)
    endfunction
    private function Init takes void returns void
        Hash = InitHashtable()
        SecondCounter = CreateTimer()
        EveryUnit = CreateGroup()
        TimerStart(SecondCounter, 1.0, true, function CountSecond)
    endfunction
endlibrary

JASS:
library TimeTravel initializer Init
globals
private hashtable Hash
private group EveryUnit
private timer SecondCounter
public integer Seconds=0
endglobals
private function IsUnitDead takes unit u returns boolean
return         IsUnitType(u,UNIT_TYPE_DEAD)or GetUnitTypeId(u)==0
endfunction
private function SaveUnit takes unit U returns nothing
local integer ID=GetHandleId(U)
if             IsUnitDead(U)then
call SaveBoolean(Hash,ID,StringHash("dead"+I2S(Seconds)),true)
else
call SaveBoolean(Hash,ID,StringHash("dead"+I2S(Seconds)),false)
call SaveReal(Hash,ID,StringHash("x"+I2S(Seconds)),GetUnitX(U))
call SaveReal(Hash,ID,StringHash("y"+I2S(Seconds)),GetUnitY(U))
endif
endfunction
public function RegisterUnit takes unit U returns nothing
call SaveInteger(Hash,GetHandleId(U),StringHash("existed"),Seconds)
call GroupAddUnit(EveryUnit,U)
call SaveUnit(U)
endfunction
private function SaveUnitPre takes nothing returns nothing
call SaveUnit(GetEnumUnit())
endfunction
private function CountSecond takes nothing returns nothing
set Seconds=Seconds+1
call ForGroup(EveryUnit,function SaveUnitPre)
endfunction
public function ResetUnit takes unit U,integer ToWhen returns nothing
local integer ID=GetHandleId(U)
local integer Existed=LoadInteger(Hash,ID,StringHash("existed"))
if             Existed>ToWhen then
call RemoveUnit(U)
return        
endif
if             LoadBoolean(Hash,ID,StringHash("dead"+I2S(ToWhen)))then
call KillUnit(U)
endif
call SetUnitX(U,LoadReal(Hash,ID,StringHash("x"+I2S(ToWhen))))
call SetUnitY(U,LoadReal(Hash,ID,StringHash("y"+I2S(ToWhen))))
endfunction
private function ResetUnitPre takes nothing returns nothing
call ResetUnit(GetEnumUnit(),Seconds)
endfunction
public function TimeTravelBack takes integer HowFar returns nothing
set Seconds=Seconds-HowFar
call ForGroup(EveryUnit,function ResetUnitPre)
endfunction
private function Init takes nothing returns nothing
set Hash=InitHashtable()
set SecondCounter=CreateTimer()
set EveryUnit=CreateGroup()
call TimerStart(SecondCounter,1.0,true,function CountSecond)
endfunction
endlibrary

Please note that you'll need to #include "cj_types.j" somewhere if you use the cJass version.
 

Attachments

  • Time Travel.w3x
    13.9 KB · Views: 83
Last edited:
Level 9
Joined
Nov 28, 2008
Messages
704
I realized that quite quickly. Fixed.

Anyways, that library only handles the units position - but the rest is incredibly easy to add.

It can handle individual units, or the whole maps units. I'll work on it sometime, but again, that is quite basically how you would do it. Simple, no?
 
Level 9
Joined
Nov 28, 2008
Messages
704
I could think of a couple cool games to make with time travel in WC3.

Oh, and I scartch saying current order would be impossible. It isn't, it would just require a bit more work (maybe an extra 30 lines?)
 
Level 9
Joined
Nov 28, 2008
Messages
704
There are numerous advantages of cJass, such as defines (I hate vJass textmacros - frankly, Vex was on crack when he made those), incremental operators, removal of set and call, declaration of variables anywhere in a block of code, as well as endblock manipulators (if { } instead of if then endif). Oh, and using those defines you can change variables like intteger to int.

pyJass is a preprocessor I made when I was bored. Its not released, but it might in the near future, I guess. I just don't feel like spending the time to make an installer script.

It just allows endblock manipulation like if:. Nothing much, but I can always add on it.

And anyone can feel free to save those orders mana hp and level and such. All of them are easy, except orders. I'll leave it for those people to do, until such a time when I find time to work on this system. >.> I'm not submitting it of course, which is why I did it in pyJass.

But in my opinion, there is no reason they should not accept submissions in cJass. Just because vJass is included by default in NewGen does not make it BETTER than cJass, especially considering Vex refused to add all of the features cJass included (incremental operators come to mind). Besides, cJass takes 30 seconds to install (cjass.xgm.ru, download installer, bam).
 
Level 9
Joined
Nov 28, 2008
Messages
704
In case anyone cared, I upgraded the library slightly. It limits how much data it stores and lets you handle how much "precision" you want to deal with timewise (it can save every 0.1 seconds if you desire, and takes reals for how far back you want to go).

JASS:
library TimeTravel initializer Init
    /***********************************************************************
    The function of this library is to provide a way to reverse the map (or single units) back to a previous time.
    This means that you should be able to set everything in a map back to where it was x seconds ago.
    It should then move everything in the map (or specified thing) back to said time.
    
    Functions:
        TimeTravel_GoBack(real time)                  This reverses the entire map to a point in time.
        
        TimeTravel_UnitGoBack(unit, real howFar)    This will set a unit to a point in time.
        
        TimeTravel_RegisterUnit(unit)
        
    NOTE: The times in the functions depend upon on the srtting SECONDS_PER_UPDATE. The system will round the times
    specified to some multiple of SECONDS_PER_UPDATE.
    
    NOTE: Some settings of units are not able to be set without a great deal of work, which this library is not
    prepared to deal with. This means that cooldowns, tinting, scaling and such of units will *not* be changed.
    
    Enjoy.
    ***********************************************************************/
    #define private SECONDS_PER_UPDATE = 0.5 
    #define private MAX_SECONDS_TRACKED = 1000

    //You probably shouldn't edit below here unless you know what you're doing.
    #include "cj_types_priv.j"
    #define private StringHashU(string) = StringHash(string + I2S(Updates))
    #define private StringHashT(string, time) = StringHash(string + I2S(time))
    private timer UpdateTimer
    private int Updates = 0
    private int UpdatesTotal = 0
    private hashtable Hash
    private group TrackedUnits
    private int TempNum
    private function Error takes string s returns nothing
        BJDebugMsg("|cffff0000" + SCOPE_PREFIX + " Error:|r " + s) 
    endfunction
    function IsUnitDead takes unit u returns boolean
      return IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0 
    endfunction
    private function SaveUnit takes unit u returns nothing
        int id = GetHandleId(u)
        SaveReal(Hash, id, StringHashU("x"), GetUnitX(u))
        SaveReal(Hash, id, StringHashU("y"), GetUnitY(u))
        SaveReal(Hash, id, StringHashU("hp"), GetUnitState(u, UNIT_STATE_LIFE))
        SaveReal(Hash, id, StringHashU("mana"), GetUnitState(u, UNIT_STATE_MANA))
        SaveBoolean(Hash, id, StringHashU("alive"), not IsUnitDead(u))
    endfunction
    public function UnitGoTo takes unit u, int when returns void
        int id = GetHandleId(u)
        int id2
        unit u2
        int i = 0
        if not LoadBoolean(Hash, id, StringHashT("alive", when)) then
            RemoveUnit(u)
        else
            if IsUnitDead(u) then
                if not IsHeroUnitId(GetUnitTypeId(u)) then
                    GroupRemoveUnit(TrackedUnits, u)
                    u2 = CreateUnit(GetOwningPlayer(u), GetUnitTypeId(u), 0, 0, 0)
                    id2 = GetHandleId(u2)
                    loop
                        exitwhen i == MAX_SECONDS_TRACKED
                        SaveReal(Hash, id2, StringHashT("x", i), LoadReal(Hash, id, StringHashT("x", i)))
                        SaveReal(Hash, id2, StringHashT("y", i), LoadReal(Hash, id, StringHashT("y", i)))
                        SaveReal(Hash, id2, StringHashT("hp", i), LoadReal(Hash, id, StringHashT("hp", i)))
                        SaveReal(Hash, id2, StringHashT("mana", i), LoadReal(Hash, id, StringHashT("mana", i)))
                        SaveBoolean(Hash, id2, StringHashT("alive", i), LoadBoolean(Hash, id, StringHashT("alive", i)))
                        i++
                    endloop
                    FlushChildHashtable(Hash, id)
                    RemoveUnit(u)
                    u = u2
                    id = id2
                    GroupAddUnit(TrackedUnits, u)
                else
                    ReviveHero(u, 0, 0, false)
                endif
            endif
        endif
        SetUnitX(u, LoadReal(Hash, id, StringHashT("x", when)))
        SetUnitY(u, LoadReal(Hash, id, StringHashT("y", when)))
        SetUnitState(u, UNIT_STATE_LIFE, LoadReal(Hash, id, StringHashT("hp", when)))
        SetUnitState(u, UNIT_STATE_MANA, LoadReal(Hash, id, StringHashT("mana", when)))
    endfunction
    private function ConvertTime takes real time returns int
        int updates
        if time / SECONDS_PER_UPDATE > UpdatesTotal then
            debug Error("Attempted to go back to a time before tracking began.")
            time = Updates * SECONDS_PER_UPDATE
        endif
        updates = Updates - R2I(time / SECONDS_PER_UPDATE)
        if updates < 0 then
            updates += MAX_SECONDS_TRACKED
        endif
        if updates < 0 then
            debug Error("Attempted to go back farther than MAX_SECONDS_TRACKED. Errors are going to occur. Thread halted.")
            loop
                TriggerSleepAction(999999)
            endloop
        endif
        return updates
    endfunction
    public function UnitGoBack takes unit u, real howFar returns void
        UnitGoTo(u, ConvertTime(howFar))
    endfunction
    private function UnitGoBackEnum takes void returns void
        UnitGoTo(GetEnumUnit(), TempNum)
    endfunction
    public function GoBack takes real howFar returns void
        TempNum = ConvertTime(howFar)
        ForGroup(TrackedUnits, function UnitGoBackEnum)
        Updates = TempNum
        UpdatesTotal = R2I(UpdatesTotal - (howFar / SECONDS_PER_UPDATE))
        if UpdatesTotal < 0 then
            UpdatesTotal = 0
        endif
    endfunction
    public function RegisterUnit takes unit u returns nothing
        GroupAddUnit(TrackedUnits, u)
        SaveUnit(u)
    endfunction
    private function UpdateUnitEnum takes nothing returns nothing
        SaveUnit(GetEnumUnit())
    endfunction
    private function Update takes nothing returns nothing
        Updates++
        UpdatesTotal++
        if Updates * SECONDS_PER_UPDATE == MAX_SECONDS_TRACKED then
            Updates = 0
        endif
        ForGroup(TrackedUnits, function UpdateUnitEnum)
    endfunction
    private function Init takes void returns void
        TrackedUnits = CreateGroup()
        UpdateTimer = CreateTimer()
        Hash = InitHashtable()
        TimerStart(UpdateTimer, SECONDS_PER_UPDATE, true, function Update)
    endfunction
endlibrary

I'll see if I cant finish it in the next whil;e.
 
Status
Not open for further replies.
Top