Name | Type | is_array | initial_value |
tempInt | integer | No |
//TESH.scrollpos=114
//TESH.alwaysfold=0
/* ======================
Respawn System - v1.0b
======================
Coded by: Mr_Bean ([email protected])
Introduction:
-------------
This system was originally requested by Peacehagen for his map Rise of the Corruptor ORPG.
He recommended that I upload it because it would help people, which I hope it does!
I added some new features to the system and made it as user-friendly as possible.
This system only works for units that are on the map as soon as the game starts.
Feel free to contact me if something is not clear, or if you find bugs.
Installation:
-------------
> Copy this trigger.
> Copy the "TimerUtils" trigger (if you don't have it already).
> Configure the "Configurables" section in this trigger as desired.
Available Functions:
--------------------
> RespawnAllCreeps()
- Immediately respawns all dead creeps.
- Does not affect countdown timer.
> RespawnAllCreepsForPlayer(player whichPlayer)
- Immediately respawns all dead creeps for a specified player.
- Does not affect countdown timer.
> PauseRespawnSystem(real howLong)
- If all creeps respawn together, you can use this to pause the system.
- The parameter is how long it is paused for. Use any negative number to pause it forever (until you resume again). Using 0 means no pause.
- Can only use if RESPAWN_TOGETHER = true.
> ResumeRespawnSystem()
- This will resume the system.
- Only works if you used PauseRespawnSystem with a negative number (like PauseRespawnSystem(-1)).
- Can only use if RESPAWN_TOGETHER = true.
> ResetRespawnSystem()
- This will pause the system and reset the countdown.
- Needs to be unpaused with ResumeRespawnSystem().
- Can only use if RESPAWN_TOGETHER = true.
> RestartRespawnSystem()
- This will reset the respawn countdown.
- Will not pause the system.
- Can only use if RESPAWN_TOGETHER = true.
> GetAliveCreeps()
- Returns how many creeps are alive.
> GetDeadCreeps()
- Returns how many creeps are dead.
> GetTotalCreeps()
- Returns how many creeps there are in total.
Contact:
--------
> [email protected]
> Mr_Bean987 on the HiveWorkshop.com
*/
library RespawnSystem initializer OnInit requires TimerUtils
//**************************************************\\
//*** START OF CONFIGURABLES ***\\
//**************************************************\\
globals
// Delay between unit dying and respawning:
private constant real RESPAWN_DELAY = 30.0
// Respawn all dead units at once? If set to false, units will respawn individually.
private constant boolean RESPAWN_TOGETHER = true
// Show a countdown window to all players:
private constant boolean SHOW_TIMER_WINDOW = false
// Countdown window text (only applicable if SHOW_TIMER_WINDOW = true):
private constant string TIMER_WINDOW_TEXT = "Creeps respawn in:"
// Respawn heroes:
private constant boolean RESPAWN_HEROES = false
// Effect created on respawning units (set to "" to have no effect):
private constant string RESPAWN_EFFECT = "Abilities\\Spells\\Undead\\RaiseSkeletonWarrior\\RaiseSkeleton.mdl"
// Attachment point of effect on unit:
private constant string EFFECT_ATTACHMENT = "origin"
endglobals
private function CheckOwner takes nothing returns boolean
local integer playerId = GetPlayerId(GetOwningPlayer(GetFilterUnit()))
if /*
//------------------------------------------------------\\
// Add the players that the system respawns creeps for: \\
// Remember to subtract 1 from GUI player indexes! \\
*/ playerId == 10 or /* // Player 11 in GUI. \*
*/ playerId == 11 or /* // Player 12 in GUI. \*
*/ playerId == 12 /* // Neutral Hostile in GUI. \*
//------------------------------------------------------\\
*/ then
return true
endif
return false
endfunction
//**************************************************\\
//*** END OF CONFIGURABLES ***\\
//**************************************************\\
globals
private group deadCreeps = CreateGroup()
private group creeps = CreateGroup()
private group enumG = CreateGroup()
private hashtable ht = InitHashtable()
private timer respawnTimer
private timerdialog timerWindow
private integer tempInt = 0
endglobals
private function Increment takes nothing returns nothing
set tempInt = tempInt + 1
endfunction
private function EnumerateGroup takes group g returns integer
set tempInt = 0
call ForGroup(g, function Increment)
return tempInt
endfunction
//**************************************************\\
//*** GetTotalCreeps ***\\
//**************************************************\\
function GetTotalCreeps takes nothing returns integer
return EnumerateGroup(creeps) + EnumerateGroup(deadCreeps)
endfunction
//**************************************************\\
//*** GetAliveCreeps ***\\
//**************************************************\\
function GetDeadCreeps takes nothing returns integer
return EnumerateGroup(deadCreeps)
endfunction
//**************************************************\\
//*** GetDeadCreeps ***\\
//**************************************************\\
function GetAliveCreeps takes nothing returns integer
return EnumerateGroup(creeps)
endfunction
//**************************************************\\
private function RespawnCreepX takes integer handleId returns nothing
local real x = LoadReal(ht, handleId, StringHash("x"))
local real y = LoadReal(ht, handleId, StringHash("y"))
local real angle = LoadReal(ht, handleId, StringHash("facing"))
local integer owner = LoadInteger(ht, handleId, StringHash("owner"))
local integer unitType = LoadInteger(ht, handleId, StringHash("type"))
local unit creep
set creep = CreateUnit(Player(owner), unitType, x, y, angle)
call FlushChildHashtable(ht, handleId)
call DestroyEffect(AddSpecialEffectTarget(RESPAWN_EFFECT, creep, EFFECT_ATTACHMENT))
set handleId = GetHandleId(creep)
call SaveReal(ht, handleId, StringHash("x"), GetUnitX(creep))
call SaveReal(ht, handleId, StringHash("y"), GetUnitY(creep))
call SaveReal(ht, handleId, StringHash("facing"), GetUnitFacing(creep))
call SaveInteger(ht, handleId, StringHash("owner"), GetPlayerId(GetOwningPlayer(creep)))
call SaveInteger(ht, handleId, StringHash("type"), GetUnitTypeId(creep))
call GroupRemoveUnit(deadCreeps, creep)
call GroupAddUnit(creeps, creep)
set creep = null
endfunction
//**************************************************\\
//*** ResumeRespawnSystem ***\\
//**************************************************\\
function ResumeRespawnSystem takes nothing returns nothing
static if RESPAWN_TOGETHER then
call ResumeTimer(respawnTimer)
else
call BJDebugMsg("|cffff0000Respawn System|r: Cannot use ResumeRespawnSystem when RESPAWN_TOGETHER = false.")
endif
endfunction
//**************************************************\\
//*** PauseRespawnSystem ***\\
//**************************************************\\
function PauseRespawnSystem takes real duration returns nothing
static if RESPAWN_TOGETHER then
if duration < 0 then
call PauseTimer(respawnTimer)
elseif duration > 0 then
call PauseTimer(respawnTimer)
call TimerStart(NewTimer(), duration, false, function ResumeRespawnSystem)
endif
else
call BJDebugMsg("|cffff0000Respawn System|r: Cannot use PauseRespawnSystem when RESPAWN_TOGETHER = false.")
endif
endfunction
//**************************************************\\
//*** RespawnAllCreepsForPlayer ***\\
//**************************************************\\
function RespawnAllCreepsForPlayer takes player whichPlayer returns nothing
local unit temp
set enumG = deadCreeps
loop
set temp = FirstOfGroup(enumG)
exitwhen temp == null
if GetOwningPlayer(temp) == whichPlayer then
call RespawnCreepX(GetHandleId(temp))
endif
call GroupRemoveUnit(enumG, temp)
endloop
endfunction
private function GroupEnumeration takes nothing returns nothing
call RespawnCreepX(GetHandleId(GetEnumUnit()))
endfunction
//**************************************************\\
//*** RespawnAllCreeps ***\\
//**************************************************\\
function RespawnAllCreeps takes nothing returns nothing
call ForGroup(deadCreeps, function GroupEnumeration)
endfunction
//**************************************************\\
//*** ResetRespawnSystem ***\\
//**************************************************\\
function ResetRespawnSystem takes nothing returns nothing
static if RESPAWN_TOGETHER then
call TimerStart(respawnTimer, RESPAWN_DELAY, true, function RespawnAllCreeps)
call PauseTimer(respawnTimer)
else
call BJDebugMsg("|cffff0000Respawn System|r: Cannot use ResetRespawnSystem when RESPAWN_TOGETHER = false.")
endif
endfunction
//**************************************************\\
//*** RestartRespawnSystem ***\\
//**************************************************\\
function RestartRespawnSystem takes nothing returns nothing
static if RESPAWN_TOGETHER then
call TimerStart(respawnTimer, RESPAWN_DELAY, true, function RespawnAllCreeps)
else
call BJDebugMsg("|cffff0000Respawn System|r: Cannot use RestartRespawnSystem when RESPAWN_TOGETHER = false.")
endif
endfunction
//**************************************************\\
private function TimerExpiration takes nothing returns nothing
call RespawnCreepX(GetTimerData(GetExpiredTimer()))
call ReleaseTimer(GetExpiredTimer())
endfunction
private function CreepDeath takes nothing returns boolean
local unit u = GetTriggerUnit()
if IsUnitInGroup(u, creeps) then
call GroupRemoveUnit(creeps, u)
call GroupAddUnit(deadCreeps, u)
static if not RESPAWN_TOGETHER then
call TimerStart(NewTimerEx(GetHandleId(u)), RESPAWN_DELAY, false, function TimerExpiration)
endif
endif
set u = null
return false
endfunction
private function GroupCreeps takes nothing returns nothing
local unit temp
local integer handleId
call GroupEnumUnitsInRect(enumG, bj_mapInitialPlayableArea, Condition(function CheckOwner))
loop
set temp = FirstOfGroup(enumG)
exitwhen temp == null
if not IsUnitType(temp, UNIT_TYPE_DEAD) then
set handleId = GetHandleId(temp)
call SaveReal(ht, handleId, StringHash("x"), GetUnitX(temp))
call SaveReal(ht, handleId, StringHash("y"), GetUnitY(temp))
call SaveReal(ht, handleId, StringHash("facing"), GetUnitFacing(temp))
call SaveInteger(ht, handleId, StringHash("owner"), GetPlayerId(GetOwningPlayer(temp)))
call SaveInteger(ht, handleId, StringHash("type"), GetUnitTypeId(temp))
call GroupAddUnit(creeps, temp)
endif
call GroupRemoveUnit(enumG, temp)
endloop
endfunction
private function CreateDialog takes nothing returns nothing
set timerWindow = CreateTimerDialog(respawnTimer)
call TimerDialogSetTitle(timerWindow, TIMER_WINDOW_TEXT)
call TimerDialogDisplay(timerWindow, true)
endfunction
private function OnInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerAddCondition(t, Condition(function CreepDeath))
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
set t = null
call GroupCreeps()
static if RESPAWN_TOGETHER then
set respawnTimer = CreateTimer()
call TimerStart(respawnTimer, RESPAWN_DELAY, true, function RespawnAllCreeps)
static if SHOW_TIMER_WINDOW then
call TimerStart(NewTimer(), 0.01, false, function CreateDialog)
endif
endif
endfunction
endlibrary
//TESH.scrollpos=0
//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