- Joined
- Sep 26, 2009
- Messages
- 9,534

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.
To view the AIDS version (if you'd prefer it to AutoIndex), click here.
JASS:
// _ _ ___ _ _ ___ ___
// /__/ / / | / _ / / /
// / / /__/ |/|/ / /__/
// 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:
JASS:
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
Example:
JASS:
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:
JASS:
//! 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:
JASS:
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
Attachments
Last edited: