Name | Type | is_array | initial_value |
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+)
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
endglobals
//==========================================================================================
function NewTimer takes nothing returns timer
if (tN==0) then
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
set tT[0]=CreateTimer()
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],0)
return tT[tN]
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library XYprops
function PolarProjectionX takes real x, real dist, real angle returns real
return x+dist*Cos(angle*bj_DEGTORAD)
endfunction
function PolarProjectionY takes real y, real dist, real angle returns real
return y+dist*Sin(angle*bj_DEGTORAD)
endfunction
function GetDistanceBetweenPoints takes real x1, real y1, real x2, real y2 returns real
return SquareRoot((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))
endfunction
function GetAngleBetweenPoints takes real x1, real y1, real x2, real y2 returns real
return bj_RADTODEG * Atan2(y2-y1, x2-x1)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
scope ML initializer Init
native UnitAlive takes unit id returns boolean
globals
private constant integer HAID = 'A000'
private constant integer DUID = 'h000'
private constant string LFID = "DRAM"
private constant real period = 0.03
private constant attacktype att = ATTACK_TYPE_CHAOS
private constant damagetype dtt = DAMAGE_TYPE_UNKNOWN
private constant weapontype wtt = WEAPON_TYPE_METAL_HEAVY_BASH
// -=-=-=-=-=-=-=-=-=-=-
private integer lvl
endglobals
private struct Magic
unit dummy
unit target
unit caster
real tamanho
real damage
timer tim
real x
real y
real angle
boolean hastarget
group g
boolean back
real velocity
lightning light
integer level
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.tim)
set .tim = null
set .caster = null
call DestroyGroup(.g)
set .g = null
call RemoveUnit(.dummy)
set.dummy = null
call DestroyLightning(.light)
set .light = null
endmethod
endstruct
// # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- #
function Comprimento takes integer l returns real
return 550+(150*I2R(l))
endfunction
function DmgPS takes integer l , real vel returns real
return (vel/55)+0.1*I2R(l)
endfunction
function Damag takes integer l returns real
return 25*I2R(l)
endfunction
function velreduct takes real dist returns real
return dist/32.0
endfunction
// # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- #
function Loop takes nothing returns nothing
local Magic data = GetTimerData(GetExpiredTimer())
local unit t
set data.g = CreateGroup()
if data.back and data.tamanho <= 100.0 then
call data.destroy()
else
if data.tamanho <= 10.0 then
set data.back = true
endif
if data.back then
set data.tamanho = GetDistanceBetweenPoints(data.x,data.y,GetUnitX(data.caster),GetUnitY(data.caster))
if data.hastarget then
// \\
set data.x = GetUnitX(data.target)
set data.y = GetUnitY(data.target)
// enables the target to run and receive other skills that involve the movement \\
set data.angle = GetAngleBetweenPoints(data.x,data.y,GetUnitX(data.caster),GetUnitY(data.caster))
if data.velocity <= 19.5 then
set data.velocity = 19.4
else
set data.velocity = velreduct(GetDistanceBetweenPoints(data.x,data.y,GetUnitX(data.caster),GetUnitY(data.caster)))
endif
set data.x = PolarProjectionX(data.x,data.velocity,data.angle)
set data.y = PolarProjectionY(data.y,data.velocity,data.angle)
call SetUnitX(data.target,data.x)
call SetUnitY(data.target,data.y)
call SetUnitX(data.dummy,data.x)
call SetUnitY(data.dummy,data.y)
call MoveLightning(data.light,true,GetUnitX(data.caster),GetUnitY(data.caster),data.x,data.y)
call UnitDamageTarget(data.caster,data.target,DmgPS(data.level,data.velocity),true,true,att,dtt,wtt)
else
set data.x = GetUnitX(data.dummy)
set data.y = GetUnitY(data.dummy)
set data.angle = GetAngleBetweenPoints(data.x,data.y,GetUnitX(data.caster),GetUnitY(data.caster))
set data.x = PolarProjectionX(data.x,data.velocity,data.angle)
set data.y = PolarProjectionY(data.y,data.velocity,data.angle)
call SetUnitX(data.dummy,data.x)
call SetUnitY(data.dummy,data.y)
call MoveLightning(data.light,true,GetUnitX(data.caster),GetUnitY(data.caster),data.x,data.y)
endif
else
set data.x = GetUnitX(data.dummy)
set data.y = GetUnitY(data.dummy)
set data.x = PolarProjectionX(data.x,data.velocity,data.angle)
set data.y = PolarProjectionY(data.y,data.velocity,data.angle)
call SetUnitX(data.dummy,data.x)
call SetUnitY(data.dummy,data.y)
call MoveLightning(data.light,true,data.x,data.y,GetUnitX(data.caster),GetUnitY(data.caster))
call GroupEnumUnitsInRange(data.g,data.x,data.y,90.0,null)
// LOOPSTART \\
loop
set t = FirstOfGroup(data.g)
exitwhen t == null
if UnitAlive(t) then
if IsUnitEnemy(t,GetOwningPlayer(data.caster)) then
if data.hastarget == false then
set data.target = t
set data.hastarget = true
set data.back = true
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl",data.target,"chest"))
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Human\\ManaFlare\\ManaFlareBoltImpact.mdl",data.target,"chest"))
call DestroyEffect(AddSpecialEffectTarget("Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl",data.target,"chest"))
call UnitDamageTarget(data.caster,data.target,Damag(data.level),true,true,att,dtt,wtt)
call GroupRemoveUnit(data.g,t)
set t = null
set data.tamanho = GetDistanceBetweenPoints(data.x,data.y,GetUnitX(data.caster),GetUnitY(data.caster))
else
call GroupRemoveUnit(data.g,t)
set t = null
endif
else
call GroupRemoveUnit(data.g,t)
set t = null
endif
else
call GroupRemoveUnit(data.g,t)
set t = null
endif
endloop
// ENDLOOP \\
call DestroyGroup(data.g)
set data.g = null
set data.tamanho = data.tamanho-data.velocity
endif
endif
endfunction
private function Actions takes nothing returns nothing
local Magic MyStruct = Magic.create()
set MyStruct.caster = GetTriggerUnit()
set MyStruct.x = GetUnitX(MyStruct.caster)
set MyStruct.y = GetUnitY(MyStruct.caster)
set MyStruct.hastarget = false
set MyStruct.back = false
set MyStruct.level = GetUnitAbilityLevel(MyStruct.caster,HAID)
set MyStruct.tamanho = Comprimento(MyStruct.level)
set MyStruct.dummy = CreateUnit(GetOwningPlayer(GetTriggerUnit()),DUID,MyStruct.x,MyStruct.y,0.00)
set MyStruct.light = AddLightning(LFID,true,MyStruct.x,MyStruct.y,GetUnitX(MyStruct.caster),GetUnitY(MyStruct.caster))
set MyStruct.angle = GetAngleBetweenPoints(MyStruct.x,MyStruct.y,GetSpellTargetX(),GetSpellTargetY())
set MyStruct.velocity = 23.5
set MyStruct.tim = NewTimer()
call SetTimerData(MyStruct.tim,MyStruct)
call TimerStart(MyStruct.tim,period,true,function Loop)
endfunction
private function Condit takes nothing returns boolean
return GetSpellAbilityId() == HAID
endfunction
function Init takes nothing returns nothing
local trigger trg = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( trg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition(trg,Condition(function Condit))
call TriggerAddAction( trg, function Actions )
set trg = null
endfunction
endscope