- Joined
- Jan 9, 2005
- Messages
- 2,123
JASS:
library RiseAndFall requires optional Table
/* RiseAndFall v1.06 by Spellbound and revised by JAKEZINC & AGD. Special Thanks to Wareditor.
This simple library can be used to simulate an airborne effect by causing units to bob up and
down. It can also be used to make a unit fall from a certain height or rise in the air.
You can manipulate the height of the unit using this library or for your other purposes easily.
If you want units with movement types (no other than hover or fly) to be able to use fly height,
you may put Crow Form ability on them and then remove it at the same time. Alternatively, just get
AutoFly library and make your life easier. One is available in the test map. Requires UnitDex.
------------
INTERFACE
------------
Operator:
RiseAndFall[whichUnit] will return the RiseAndFall instance of the unit.
Static Methods:
RiseAndFall.create(unit whichUnit, real currentZ, real endZ, real duration, boolean flag)
^ This will cause a unit to changed its height from currentZ to endZ over duration.
The flag determines if the unit should accelerate/decelerate as they reach destination height.
This function returns the struct instance if you wish to store it.
RiseAndFall.levitate(unit whichUnit, real currentZ, real endZ, real duration)
^ This will make your unit appear to levitate between currentZ and endZ over duration.
This function returns the struct instance if you wish to store it.
RiseAndFall.isUnitAirborne(unit whichUnit)
^ This returns true/false if a unit is levitating/rising/falling.
Instance Methods:
RiseAndFall[whichUnit].end(real endZ, real speed)
^ Ends the Rise/Fall/Airborne. real endZ determines the height at which you
want the unit to end at and the speed determines how fast the unit will get there.
(calculated as: speed = PERIODIC_INTERVAL / ( (currentHeight - endHeight)/speed )
Set to the current height of the unit to terminate instantly.
*/
globals
private constant real PERIODIC_INTERVAL = 0.031250
endglobals
struct RiseAndFall
private unit unit
private real heightStart
private real heightEnd
private real speed
private real progress
private boolean useSmoothstep
private boolean isAirborne
private thistype prev
private thistype next
private static timer time = CreateTimer()
static if LIBRARY_Table then
private static key table
else
private static hashtable table = InitHashtable()
endif
static method operator [] takes unit whichUnit returns thistype
static if LIBRARY_Table then
return Table(table)[GetHandleId(whichUnit)]
else
return LoadInteger(table,GetHandleId(whichUnit),0)
endif
endmethod
static method isUnitAirborne takes unit whichUnit returns boolean
return thistype[whichUnit] != 0
endmethod
private method destroy takes nothing returns nothing
static if LIBRARY_Table then
call Table(table).remove( GetHandleId(.unit) )
else
call RemoveSavedInteger(table,GetHandleId(.unit),0)
endif
set this.next.prev = this.prev
set this.prev.next = this.next
if thistype(0).next == 0 then
call PauseTimer(time)
endif
call .deallocate()
set .unit = null
endmethod
private static method update takes nothing returns nothing
local real x
local thistype this = thistype(0).next
loop
exitwhen this == 0
set .progress = .progress + .speed
if .progress >= 1.0 then
call SetUnitFlyHeight(.unit,.heightEnd,0)
if .isAirborne then
set x = .heightStart
set .heightStart = .heightEnd
set .heightEnd = x
set .progress = 0.
else
if isUnitAirborne(.unit) then
call .destroy()
endif
endif
else
set x = .progress
if .useSmoothstep then
call SetUnitFlyHeight(.unit,.heightStart + (.heightEnd - .heightStart)*(x*x*x*(x*(x*6.0-15)+10) ),0)
else
call SetUnitFlyHeight(.unit,.heightStart + (.heightEnd - .heightStart)*(x*x),0)
endif
endif
set this = .next
endloop
endmethod
method end takes real endZ, real speed returns nothing
local real height = GetUnitFlyHeight(.unit)
if endZ != height then
set .heightStart = height
set .heightEnd = endZ
set .progress = 0.
set .isAirborne = false
set .useSmoothstep = false
if speed == 0. then // divide by zero prevention
set speed = 500.
endif
set .speed = PERIODIC_INTERVAL / ( (.heightStart - .heightEnd)/speed )
elseif isUnitAirborne(.unit) then
set .heightStart = height
set .heightEnd = endZ
set .progress = 1.0
set .isAirborne = false
set .useSmoothstep = false
call .destroy()
endif
endmethod
private static method createAirborne takes unit whichUnit, real currentZ, real endZ, real duration, boolean airborne, boolean smooth returns thistype
local thistype this = RiseAndFall[whichUnit]
if this != 0 then
call this.end(GetUnitFlyHeight(whichUnit),1.0)
endif
set this = allocate()
set this.unit = whichUnit
set this.heightStart = currentZ
set this.heightEnd = endZ
set this.progress = 0.
set this.speed = PERIODIC_INTERVAL/duration
set this.isAirborne = airborne
set this.useSmoothstep = smooth
static if LIBRARY_Table then
set Table(table)[GetHandleId(whichUnit)] = this
else
call SaveInteger(table,GetHandleId(whichUnit),0,this)
endif
if thistype(0).next == 0 then
call TimerStart(time,PERIODIC_INTERVAL,true,function thistype.update)
endif
set this.next = 0
set this.prev = thistype(0).prev
set thistype(0).prev.next = this
set thistype(0).prev = this
return this
endmethod
static method create takes unit whichUnit, real currentZ, real endZ, real duration, boolean flag returns thistype
if RiseAndFall[whichUnit] == 0 then
return createAirborne(whichUnit,currentZ,endZ,duration,false,flag)
endif
return 0
endmethod
static method levitate takes unit whichUnit, real currentZ, real endZ, real duration returns thistype
if RiseAndFall[whichUnit] == 0 then
return createAirborne(whichUnit,currentZ,endZ,duration,true,true)
endif
return 0
endmethod
endstruct
endlibrary
This simple library can be used to simulate an airborne effect by causing units to bob up and
down. It can also be used to make a unit fall from a certain height or rise in the air. The
purpose of this library is to be used with AttachObject, but one could always find some other
use for it.
This library comes in 2 versions: one that uses ListT and one that uses TimerUtils. You'll probably want the ListT version if you're going to be adding airborne effect to a lot of units.
Demo map attached below.
JASS:
library RiseAndFall requires ListT optional UnitDex
/*
RiseAndFall v1.05
by Spellbound
Special thanks to Wareditor for the math and re-organising my code and got rid of a lot of
needless lines and methods.
This simple library can be used to simulate an airborne effect by causing units to bob up and
down. It can also be used to make a unit fall from a certain height or rise in the air. The
purpose of this library is to be used with AttachObject, but one could always find some other
use for it.
_________________________________________________________________________
REQUIREMENTS
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Required:
ListT: https://www.hiveworkshop.com/threads/containers-list-t.249011/
Optional
UnitDex: https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
UnitDex will replace hashtables for retrieving airbone instances. Highly recommended so
as to avoid the hashtable limit.
PS if you want units with non-hover or non-flying movement type to be able to levitate, you
need to give them and then immediately remove Crow Form from the. Alternatively, just get an
Autofly library and make your life easier. One is available in the test map. Requires UnitDex.
_________________________________________________________________________
API
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
operator:
RiseAndFall[yourUnit] will return the RiseAndFall instance of a unit.
static methods:
RiseAndFall.create(unit u, real currentZ, real endZ, real duration, boolean flag)
^ This will cause a unit to changed its height from currentZ to endZ over duration.
If the flag is true, the unit will decelerate as they reach destination height. This
can be useful for units that fly or hover.
This function returns the struct instance if you wish to store it.
RiseAndFall.createAirborne(unit u, real currentZ, real endZ, real duration)
^ This will make your unit appear to levitate between currentZ and endZ over duration.
Set a narrow distance for a more realistic effect.
This function returns the struct instance if you wish to store it.
RiseAndFall.isUnitAirborne(unit u)
^ This returns true if a unit is airborne/falling/rising.
non-static methods:
RiseAndFall.end(real endZ, real speed)
^ Ends the Rise/Fall/Airborne of a unit. real endZ determines the height at which you
want the unit to end at and speed determines how fast in gets there. use Warcraft 3
missile speed for referrence.
Set to the current height of the unit to terminate immediately.
*/
globals
private constant real INTERVAL = .03125
endglobals
private module Init
private static method onInit takes nothing returns nothing
static if not LIBRARY_UnitDex then
set instance_storage = InitHashtable()
endif
set list = IntegerList.create()
endmethod
endmodule
struct RiseAndFall
private unit u
private real heightStart
private real heightEnd
private real speed
private real progress
private boolean useSmoothstep
private boolean isAirborne
private static IntegerList list
private static timer clock = CreateTimer()
static if LIBRARY_UnitDex then
private static integer array instance
else
private static hashtable instance_storage
endif
static method operator [] takes unit u returns thistype
static if LIBRARY_UnitDex then
return instance[GetUnitId(u)]
else
return LoadInteger(instance_storage, GetHandleId(u), 0)
endif
endmethod
static method isUnitAirborne takes unit u returns boolean
static if LIBRARY_UnitDex then
return instance[GetUnitId(u)] != 0
else
return LoadInteger(instance_storage, GetHandleId(u), 0) != 0
endif
endmethod
method destroy takes nothing returns nothing
static if LIBRARY_UnitDex then
set instance[GetUnitId(u)] = 0
else
call FlushChildHashtable(instance_storage, GetHandleId(.u))
endif
set .u = null
call list.removeElem(this)
if list.size() == 0 then
call PauseTimer(clock)
endif
call .deallocate()
endmethod
private static method update takes nothing returns nothing
local thistype this
local IntegerListItem node = list.first
local IntegerListItem nodeNext
local real s
local real hs
loop
exitwhen node == 0
set nodeNext = node.next
set this = node.data
set .progress = .progress + .speed
if .progress >= 1.then
call SetUnitFlyHeight(.u, .heightEnd, 0.)
if .isAirborne then
set hs = .heightStart
set .heightStart = .heightEnd
set .heightEnd = hs
set .progress = 0.
else
call .destroy()
endif
else
set s = .progress
if .useSmoothstep then
call SetUnitFlyHeight(.u, .heightStart + ( .heightEnd - .heightStart ) * (s * s * s * (s * (s * 6 - 15) + 10)), 0.) //smoothstep
else
call SetUnitFlyHeight(.u, .heightStart + ( .heightEnd - .heightStart ) * (s * s), 0.)
endif
endif
set node = nodeNext
endloop
endmethod
method end takes real endZ, real spd returns nothing
local real height = GetUnitFlyHeight(.u)
if endZ != height then
set .heightStart = height
set .heightEnd = endZ
set .progress = 0.
set .isAirborne = false
set .useSmoothstep = false
if spd == 0. then // divide by zero prevention
set spd = 600.
endif
set .speed = INTERVAL / ((.heightStart - .heightEnd) / spd)
else
call .destroy()
endif
endmethod
private static method createEx takes unit u, real currentZ, real endZ, real duration, boolean smooth, boolean airborn returns thistype
local thistype this = RiseAndFall[u]
if this != 0 then
call this.end(GetUnitFlyHeight(u), 1.)
endif
set this = allocate()
set this.u = u
set this.heightStart = currentZ
set this.heightEnd = endZ
set this.progress = 0.
set this.speed = INTERVAL/duration
set this.useSmoothstep = smooth
set this.isAirborne = airborn
static if LIBRARY_UnitDex then
set instance[GetUnitId(u)] = this
else
call SaveInteger(instance_storage, GetHandleId(u), 0, this)
endif
call list.push(this)
if list.size() == 1 then
call TimerStart(clock, INTERVAL, true, function thistype.update)
endif
return this
endmethod
static method create takes unit u, real currentZ, real endZ, real duration, boolean smooth returns thistype
return createEx(u, currentZ, endZ, duration, smooth, false)
endmethod
static method createAirborne takes unit u, real currentZ, real endZ, real duration returns thistype
return createEx(u, currentZ, endZ, duration, true, true)
endmethod
implement Init
endstruct
endlibrary
JASS:
library RiseAndFall requires TimerUtils optional UnitDex
/*
RiseAndFall v1.05
by Spellbound
Special thanks to Wareditor for the math and re-organising my code and got rid of a lot of
needless lines and methods.
This simple library can be used to simulate an airborne effect by causing units to bob up and
down. It can also be used to make a unit fall from a certain height or rise in the air. The
purpose of this library is to be used with AttachObject, but one could always find some other
use for it.
_________________________________________________________________________
REQUIREMENTS
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Required:
TimerUtils: http://www.wc3c.net/showthread.php?t=101322
Optional
UnitDex: https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
UnitDex will replace hashtables for retrieving airbone instances. Highly recommended so
as to avoid the hashtable limit.
PS if you want units with non-hover or non-flying movement type to be able to levitate, you
need to give them and then immediately remove Crow Form from the. Alternatively, just get an
Autofly library and make your life easier. One is available in the test map. Requires UnitDex.
_________________________________________________________________________
API
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
operator:
RiseAndFall[yourUnit] will return the RiseAndFall instance of a unit.
static methods:
RiseAndFall.create(unit u, real currentZ, real endZ, real duration, boolean flag)
^ This will cause a unit to changed its height from currentZ to endZ over duration.
If the flag is true, the unit will decelerate as they reach destination height. This
can be useful for units that fly or hover.
This function returns the struct instance if you wish to store it.
RiseAndFall.createAirborne(unit u, real currentZ, real endZ, real duration)
^ This will make your unit appear to levitate between currentZ and endZ over duration.
Set a narrow distance for a more realistic effect.
This function returns the struct instance if you wish to store it.
RiseAndFall.isUnitAirborne(unit u)
^ This returns true if a unit is airborne/falling/rising.
non-static methods:
RiseAndFall.end(real endZ)
^ Ends the Rise/Fall/Airborne of a unit. real endZ determines the height at which you
want the unit to end at and speed determines how fast in gets there. use Warcraft 3
missile speed for referrence.
Set to the current height of the unit to terminate immediately.
*/
globals
private constant real INTERVAL = .03125
endglobals
private module Init
private static method onInit takes nothing returns nothing
static if not LIBRARY_UnitDex then
set instance_storage = InitHashtable()
endif
set list = IntegerList.create()
endmethod
endmodule
struct RiseAndFall
private unit u
private real heightStart
private real heightEnd
private real speed
private real progress
private boolean useSmoothstep
private boolean isAirborne
private static timer clock = CreateTimer()
static if LIBRARY_UnitDex then
private static integer array instance
else
private static hashtable instance_storage
endif
static method operator [] takes unit u returns thistype
static if LIBRARY_UnitDex then
return instance[GetUnitId(u)]
else
return LoadInteger(instance_storage, GetHandleId(u), 0)
endif
endmethod
static method isUnitAirborne takes unit u returns boolean
static if LIBRARY_UnitDex then
return instance[GetUnitId(u)] != 0
else
return LoadInteger(instance_storage, GetHandleId(u), 0) != 0
endif
endmethod
method destroy takes nothing returns nothing
static if LIBRARY_UnitDex then
set instance[GetUnitId(u)] = 0
else
call FlushChildHashtable(instance_storage, GetHandleId(.u))
endif
set .u = null
call .deallocate()
endmethod
private static method update takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
local real s
local real hs
set .progress = .progress + .speed
if .progress >= 1.then
call SetUnitFlyHeight(.u, .heightEnd, 0.)
if .isAirborne then
set hs = .heightStart
set .heightStart = .heightEnd
set .heightEnd = hs
set .progress = 0.
else
call ReleaseTimer(t)
call .destroy()
endif
else
set s = .progress
if .useSmoothstep then
call SetUnitFlyHeight(.u, .heightStart + ( .heightEnd - .heightStart ) * (s * s * s * (s * (s * 6 - 15) + 10)), 0.) //smoothstep
else
call SetUnitFlyHeight(.u, .heightStart + ( .heightEnd - .heightStart ) * (s * s), 0.)
endif
endif
set t = null
endmethod
method end takes real endZ, real spd returns nothing
local real height = GetUnitFlyHeight(.u)
if endZ != height then
set .heightStart = height
set .heightEnd = endZ
set .progress = 0.
set .isAirborne = false
set .useSmoothstep = false
if spd == 0. then // divide by zero prevention
set spd = 600.
endif
set .speed = INTERVAL / ((.heightStart - .heightEnd) / spd)
else
call .destroy()
endif
endmethod
private static method createEx takes unit u, real currentZ, real endZ, real duration, boolean smooth, boolean airborn returns thistype
local thistype this = RiseAndFall[u]
if this != 0 then
call this.end(GetUnitFlyHeight(u), 1.)
endif
set this = allocate()
set this.u = u
set this.heightStart = currentZ
set this.heightEnd = endZ
set this.progress = 0.
set this.speed = INTERVAL/duration
set this.useSmoothstep = smooth
set this.isAirborne = airborn
static if LIBRARY_UnitDex then
set instance[GetUnitId(u)] = this
else
call SaveInteger(instance_storage, GetHandleId(u), 0, this)
endif
call TimerStart(NewTimerEx(this), INTERVAL, true, function thistype.update)
return this
endmethod
static method create takes unit u, real currentZ, real endZ, real duration, boolean smooth returns thistype
if RiseAndFall[u] == 0 then
return createEx(u, currentZ, endZ, duration, smooth, false)
endif
return 0
endmethod
static method startAirborne takes unit u, real currentZ, real endZ, real duration returns thistype
if RiseAndFall[u] == 0 then
return createEx(u, currentZ, endZ, duration, true, true)
endif
return 0
endmethod
implement Init
endstruct
endlibrary
- v1.06 (JAKEZINC version) Fixed double-free bug and has no more dependencies. .createAirborne is now .levitate. [vJASS] - RiseAndFall
- v1.05 creator methods renamed to create/createAirborne. Added safety to the private .createEx method as well as to the .end method. Updated documentation. Removed unused/redundant lines.
- v1.04 fixed an issue with the initialization order which was causing libraries depending on RiseAndFall to freeze when saving. Also added a speed parameter to .end for greater control.
- v1.03 RiseAndFall.end now requires a real rather than a boolean.
- v1.02 RiseAndFall.start now takes a boolean argument rather than a integer. RiseAndFall.end now takes a real argument for the endZ.
- v1.01 updated code in great part thanks to Wareditor.
- v1.00 Initial release
- v1.05 creator methods renamed to create/createAirborne. Added safety to the private .createEx method as well as to the .end method. Updated documentation. Removed unused/redundant lines.
- v1.04 fixed an issue with the initialization order which was causing libraries depending on RiseAndFall to freeze when saving. Also added a speed parameter to .end for greater control.
- v1.03 RiseAndFall.end now requires a real rather than a boolean.
- v1.02 RiseAndFall.start now takes a boolean argument rather than a integer. RiseAndFall.end now takes a real argument for the endZ.
- v1.01 updated code in great part thanks to Wareditor.
- v1.00 Initial release
Attachments
Last edited: