Name | Type | is_array | initial_value |
Dummy | unit | No | |
TempGroup | group | No | |
TempInt | integer | No | |
TempLoc | location | No | |
TempLoc2 | location | No | |
Y_Map_Point | location | Yes | |
Y_Map_Region | rect | Yes |
//TESH.scrollpos=0
//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=0
//TESH.alwaysfold=0
library DummyRecycler requires TimerUtils
//
//
//
// *Dummyrecycler v1.1b*
// by baassee
//
// *Requirments*
//
// TimerUtils by Vexorian
//
// *Credits*
//
// To Vexorian for TimerUtils
//1 watermelon_1234 for helping me alot with this!
//
// *Changelog*
//
// v1.0 Release
//
// v1.1 -Fixed debug infront of debug messages.
// -Added a check to check
// if the user have boundsentinel or not, if not, the system will
// create it's own kinda boundsentinel.
// -Added two more functions which are provided to help the GUI
// users even more. One is normal newdummy but takes location loc
// and the second is the same but with auto recycling.
//
// v1.1b -Now the recycle struct is private. Special thanks goes to watermelon_1234
// for helping me with this.
//
// *Standard information*
//
// This is just a standard dummy recycler with a limit of 8191 dummies.
// Always remember to recycle the dummies after you used them and
// NOT apply timed life to them or kill/remove them. Then the recycler
// will just continue to create units and that is not what this library
// is created for. It uses TimerUtils for a few functions, the auto
// recycling functions. All dummies are created for a player and not
// for player(15). If you want to do that, use NewDummy function and set
// the player to player(15).
//
// Please give proper credit to me for this library and vexorian for
// TimerUtils. Do not distrubate this to other maps without permission.
//
// *Functions provided by this library*
//
// function NewDummy takes player owner, real x, real y returns unit
// gets a new dummy from the system for owner, spawned at coord (x,y)
//
// function RecycleDummy takes unit u returns nothing
// recycles dummy u and moves it to STORAGE(X,Y) if MOVE == true
// Warning, you can't recycle units that isn't the dummy unittype
//
// function NewDummyEx takes unit caster returns unit
// gets a dummy created at the position of the triggering unit
//
// function NewDummyLoc takes player owner, location loc returns unit
// just like NewDummy but this takes a loc, easier for GUI users
// who don't want the dummy to be created at the casters coordinates
//
// function NewDummyAuto takes player owner, real x, real y, real time returns unit
// just like NewDummy function but this auto recycles the dummy after set time
//
// function NewDummyExAuto takes unit caster, real time returns unit
// just like NewDummyAuto but this is made from the NewDummyEx function.
//
// function NewDummyLocAuto takes player owner, location loc, real time returns unit
// just like NewDummyLoc but this have the auto recycle function
//
//
//
// *Exmaples*
//
// These below are just JASS examples, check the example category if you
// are a GUI user :D
//
//
// local unit u = NewDummy(Player(15), 0., 0.)
// call RecycleDummy(u)
// Creates a dummy for player(15) at coordinates (0,0)
//
// local unit u = NewDummyEx(GetTriggerUnit())
// call RecycleDummy(u)
// Creates a dummy for the owner of GetTriggerUnit() and on the same spot
//
// local real x = GetUnitX(GetTriggerUnit())
// local real y = GetUnitY(GetTriggerUnit())
// local unit u = NewDummyAuto(GetOwningPlayer(GetTriggerUnit()), x, y, 1.)
// Creates a dummy for the owner of GetTriggerUnit() and recycles it after 1 second.
//
// local unit u = NewDummyExAuto(GetTriggerUnit(), 5.)
// Creates a dumym for the owner of GetTriggerUnit() and recycles after 5 seconds.
//
// *How to import*
//
// Copy the dummy unit to your map or make your own dummy unit, preffered with
// a dummy model such as the xe dummy or others. Make sure the dummy have locust.
//
// If you don't have timerutils then copy the whole category to your map else
// you can just copy this trigger.
//
// Set up stuff as you want to such as store coordinates and remember to set the
// dummy rawcode.
//
// Credit baassee and you are good to go!
//
//
//
// *SETUP PART*
globals
//
// The counter variable, don't touch.
//
private integer N = 0
//
// The Rawcode of the dummy that will be used by this system.
// To get rawcodes, select the dummy inside the object editor
// and press ctrl + d and the raw code will show up instead
// of the name.
//
private constant integer RAWCODE = 'h006'
//
// A boolean to check if the dummies should be moved to the
// storage spot or just remain where they are.
//
private constant boolean MOVE = true
//
// If the boolean above is true, enter the x coordinate
// here.
//
private constant real STOREX = 0.
//
// If the boolean MOVE is true, enter the y
// coordinate here.
//
private constant real STOREY = 0.
//
// Dummy global that will be used. Don't touch.
//
private unit array DUMMY
//
// The setup continues below, just read the comments.
//
endglobals
//
// Here it checks if you have the library BoundSentinel,
// if you have, you won't need this protect stuff.
//
static if not LIBRARY_BoundSentinel then
globals
//
// Sets the reals of the map. You can change
// the region if you want to, but it shouldn't be
// necessary as you set the storage coordinates
// by yourself
//
private constant real MINX = GetRectMinX(bj_mapInitialPlayableArea)
private constant real MAXX = GetRectMaxX(bj_mapInitialPlayableArea)
private constant real MINY = GetRectMinY(bj_mapInitialPlayableArea)
private constant real MAXY = GetRectMaxY(bj_mapInitialPlayableArea)
endglobals
endif
//
// *END OF SETUP*
//
//
// *Recycle function below*
//
function RecycleDummy takes unit u returns nothing
// just to check the limit, I dont think someone that use this proper
// will reach this but to protect the users from themselves.
if N != 8191 then
if u != null and GetUnitTypeId(u) == RAWCODE then
// here I set the color to false cause it's not needed
// as they wont be used by any player until called again.
call SetUnitOwner(u, Player(15), false)
call PauseUnit(u, true)
set N = N + 1
set DUMMY[N] = u
if MOVE == true then
// checks for boundsentinel so I don't have to do my own check
// because I'll know it will be safe with boundsentinel
static if LIBRARY_BoundSentinel then
// moves units if you have boundsentinel
call SetUnitX(u, STOREX)
call SetUnitY(u, STOREY)
else // if you dont have boundsentinel, we do our own check
if (MINX <= STOREX) and (STOREX <= MAXX) and (MINY <= STOREY) and (STOREY <= MAXY) then
call SetUnitX(u, STOREX)
call SetUnitY(u, STOREY)
else
// announcing that the user have made it wrong and have to re-do it.
debug call BJDebugMsg("DUMMYRECYCLER - The map doesn't contain the coordinates you've set, please change them.")
endif
endif
endif
else
// just a debug message to show the user what he did wrong with recycling.
debug call BJDebugMsg("DUMMYRECYCLER - Tried to recycle a nulled unit or a not-dummy type unit.")
endif
else
// a announce that a user have used way to many dummies.
debug call BJDebugMsg("DUMMYRECYCLER - You've reached the max number of dummies, please use dummies properly.")
// doing as vexorian does, explode the unit
call ExplodeUnitBJ(u)
endif
endfunction
//
// A normal struct with timers that is used for the autorecycle functions.
// NOW a private struct, will look weird having it between the functions but it will
// only work that way. Special thanks to watermelon_1234 for helping me with this.
//
private struct recycler
unit u
static method start takes unit u, real time returns thistype
local thistype this = thistype.allocate()
local timer t = NewTimer()
set .u = u
call SetTimerData(t, this)
call TimerStart(t, time, false, function thistype.recycle)
return this
endmethod
static method recycle takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call RecycleDummy(.u)
call ReleaseTimer(GetExpiredTimer())
call .destroy()
endmethod
endstruct
//
// The rest of the functions!
//
function NewDummy takes player owner, real x, real y returns unit
if N == 0 then
return CreateUnit(owner, RAWCODE, x, y, 0.)
endif
call PauseUnit(DUMMY[N], false)
// yeah I don't know why I set the color to true but someone might use it.
call SetUnitOwner(DUMMY[N], owner, true)
call SetUnitX(DUMMY[N], x)
call SetUnitY(DUMMY[N], y)
set N = N - 1
return DUMMY[N + 1]
endfunction
function NewDummyEx takes unit caster returns unit
local real x = GetUnitX(caster)
local real y = GetUnitY(caster)
if N == 0 then
return CreateUnit(GetOwningPlayer(caster), RAWCODE, x, y, 0.)
endif
call PauseUnit(DUMMY[N], false)
// yeah I don't know why I set the color to true but someone might use it.
call SetUnitOwner(DUMMY[N], GetOwningPlayer(caster), true)
call SetUnitX(DUMMY[N], x)
call SetUnitY(DUMMY[N], y)
set N = N - 1
return DUMMY[N + 1]
endfunction
function NewDummyLoc takes player owner, location loc returns unit
local real x = GetLocationX(loc)
local real y = GetLocationY(loc)
if N == 0 then
return CreateUnit(owner, RAWCODE, x, y, 0.)
endif
call PauseUnit(DUMMY[N], false)
call SetUnitOwner(DUMMY[N], owner, true)
call SetUnitX(DUMMY[N], x)
call SetUnitY(DUMMY[N], y)
set N = N - 1
return DUMMY[N + 1]
endfunction
function NewDummyAuto takes player owner, real x, real y, real time returns unit
local unit u
if N == 0 then
set u = CreateUnit(owner, RAWCODE, x, y, 0.)
call recycler.start(u, time)
return u
endif
call PauseUnit(DUMMY[N], false)
// yeah I don't know why I set the color to true but someone might use it.
call SetUnitOwner(DUMMY[N], owner, true)
call recycler.start(DUMMY[N], time)
call SetUnitX(DUMMY[N], x)
call SetUnitY(DUMMY[N], y)
set N = N - 1
return DUMMY[N + 1]
endfunction
function NewDummyExAuto takes unit caster, real time returns unit
local unit u
local real x = GetUnitX(caster)
local real y = GetUnitY(caster)
if N == 0 then
set u = CreateUnit(GetOwningPlayer(caster), RAWCODE, x, y, 0.)
call recycler.start(u, time)
return u
endif
call PauseUnit(DUMMY[N], false)
// yeah I don't know why I set the color to true but someone might use it.
call SetUnitOwner(DUMMY[N], GetOwningPlayer(caster), true)
call recycler.start(DUMMY[N], time)
call SetUnitX(DUMMY[N], x)
call SetUnitY(DUMMY[N], y)
set N = N - 1
return DUMMY[N + 1]
endfunction
function NewDummyLocAuto takes player owner, location loc, real time returns unit
local unit u
local real x = GetLocationX(loc)
local real y = GetLocationY(loc)
if N == 0 then
set u = CreateUnit(owner, RAWCODE, x, y, 0.)
call recycler.start(u, time)
return u
endif
call PauseUnit(DUMMY[N], false)
call SetUnitOwner(DUMMY[N], owner, true)
call recycler.start(DUMMY[N], time)
call SetUnitX(DUMMY[N], x)
call SetUnitY(DUMMY[N], y)
set N = N - 1
return DUMMY[N + 1]
endfunction
endlibrary