//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
//TESH.scrollpos=12
//TESH.alwaysfold=0
//**********************************************************************************************************************
//**********************Spell Create By:
// -Elphis
//*********************Requires:
// - SimpleJumpSystem
// - Linked Lightning
// - DummyRemoved
// - DamageDestructable
// - SpellEffectEvent - http://www.hiveworkshop.com/forums/jass-resources-412/snippet-spelleffectevent-187193/
// - TimerUtils - http://www.wc3c.net/showthread.php?t=101322
// - GroupUtils - http://www.wc3c.net/showthread.php?t=104464
// - DelDX - http://www.hiveworkshop.com/forums/spells-569/delfx-delayed-effects-1-4-2-a-116064/?prev=search%3DDel%2520Effect%26d%3Dlist%26r%3D20%26c%3D112
//*********************Optional:
// - xebasic - http://www.wc3c.net/showthread.php?t=101150
//************Spell Information:
// - Blocking all enemies and allies within certain, making them unable to pass through
// - Level 1: Block in 4 seconds
// - Level 2: Block in 5 seconds
// - Level 3: Block in 6 seconds
// - Level 4: Block in 7 seconds
//*****************Installation:
// - Import/copy the required libraries and Lightning Walls code to your map
// - Import/copy the custom ability and unit to your map and change the SPELL_ID if needed
// - You may view the raw ID of the objects by pressing CTRL+D in the object editor
// - You may play with the configurables below
//***********************************************************************************************************************
library LightningWalls initializer Init uses DestructableHurt,SJS,DummRemove,CheckRawcodeAvailable,Linked,DelFX optional xebasic
//==================================CONFIGURABLES==================================//
globals
//Rawcode of Spell
private constant integer SPELL_ID = 'LNWA'
//Pathing Block Unit
private constant integer PATHING_BLOCK = 'YTpc'
//Wall Count
private constant integer WALLS_COUNT = 10
//Lightning Ball Effect
private constant string LIGHTNING_BALL = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
//Combine with Ball
private constant string LIGHTNING_BALL_COMBINE= "Abilities\\Spells\\Orc\\LightningShield\\LightningShieldTarget.mdl"
//Effect when lightning destroyed
private constant string LIGHTNING_EXPLORE = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
//Lightning Attach
private constant string LIGHTNING_ALT = "CLPB"
// Attachment of lightning ball
private constant string ATTACH_BALL = "chest"
// Attachment of lightning ball effect
private constant string ATTACH_BALL_EFFECT = "chest"
// Duration of Walls
private constant real DURATION_BASE = 3.
// Wall Size
private constant real WALLS_SIZE = 1.5
// Distance Between walls created
private constant real WALLS_DISTANCE = 125.
// HEIGHT of Balls
private constant real HEIGHT = 700.
// Safe Timer Will Destroy with Balls (Effect Only)
private constant real SAFE_TIMER = 2.
// Angle of Walls Created
private constant real WALLS_ANGLE = -90.
//==================================CONFIGURABLES==================================//
endglobals
//
//==================================Do not edit anything below==================================//
private function onDuration takes unit u returns real
return DURATION_BASE+GetUnitAbilityLevel(u,SPELL_ID)
endfunction
//
private function onCast takes nothing returns nothing
local integer i = WALLS_COUNT-1
local unit u = GetTriggerUnit()
local real tarx = GetSpellTargetX()
local real tary = GetSpellTargetY()
local real cos = Cos((GetUnitFacing(u)+WALLS_ANGLE)*3.14159/180)
local real sin = Sin((GetUnitFacing(u)+WALLS_ANGLE)*3.14159/180)
local real e = -WALLS_DISTANCE*WALLS_COUNT/2
local real dur = onDuration(u) + SAFE_TIMER
local unit d
local real x
local real y
local unit dd
local destructable des
//
loop
exitwhen i < 0
//
set e = e + WALLS_DISTANCE
set x = tarx + e * cos
set y = tary + e * sin
if RawcodeUnit(XE_DUMMY_UNITID) != null then
set d = CreateUnit(Player(15),XE_DUMMY_UNITID,x,y,bj_UNIT_FACING)
call CreateDelayedEffectTarget(LIGHTNING_BALL,d,ATTACH_BALL,0.,dur)
call CreateDelayedEffectTarget(LIGHTNING_BALL_COMBINE,d,ATTACH_BALL,0.,dur)
call SetUnitScale(d,WALLS_SIZE,WALLS_SIZE,WALLS_SIZE)
set dd = CreateUnit(Player(15),XE_DUMMY_UNITID,x,y,bj_UNIT_FACING)
call CreateDelayedEffectTarget(LIGHTNING_BALL,dd,ATTACH_BALL,0.,dur)
call CreateDelayedEffectTarget(LIGHTNING_BALL_COMBINE,dd,ATTACH_BALL,0.,dur)
call SetUnitScale(dd,WALLS_SIZE,WALLS_SIZE,WALLS_SIZE)
set des = CreateDestructable(PATHING_BLOCK,x,y,0.,1.,0)
call J.Jumping(1.,true,dur-SAFE_TIMER,d,HEIGHT,"",LIGHTNING_EXPLORE,false,true,false,0.)
call LL.LLSU(0.,d,dd,LIGHTNING_ALT,dur)
call D.DesHurt(dur,des)
call Dum.Remove(dd,dur)
else
debug BJDebugMsg("Error: Rawcode of this DUMMY is not Available, please check this rawcode !!")
endif
//
set i = i - 1
endloop
//
set des = null
set u = null
set d = null
set dd = null
endfunction
//
private function Init takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID,function onCast)
endfunction
//
endlibrary
//TESH.scrollpos=32
//TESH.alwaysfold=0
library xebasic
//**************************************************************************
//
// xebasic 0.4
// =======
// XE_DUMMY_UNITID : Rawcode of the dummy unit in your map. It should
// use the dummy.mdx model, so remember to import it as
// well, just use copy&paste to copy the dummy from the
// xe map to yours, then change the rawcode.
//
// XE_HEIGHT_ENABLER: Medivh's raven form ability, you may need to change
// this rawcode to another spell that morphs into a flier
// in case you modified medivh's spell in your map.
//
// XE_TREE_RECOGNITION: The ancients' Eat tree ability, same as with medivh
// raven form, you might have to change it.
//
// XE_ANIMATION_PERIOD: The global period of animation used by whatever
// timer that depends on it, if you put a low value
// the movement will look good but it may hurt your
// performance, if instead you use a high value it
// will not lag but will be fast.
//
// XE_MAX_COLLISION_SIZE: The maximum unit collision size in your map, if
// you got a unit bigger than 197.0 it would be
// a good idea to update this constant, since some
// enums will not find it. Likewise, if none of
// your units can go bellow X and X is much smaller
// than 197.0, it would be a good idea to update
// as well, since it will improve the performance
// those enums.
//
// Notice you probably don't have to update this library, unless I specify
// there are new constants which would be unlikely.
//
//**************************************************************************
//===========================================================================
globals
constant integer XE_DUMMY_UNITID = 'xeca'
constant integer XE_HEIGHT_ENABLER = 'Amrf'
constant integer XE_TREE_RECOGNITION = 'Aeat'
constant real XE_ANIMATION_PERIOD = 0.025
constant real XE_MAX_COLLISION_SIZE = 197.0
endglobals
endlibrary
//TESH.scrollpos=6
//TESH.alwaysfold=0
//I Created xe dummy by Vexorian in your Object, Just create trigger and copy this in your trigger: //! runtextmacro RunXeUnit()
//! textmacro RunXeUnit
//! external ObjectMerger w3u ewsp xeca unam "xe unit" uhpm 1 uabi Aloc,Aeth, umdl "war3mapImported\dummy.mdx" uico "ReplaceableTextures\CommandButtons\BTNtemp.blp" usid 1 usin 1 unsf "(Caster System?)" uble 0.00 ucbs 0.00 umxp 0.00 umxr 0.00 uimz 0.00 ulpz 0.00 uprw 1.00 uspa "" udty Divine umvs 522 ucol 0.00 ufoo 0 uhom 1 umpi 1000 umpm 1000 umpr 1.00 urac Commoner utyp "" ubui "" upgr "" utub "http://wc3campaigns.net/vexorian-External By Elphis" utip ""
//! endtextmacro
//---------------------------------------------
//
//
//! zinc
//
//
//---------------------------------------------
//
library DestructableHurt requires TimerUtils
{
public struct D
{
destructable d;
//
static method Loop()
{
thistype this = GetTimerData(GetExpiredTimer());
if(GetWidgetLife(d) > 0.425)
{
RemoveDestructable(d);
ReleaseTimer(GetExpiredTimer());
d = null;
destroy();
}
}
//
static method DesHurt (real wait,destructable p)
{
thistype this;
if (GetWidgetLife(p) > 0.425)
{
this = allocate();
d = p;
TimerStart(NewTimerEx(this),wait,false,function thistype.Loop);
}
}
}
}
//
library SJS requires TimerUtils,AaD
{
constant real time = .032;
public struct J
{
real Fly;
real Dis;
real ins;
boolean kill;
unit U;
real High;
real d;
real Highsettings;
real Timer;
boolean Ag;
real ag;
boolean Jumpag;
boolean rem;
string EfEnd;
//
real savehigh;
boolean blockhig;
real timeblocking;
//
static method onLoopJ()
{
thistype this = GetTimerData(GetExpiredTimer());
if(IsUnitType(U, UNIT_TYPE_DEAD) != true && GetUnitTypeId(U) != 0)
{
if(d > 0)
d = d - time;
else
{
if (Fly > 0)
{
if(Fly >= savehigh)
{
Timer = Timer + 180/(Dis / 30.);
Fly = Sin(Timer*bj_DEGTORAD)*Highsettings*1.3;
if(blockhig && Fly > savehigh)
savehigh = Fly;
if (Ag == false)
SetUnitFlyHeight(U,Fly,0.);
else
SetUnitFlyHeight(U,Fly/ag,0.);
}
else
{
if(timeblocking > 0)
timeblocking = timeblocking - .032500000;
else
{
blockhig = false;
savehigh = 0.;
}
}
}
else
if (Jumpag == true)
{
Ag = true;
ag = ag + 1;
Fly = 1.;
Timer = 1.;
if (ag > 3.)
{
if(kill && !rem)
KillUnit(U);
if(!kill && rem && ins <= 0)
RemoveUnit(U);
if(!kill && rem && ins > 0)
Dum.Remove(U,ins);
UnitAddAbility(U,'Amrf');
UnitRemoveAbility(U,'Amrf');
SetUnitFlyHeight(U,0.,GetUnitDefaultFlyHeight(U));
DestroyEffect(AddSpecialEffect(EfEnd,GetUnitX(U),GetUnitY(U)));
U = null;
EfEnd = null;
ReleaseTimer(GetExpiredTimer());
destroy();
}
}
else
{
if(kill && !rem)
KillUnit(U);
if(!kill && rem && ins <= 0)
RemoveUnit(U);
if(!kill && rem && ins > 0)
Dum.Remove(U,ins);
UnitAddAbility(U,'Amrf');
UnitRemoveAbility(U,'Amrf');
SetUnitFlyHeight(U,0.,GetUnitDefaultFlyHeight(U));
DestroyEffect(AddSpecialEffect(EfEnd,GetUnitX(U),GetUnitY(U)));
U = null;
EfEnd = null;
ReleaseTimer(GetExpiredTimer());
destroy();
}
}
}
else
{
if(kill && !rem)
KillUnit(U);
if(!kill && rem && ins <= 0)
RemoveUnit(U);
if(!kill && rem && ins > 0)
Dum.Remove(U,ins);
UnitAddAbility(U,'Amrf');
UnitRemoveAbility(U,'Amrf');
SetUnitFlyHeight(U,0.,GetUnitDefaultFlyHeight(U));
U = null;
EfEnd = null;
ReleaseTimer(GetExpiredTimer());
destroy();
}
}
//
static method Jumping(real delay,boolean blockhe,real pauhe,unit wunit,real hei,string XJ,string EJ,boolean jumpags,boolean rems,boolean kills,real inss)
{
thistype this;
if(IsUnitType(wunit, UNIT_TYPE_DEAD) != true && GetUnitTypeId(wunit) != 0 && GetUnitFlyHeight(wunit) <= GetUnitDefaultFlyHeight(wunit)+10)
{
this = allocate();
U = wunit;
Timer = 0.;
Fly = 1.;
blockhig = blockhe;
timeblocking = pauhe;
ins = inss;
d = delay;
rem = rems;
kill = kills;
Dis = hei;
savehigh = 0.;
Jumpag = jumpags;
Ag = false;
ag = 0.5;
EfEnd = EJ;
DestroyEffect(AddSpecialEffect(XJ,GetUnitX(U),GetUnitY(U)));
High = 0.4;
Highsettings = Dis*High;
UnitAddAbility(U,'Amrf');
UnitRemoveAbility(U,'Amrf');
TimerStart(NewTimerEx(this),.032500000,true,function thistype.onLoopJ);
}
}
}
}
//
library AaD
{
public function Distance(real xa,real ya,real xb,real yb) -> real
{
real dx = xb - xa;
real dy = yb - ya;
return SquareRoot(dx * dx + dy * dy);
}
//
public function Angle(real xa,real ya,real xb,real yb) -> real
{
return bj_RADTODEG * Atan2(yb - ya, xb - xa);
}
}
//
library DummRemove requires TimerUtils
{
public struct Dum
{
unit u;
//
static method Loop()
{
thistype this = GetTimerData(GetExpiredTimer());
RemoveUnit(u);
u = null;
ReleaseTimer(GetExpiredTimer());
destroy();
}
//
static method Remove(unit whatunit,real dura) -> unit
{
thistype this = allocate();
u = whatunit;
TimerStart(NewTimerEx(this),dura,false,function thistype.Loop);
return whatunit;
}
}
}
//
library Linked requires TimerUtils
{
public struct LL
{
unit u;
unit tg;
lightning l;
boolean linked;
real time;
real x;
real y;
real w;
//
static method onPeriodic()
{
thistype this = GetTimerData(GetExpiredTimer());
boolean release = false;
//
if(w > 0)
w = w - .04;
else
{
if(!linked)
{
if(IsUnitType(u,UNIT_TYPE_DEAD) != true && GetUnitTypeId(u) != 0)
MoveLightningEx(l,true,GetUnitX(u),GetUnitY(u),GetUnitFlyHeight(u),x,y,0.);
else
release = true;
}
else
{
if(IsUnitType(u,UNIT_TYPE_DEAD) != true && GetUnitTypeId(u) != 0 && IsUnitType(tg,UNIT_TYPE_DEAD) != true && GetUnitTypeId(tg) != 0)
MoveLightningEx(l,true,GetUnitX(u),GetUnitY(u),GetUnitFlyHeight(u),GetUnitX(tg),GetUnitY(tg),GetUnitFlyHeight(tg));
else
release = true;
}
//
if(time > 0)
time = time - .04;
else
release = true;
//
}
//
if(release)
{
DestroyLightning(l);
ReleaseTimer(GetExpiredTimer());
u = null;
destroy();
}
}
//
static method LLSU(real wait,unit us,unit tgs,string ls,real times)
{
thistype this = allocate();
u = us;
tg = tgs;
time = times;
w = wait;
linked = true;
l = AddLightning(ls,true,GetUnitX(u),GetUnitY(u),x,y);
TimerStart(NewTimerEx(this),.04,true,function thistype.onPeriodic);
}
}
}
//
library CheckRawcodeAvailable
{
//This system check rawcode unit available in your object
public function RawcodeUnit(integer i) -> unit
{
unit u;
//
u = CreateUnit(Player(15),i,0,0,0);
if(u != null)
{
RemoveUnit(u);
debug BJDebugMsg("This rawcode not null");
}
else
debug BJDebugMsg("This rawcode is null");
return u;
}
}
//! endzinc
//TESH.scrollpos=-1
//TESH.alwaysfold=0
// API:
//
// >>CreateDelayedEffect(whichEffect, X, Y, Delay, Timeout)
// - whichEffect is of type string and contains the path of the
// effect-to-be-spawned
// - X and Y indicate where the effect should be spawned
// - Delay is of type real and indicates how long to wait before spawning the
// effect
// - Timeout is of type real and indicates how long to wait before destroying
// the effect after it has been created
//
// >>CreateDelayedEffectZ(whichEffect, X, Y, Z, Delay, Timeout)
// - whichEffect: see above
// - X, Y and Z indicate where to spawn the effect
// - Delay: see above
// - Timeout: see above
//
// >>CreateDelayedEffectTarget(whichEffect, Target, AttachmentPoint, Delay, Timeout)
// - whichEffect: see above
// - Target is of type widget and indicates on which widget the effect should
// be spawned
// - AttachmentPoint is of type string and holds the attachment point where
// the effect should be spawned on target widget
// - Delay: see above
// - Timeout: see above
//
// CREDITS:
// - Vexorian (JassHelper; TimerUtils)
// - Anitarf (Suggestions)
// - KaTTaNa (AddSpecialEffectZ function @ wc3jass.com)
// - PitzerMike (JassNewGenPack)
// - Pipedream (Grimoire)
// - SFilip (TESH)
library DelFX uses TimerUtils
private struct DELFX
private effect fx
private string path
private boolean target
private widget tar
private string attpt
private real x
private real y
private real z
private timer t
private real timeout
private method onDestroy takes nothing returns nothing
if .fx!=null then
call DestroyEffect(.fx)
set .fx=null
endif
set .tar=null
call ReleaseTimer(.t)
endmethod
private static method Release takes nothing returns nothing
call DELFX.destroy(GetTimerData(GetExpiredTimer()))
endmethod
private static method Callback takes nothing returns nothing
local DELFX s=GetTimerData(GetExpiredTimer())
local destructable d
debug if s.fx==null then
if s.target then
set s.fx=AddSpecialEffectTarget(s.path, s.tar, s.attpt)
elseif s.z==0 then
set s.fx=AddSpecialEffect(s.path, s.x, s.y)
else
set d=CreateDestructableZ('OTip', s.x, s.y, s.z, 0,1.,0)
set s.fx=AddSpecialEffect(s.path, s.x, s.y)
call RemoveDestructable(d)
set d=null
endif
call TimerStart(s.t, s.timeout, false, function DELFX.Release)
debug else
debug call BJDebugMsg("DELFX["+I2S(s)+"].Callback: Effect already spawned!")
debug endif
endmethod
static method Create takes string path, boolean target, widget tar, string attpt, real x, real y, real z, real delay, real timeout returns DELFX
local DELFX s=DELFX.allocate()
set s.t=NewTimer()
call SetTimerData(s.t, s)
set s.path=path
set s.target=target
set s.tar=tar
set s.attpt=attpt
set s.x=x
set s.y=y
set s.z=z
set s.timeout=timeout
call TimerStart(s.t, delay, false, function DELFX.Callback)
return s
endmethod
endstruct
// The functions below have been explained above.
function CreateDelayedEffect takes string path, real x, real y, real delay, real timeout returns nothing
call DELFX.Create(path, false, null, "", x, y, 0, delay, timeout)
endfunction
function CreateDelayedEffectZ takes string path, real x, real y, real z, real delay, real timeout returns nothing
call DELFX.Create(path, false, null, "", x, y, z, delay, timeout)
endfunction
function CreateDelayedEffectTarget takes string path, widget target, string attachmentpoint, real delay, real timeout returns nothing
call DELFX.Create(path, true, target, attachmentpoint, 0, 0, 0, delay, timeout)
endfunction
endlibrary
//TESH.scrollpos=3
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.1
* By Magtheridon96
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must either
* return a boolean (false) or nothing. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
* - Don't destroy a trigger unless you really know what you're doing.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
* - Registers code that will execute when an event fires.
* - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
* - Registers code that will execute when an event fires for a certain player.
* - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
globals
private trigger array t
endglobals
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
local integer i = GetHandleId(p)
local integer k = 15
if t[i] == null then
set t[i] = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
exitwhen k == 0
set k = k - 1
endloop
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
local integer i = 16 * GetHandleId(p) + GetPlayerId(pl)
if t[i] == null then
set t[i] = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
return t[GetHandleId(p)]
endfunction
endlibrary
//TESH.scrollpos=3
//TESH.alwaysfold=0
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, optional Table
//============================================================================
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable ht = InitHashtable()
endif
static method onCast takes nothing returns nothing
static if LIBRARY_Table then
call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
else
call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
endif
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set .tb = Table.create()
endif
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary
//TESH.scrollpos=69
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* 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)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* 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.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//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")
set tT[0]=CreateTimer()
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")
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
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
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
if ( didinit ) then
return
else
set didinit = true
endif
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