// Baradé's Unit Group Respawn System 1.0
//
// Allows killed or charmed units from a group or individually to respawn after some time.
// Respawning groups are automatically determined by default from preplaced creeps next to each other owned by player neutral aggressive.
//
// Usage:
// - Copy this code into your map script or a trigger converted into code.
// - Place some creeps on the map.
//
// You can change the default configuration by changing the code in the library UnitGroupRespawnSystemConfig.
//
// Design:
//
// The unit variables of the respawns are set to null as soon as the respawn starts, not when the unit dies/changes the owner.
// This is done on purpose, so the callbacks can still access the units by index etc.
// The system does not use vJass structs but simple JASS arrays. This allows you to go through all indices and check an index is used or not.
// The system would require some kind of global list if vJass structs were used whenever you want to get all existing instances.
// The limitation of maximum instances is the maximum array size possible in Warcraft III JASS_MAX_ARRAY_SIZE.
// Even with vJass structs you would have to specify a higher limit manually.
// The system is designed to provide a simple JASS API similar to the JASS functions provided by Blizzard.
// The callbacks work like regular trigger events.
//
// API:
//
// function TriggerRegisterUnitRespawnEvent takes trigger whichTrigger returns nothing
//
// Registers an unit respawn event for the specified trigger. This means that the trigger is evaluated and executed if
// the condition is true whenever any unit is respawned by this system. You can access the triggering respawning unit
// and index from the system via functions.
//
// function TriggerRegisterUnitRespawnStartsEvent takes trigger whichTrigger returns nothing
//
// Registers an unit respawn start event for the specified trigger. This means that the trigger is evaluated and executed if
// the condition is true whenever any unit respawn timer is started by this system. You can access the previously killed or charmed unit
// and index from the system via functions.
//
// function GetTriggerRespawnUnit takes nothing returns unit
//
// Returns the triggering respawned unit from a trigger which was evaluated or executed due to a respawn event.
// Returns the triggering killed or charmed previously respawned unit if the event is a respawn starts event.
//
// function GetTriggerRespawnUnitIndex takes nothing returns integer
//
// Returns the corresponding index of the unit respawn from the respawned unit or unit for which the respawn timer has been started of the
// evaluated or executed trigger.
//
// function PreventUnitRespawn takes unit whichUnit returns nothing
//
// Can be called in a trigger which is executed on the respawn event to prevent the actual respawn. It removes the respawned unit. The respawn has to be started manually.
//
// function GetUnitRespawnUnitIndex takes unit whichUnit returns integer
//
// Returns the corresponding index of the unit respawn matching the passed unit. If there is none, it returns -1.
//
// function GetUnitRespawnGroupIndex takes unit whichUnit returns integer
//
// function GetRespawnUnitCounter takes nothing returns integer
//
// Returns the maximum number of item respawn indices. Note that not every index in between has to be valid. This function might help integer
// loops to go through all existing item respawns. Please use IsRespawnItemValid to check if an index in between 0 and the return value of
// this function is actually used.
//
// function IsRespawnUnitValid takes integer index returns boolean
//
// function IsRespawnUnitGroupValid takes integer index returns boolean
//
// function RespawnUnit takes integer index returns boolean
//
// function RespawnAllUnits takes nothing returns nothing
//
// function StartUnitRespawn takes integer index returns nothing
//
// function StartAllUnitRespawnsNotRunning takes nothing returns nothing
//
// function AddRespawnUnit takes unit whichUnit returns integer
//
// function AddRespawnUnitPool takes unitpool whichUnitPool, real x, real y returns integer
//
// function AddRespawnUnitRandomCreep takes integer level, real x, real y returns integer
//
// function RemoveRespawnUnit takes integer index returns boolean
//
// function SetRespawnUnitEnabled takes integer index, boolean enabled returns nothing
//
// function IsRespawnUnitEnabled takes integer index returns boolean
//
// function GetRespawnUnitTimer takes integer index returns timer
//
// function GetRespawnUnitType takes integer index returns integer
//
// function SetRespawnUnitTimeout takes integer index, real timeout returns nothing
//
// function GetRespawnUnitTimeout takes integer index returns real
//
// function SetRespawnUnitOwner takes integer index, player owner returns nothing
//
// function GetRespawnUnitOwner takes integer index returns player
//
// function SetRespawnUnitFacing takes integer index, real facing returns nothing
//
// function GetRespawnUnitFacing takes integer index returns real
//
// function SetRespawnUnitX takes integer index, real x returns nothing
//
// function GetRespawnUnitX takes integer index returns real
//
// function SetRespawnUnitY takes integer index, real y returns nothing
//
// function GetRespawnUnitY takes integer index returns real
//
// function SetRespawnUnitUseDyingLoc takes integer index, boolean use returns nothing
//
// function GetRespawnUnitUseDyingLoc takes integer index returns boolean
//
// function SetRespawnUnit takes integer index, unit whichUnit returns nothing
//
// function GetRespawnUnit takes integer index returns unit
//
// function SetRespawnUnitPool takes integer index, unitpool whichUnitPool returns nothing
//
// function GetRespawnUnitPool takes integer index returns unitpool
//
// function SetRespawnUnitLevel takes integer index, integer level returns nothing
//
// function GetRespawnUnitLevel takes integer index returns integer
//
// function GetRespawnUnitGroupCounter takes nothing returns integer
//
// function SetRespawnUnitGroupIndex takes integer index, integer groupIndex returns nothing
//
// function GetRespawnUnitGroupIndex takes integer index returns integer
//
// function AddRespawnUnitGroup takes nothing returns integer
//
// function AddRespawnUnitGroupFromUnit takes unit whichUnit returns integer
//
// function AddRespawnUnitGroupFromUnitPool takes unitpool whichUnitPool, integer countMembers, real x, real y, real range returns integer
//
// function AddRespawnUnitGroupFromRandomCreepLevel takes integer minCreepLevel, integer maxCreepLevel, integer countMembers, real x, real y, real range returns integer
//
// function RemoveRespawnUnitGroup takes integer index returns boolean
//
// function SetRespawnUnitGroupTimeout takes integer index, real timeout returns nothing
//
// function GetRespawnUnitGroupTimeout takes integer index returns real
//
// function SetRespawnUnitGroupEnabled takes integer index, boolean enabled returns nothing
//
// function IsRespawnUnitGroupEnabled takes integer index returns boolean
//
// function GetRespawnUnitGroupUnits takes integer index returns group
//
// function RespawnUnitGroup takes integer index returns boolean
//
// function StartUnitGroupRespawn takes integer index returns nothing
//
library UnitGroupRespawnSystemConfig
globals
// The default delay until a unit will be respawned.
public constant real DEFAULT_TIMEOUT = 30.0
// All preplaced units owned by the CREEPS_OWNER player on the map will automatically respawn if this value is true. Otherwise, you will have to add them manually.
public constant boolean AUTO_ADD_ALL_PREPLACED_CREEPS = true
// Creates unit group respawns from preplaced creeps next to each other if this value is true. Otherwise, it will create separate unit respawns per creep.
public constant boolean AUTO_ADDED_GROUPS = true
// Defines the maximum distance between preplaced creep units to belong to the same respawn group if AUTO_ADDED_GROUPS is true.
public constant real AUTO_ADDED_GROUP_MAX_DISTANCE = 800.0
// All players who preplaced units are added as respawning groups for.
public constant force AUTO_ADDED_GROUP_PLAYERS = CreateForce()
// All auto added unit respawns and respawn groups will drop random items when the whole group has been killed or charmed when this value is true. The level of the dropped item will be the maximum unit level of the group.
// Make sure that all item types have set the field "Stats - Include As Random Choice" to the correct value in the object editor.
public constant boolean AUTO_ADDED_DROP_RANDOM_ITEMS = true
// Determines a unit's level by its unit type rather than the actual unit. This helps to get a lower level if the unit levels are changed manually during the game.
public constant boolean GET_UNIT_LEVEL_BY_TYPE = true
// Shows the eyecandy on respawning hero revivals if set to true. Otherwise, the effect is not shown.
public constant boolean HERO_RESPAWN_DO_EYECANDY = true
// Avoids permanent removal if too many heroes from the same player died.
public constant boolean SET_MAX_DEATH_TIME_TO_UNITS = true
endglobals
static if (AUTO_ADD_ALL_PREPLACED_CREEPS) then
private function IsUnitLivingNonStructure takes nothing returns boolean
return IsUnitAliveBJ(GetFilterUnit()) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE)
endfunction
// Redefine this function to add different preplaced creeps.
public function AddAllUserSpecifiedPreplacedCreeps takes group allCreeps returns nothing
call GroupEnumUnitsOfPlayer(allCreeps, Player(PLAYER_NEUTRAL_AGGRESSIVE), Filter(function IsUnitLivingNonStructure))
endfunction
private module Init
private static method onInit takes nothing returns nothing
call ForceAddPlayer(AUTO_ADDED_GROUP_PLAYERS, Player(PLAYER_NEUTRAL_AGGRESSIVE))
endmethod
endmodule
private struct S
implement Init
endstruct
endif
endlibrary
library UnitGroupRespawnSystem requires UnitGroupRespawnSystemConfig
globals
constant integer UNIT_RESPAWN_TYPE_UNIT = 0
constant integer UNIT_RESPAWN_TYPE_UNITPOOL = 1
constant integer UNIT_RESPAWN_TYPE_RANDOM_CREEP_LEVEL = 2
private integer respawnUnitCounter = 0
private integer respawnUnitFreeIndex = 0
private boolean array respawnUnitIsValid
private integer array respawnUnitType
private unit array respawnUnitUnit
private integer array respawnUnitHandleId
private integer array respawnUnitUnitTypeId
private unitpool array respawnUnitPool
private integer array respawnUnitRandomCreepLevel
private player array respawnUnitOwner
private real array respawnUnitFacing
private real array respawnUnitX
private real array respawnUnitY
private boolean array respawnUnitUseDyingLoc
private timer array respawnUnitTimer
private real array respawnUnitTimeout
private boolean array respawnUnitEnabled
private integer array respawnUnitGroupIndex
private boolean array respawnUnitReadyForRespawn
private integer callbackRespawnTriggersCounter = 0
private trigger array callbackRespawnTriggers
private integer callbackRespawnStartsTriggersCounter = 0
private trigger array callbackRespawnStartsTriggers
private unit callbackUnit = null
private integer callbackIndex = -1
private integer respawnUnitGroupCounter = 0
private integer respawnUnitGroupFreeIndex = 0
private boolean array respawnUnitGroupIsValid
private group array respawnUnitGroup
private timer array respawnUnitGroupTimer
private real array respawnUnitGroupTimeout
private boolean array respawnUnitGroupEnabled
private trigger unitDeathOrCharmTrigger = CreateTrigger()
private hashtable respawnUnitHashTable = InitHashtable()
private unit filterUnit = null
private player filterOwner = null
endglobals
function GetTriggerRespawnUnit takes nothing returns unit
return callbackUnit
endfunction
function GetTriggerRespawnUnitIndex takes nothing returns integer
return callbackIndex
endfunction
function TriggerRegisterUnitRespawnEvent takes trigger whichTrigger returns nothing
local integer index = callbackRespawnTriggersCounter
set callbackRespawnTriggers[index] = whichTrigger
set callbackRespawnTriggersCounter = callbackRespawnTriggersCounter + 1
endfunction
private function EvaluateAndExecuteCallbackRespawnTriggers takes integer index returns nothing
local integer i = 0
loop
exitwhen (i >= callbackRespawnTriggersCounter)
set callbackUnit = respawnUnitUnit[index]
set callbackIndex = index
call ConditionalTriggerExecute(callbackRespawnTriggers[i])
set i = i + 1
endloop
endfunction
function TriggerRegisterUnitRespawnStartsEvent takes trigger whichTrigger returns nothing
local integer index = callbackRespawnStartsTriggersCounter
set callbackRespawnStartsTriggers[index] = whichTrigger
set callbackRespawnStartsTriggersCounter = callbackRespawnStartsTriggersCounter + 1
endfunction
private function EvaluateAndExecuteCallbackRespawnStartsTriggers takes integer index returns nothing
local integer i = 0
loop
exitwhen (i >= callbackRespawnStartsTriggersCounter)
set callbackUnit = respawnUnitUnit[index]
set callbackIndex = index
call ConditionalTriggerExecute(callbackRespawnStartsTriggers[i])
set i = i + 1
endloop
endfunction
private function ClearRespawnUnitIndex takes integer handleID returns nothing
if (HaveSavedInteger(respawnUnitHashTable, handleID, 0)) then
call FlushChildHashtable(respawnUnitHashTable, handleID)
endif
endfunction
private function GetRespawnUnitIndexByHandleID takes integer handleID returns integer
if (HaveSavedInteger(respawnUnitHashTable, handleID, 0)) then
return LoadInteger(respawnUnitHashTable, handleID, 0)
endif
return -1
endfunction
private function GetUnitRespawnGroupIndexByHandleID takes integer handleID returns integer
if (HaveSavedInteger(respawnUnitHashTable, handleID, 1)) then
return LoadInteger(respawnUnitHashTable, handleID, 1)
endif
return -1
endfunction
function GetUnitRespawnUnitIndex takes unit whichUnit returns integer
return GetRespawnUnitIndexByHandleID(GetHandleId(whichUnit))
endfunction
function GetUnitRespawnGroupIndex takes unit whichUnit returns integer
return GetUnitRespawnGroupIndexByHandleID(GetHandleId(whichUnit))
endfunction
function GetRespawnUnitCounter takes nothing returns integer
return respawnUnitCounter
endfunction
function IsRespawnUnitValid takes integer index returns boolean
if (index < 0) then
return false
endif
return respawnUnitIsValid[index]
endfunction
function IsRespawnUnitGroupValid takes integer index returns boolean
if (index < 0) then
return false
endif
return respawnUnitGroupIsValid[index]
endfunction
function RespawnUnit takes integer index returns boolean
local boolean add = false
local integer groupIndex = respawnUnitGroupIndex[index]
if (not IsRespawnUnitValid(index)) then
return false
endif
if (respawnUnitType[index] == UNIT_RESPAWN_TYPE_UNIT) then
if (IsUnitType(respawnUnitUnit[index], UNIT_TYPE_HERO) and respawnUnitUnit[index] != null) then
call ReviveHero(respawnUnitUnit[index], respawnUnitX[index], respawnUnitY[index], UnitGroupRespawnSystemConfig_HERO_RESPAWN_DO_EYECANDY)
else
set respawnUnitUnit[index] = CreateUnit(respawnUnitOwner[index], respawnUnitUnitTypeId[index], respawnUnitX[index], respawnUnitY[index], respawnUnitFacing[index])
set add = true
endif
elseif (respawnUnitType[index] == UNIT_RESPAWN_TYPE_UNITPOOL) then
set respawnUnitUnit[index] = PlaceRandomUnit(respawnUnitPool[index], respawnUnitOwner[index], respawnUnitX[index], respawnUnitY[index], respawnUnitFacing[index])
set add = true
elseif (respawnUnitType[index] == UNIT_RESPAWN_TYPE_RANDOM_CREEP_LEVEL) then
set respawnUnitUnit[index] = CreateUnit(respawnUnitOwner[index], ChooseRandomCreep(respawnUnitRandomCreepLevel[index]), respawnUnitX[index], respawnUnitY[index], respawnUnitFacing[index])
set add = true
endif
set respawnUnitReadyForRespawn[index] = false
static if (UnitGroupRespawnSystemConfig_SET_MAX_DEATH_TIME_TO_UNITS) then
if (IsUnitType(respawnUnitUnit[index], UNIT_TYPE_HERO)) then
call BlzSetUnitRealField(respawnUnitUnit[index], UNIT_RF_DEATH_TIME, 99999999.0)
endif
endif
set respawnUnitHandleId[index] = GetHandleId(respawnUnitUnit[index])
call SaveInteger(respawnUnitHashTable, respawnUnitHandleId[index], 0, index)
if (add and IsRespawnUnitGroupValid(groupIndex)) then
call GroupAddUnit(respawnUnitGroup[groupIndex], respawnUnitUnit[index])
endif
call EvaluateAndExecuteCallbackRespawnTriggers(index)
return true
endfunction
function RespawnAllUnits takes nothing returns nothing
local integer i = 0
loop
exitwhen (i >= GetRespawnUnitCounter())
if (IsRespawnUnitValid(i) and respawnUnitUnit[i] == null) then
call RespawnUnit(i)
endif
set i = i + 1
endloop
endfunction
function PreventUnitRespawn takes unit whichUnit returns nothing
local integer index = GetUnitRespawnUnitIndex(whichUnit)
if (IsRespawnUnitValid(index)) then
if (IsUnitType(respawnUnitUnit[index], UNIT_TYPE_HERO)) then
call KillUnit(whichUnit)
else
call ClearRespawnUnitIndex(respawnUnitHandleId[index])
set respawnUnitHandleId[index] = 0
call RemoveUnit(whichUnit)
set whichUnit = null
endif
endif
endfunction
private function TimerFunctionRespawnUnit takes nothing returns nothing
local integer index = LoadInteger(respawnUnitHashTable, GetHandleId(GetExpiredTimer()), 0)
call RespawnUnit(index)
endfunction
function StartUnitRespawn takes integer index returns nothing
call EvaluateAndExecuteCallbackRespawnStartsTriggers(index)
if (respawnUnitHandleId[index] != 0) then
call ClearRespawnUnitIndex(respawnUnitHandleId[index])
endif
set respawnUnitUnit[index] = null
set respawnUnitHandleId[index] = 0
call TimerStart(respawnUnitTimer[index], respawnUnitTimeout[index], false, function TimerFunctionRespawnUnit)
set respawnUnitReadyForRespawn[index] = false
endfunction
function StartAllUnitRespawnsNotRunning takes nothing returns nothing
local integer i = 0
loop
exitwhen (i >= GetRespawnUnitCounter())
if (IsRespawnUnitValid(i) and respawnUnitUnit[i] == null and TimerGetElapsed(respawnUnitTimer[i]) <= 0.0) then
call StartUnitRespawn(i)
endif
set i = i + 1
endloop
endfunction
private function AddRespawnUnitDefault takes integer index, real x, real y returns nothing
set respawnUnitIsValid[index] = true
set respawnUnitX[index] = x
set respawnUnitY[index] = y
set respawnUnitFacing[index] = GetRandomDirectionDeg()
set respawnUnitUseDyingLoc[index] = false
set respawnUnitOwner[index] = Player(PLAYER_NEUTRAL_AGGRESSIVE)
set respawnUnitTimer[index] = CreateTimer()
set respawnUnitTimeout[index] = UnitGroupRespawnSystemConfig_DEFAULT_TIMEOUT
call SaveInteger(respawnUnitHashTable, GetHandleId(respawnUnitTimer[index]), 0, index)
set respawnUnitEnabled[index] = true
set respawnUnitGroupIndex[index] = -1
set respawnUnitReadyForRespawn[index] = true
loop
set respawnUnitFreeIndex = respawnUnitFreeIndex + 1
exitwhen (not IsRespawnUnitValid(respawnUnitFreeIndex))
endloop
if (respawnUnitFreeIndex >= JASS_MAX_ARRAY_SIZE) then
call BJDebugMsg("Warning: Reached Warcraft maximum array size for respawn units: " + I2S(respawnUnitFreeIndex))
endif
if (index >= respawnUnitCounter) then
set respawnUnitCounter = index + 1
endif
endfunction
function AddRespawnUnit takes unit whichUnit returns integer
local integer index = respawnUnitFreeIndex
set respawnUnitType[index] = UNIT_RESPAWN_TYPE_UNIT
set respawnUnitUnit[index] = whichUnit
static if (UnitGroupRespawnSystemConfig_SET_MAX_DEATH_TIME_TO_UNITS) then
if (IsUnitType(respawnUnitUnit[index], UNIT_TYPE_HERO)) then
call BlzSetUnitRealField(respawnUnitUnit[index], UNIT_RF_DEATH_TIME, 99999999.0)
endif
endif
set respawnUnitHandleId[index] = GetHandleId(whichUnit)
set respawnUnitUnitTypeId[index] = GetUnitTypeId(whichUnit)
set respawnUnitPool[index] = null
call AddRespawnUnitDefault(index, GetUnitX(whichUnit), GetUnitY(whichUnit))
set respawnUnitFacing[index] = GetUnitFacing(whichUnit)
set respawnUnitOwner[index] = GetOwningPlayer(whichUnit)
set respawnUnitReadyForRespawn[index] = false
call SaveInteger(respawnUnitHashTable, respawnUnitHandleId[index], 0, index)
return index
endfunction
function AddRespawnUnitPool takes unitpool whichUnitPool, real x, real y returns integer
local integer index = respawnUnitFreeIndex
set respawnUnitType[index] = UNIT_RESPAWN_TYPE_UNITPOOL
set respawnUnitUnit[index] = null
set respawnUnitHandleId[index] = 0
set respawnUnitUnitTypeId[index] = 0
set respawnUnitPool[index] = whichUnitPool
call AddRespawnUnitDefault(index, x, y)
call RespawnUnit(index)
return index
endfunction
function AddRespawnUnitRandomCreep takes integer level, real x, real y returns integer
local integer index = respawnUnitFreeIndex
set respawnUnitType[index] = UNIT_RESPAWN_TYPE_RANDOM_CREEP_LEVEL
set respawnUnitUnit[index] = null
set respawnUnitHandleId[index] = 0
set respawnUnitUnitTypeId[index] = 0
set respawnUnitPool[index] = null
set respawnUnitRandomCreepLevel[index] = level
call AddRespawnUnitDefault(index, x, y)
call RespawnUnit(index)
return index
endfunction
function RemoveRespawnUnit takes integer index returns boolean
if (IsRespawnUnitValid(index)) then
set respawnUnitIsValid[index] = false
if (respawnUnitUnit[index] != null) then
call ClearRespawnUnitIndex(GetHandleId(respawnUnitUnit[index]))
endif
set respawnUnitTimeout[index] = 0
set respawnUnitType[index] = 0
set respawnUnitUnit[index] = null
set respawnUnitHandleId[index] = 0
set respawnUnitUnitTypeId[index] = 0
set respawnUnitPool[index] = null
set respawnUnitRandomCreepLevel[index] = 0
set respawnUnitUseDyingLoc[index] = false
call PauseTimer(respawnUnitTimer[index])
call FlushChildHashtable(respawnUnitHashTable, GetHandleId(respawnUnitTimer[index]))
call DestroyTimer(respawnUnitTimer[index])
set respawnUnitFreeIndex = index
if (index == respawnUnitCounter - 1) then
set respawnUnitCounter = respawnUnitCounter - 1
endif
return true
endif
return false
endfunction
function SetRespawnUnitEnabled takes integer index, boolean enabled returns nothing
set respawnUnitEnabled[index] = enabled
endfunction
function IsRespawnUnitEnabled takes integer index returns boolean
return respawnUnitEnabled[index]
endfunction
function GetRespawnUnitTimer takes integer index returns timer
return respawnUnitTimer[index]
endfunction
function GetRespawnUnitType takes integer index returns integer
return respawnUnitType[index]
endfunction
function SetRespawnUnitTimeout takes integer index, real timeout returns nothing
set respawnUnitTimeout[index] = timeout
endfunction
function GetRespawnUnitTimeout takes integer index returns real
return respawnUnitTimeout[index]
endfunction
function SetRespawnUnitOwner takes integer index, player owner returns nothing
set respawnUnitOwner[index] = owner
endfunction
function GetRespawnUnitOwner takes integer index returns player
return respawnUnitOwner[index]
endfunction
function SetRespawnUnitFacing takes integer index, real facing returns nothing
set respawnUnitFacing[index] = facing
endfunction
function GetRespawnUnitFacing takes integer index returns real
return respawnUnitFacing[index]
endfunction
function SetRespawnUnitX takes integer index, real x returns nothing
set respawnUnitX[index] = x
endfunction
function GetRespawnUnitX takes integer index returns real
return respawnUnitX[index]
endfunction
function SetRespawnUnitY takes integer index, real y returns nothing
set respawnUnitX[index] = y
endfunction
function GetRespawnUnitY takes integer index returns real
return respawnUnitY[index]
endfunction
function SetRespawnUnitUseDyingLoc takes integer index, boolean use returns nothing
set respawnUnitUseDyingLoc[index] = use
endfunction
function GetRespawnUnitUseDyingLoc takes integer index returns boolean
return respawnUnitUseDyingLoc[index]
endfunction
function SetRespawnUnit takes integer index, unit whichUnit returns nothing
local integer groupIndex = respawnUnitGroupIndex[index]
local boolean validRespawnUnitGroup = IsRespawnUnitGroupValid(groupIndex)
local integer handleId = GetHandleId(whichUnit)
if (respawnUnitUnit[index] != null) then
if (validRespawnUnitGroup) then
call GroupRemoveUnit(respawnUnitGroup[groupIndex], respawnUnitUnit[index])
endif
call ClearRespawnUnitIndex(GetHandleId(respawnUnitUnit[index]))
endif
set respawnUnitUnit[index] = whichUnit
set respawnUnitHandleId[index] = handleId
call SaveInteger(respawnUnitHashTable, handleId, 0, index)
set respawnUnitReadyForRespawn[index] = false
if (validRespawnUnitGroup) then
call GroupAddUnit(respawnUnitGroup[groupIndex], whichUnit)
endif
endfunction
function GetRespawnUnit takes integer index returns unit
return respawnUnitUnit[index]
endfunction
function SetRespawnUnitPool takes integer index, unitpool whichUnitPool returns nothing
set respawnUnitPool[index] = whichUnitPool
endfunction
function GetRespawnUnitPool takes integer index returns unitpool
return respawnUnitPool[index]
endfunction
function SetRespawnUnitLevel takes integer index, integer level returns nothing
set respawnUnitRandomCreepLevel[index] = level
endfunction
function GetRespawnUnitLevel takes integer index returns integer
return respawnUnitRandomCreepLevel[index]
endfunction
function GetRespawnUnitGroupCounter takes nothing returns integer
return respawnUnitGroupCounter
endfunction
function SetRespawnUnitGroupIndex takes integer index, integer groupIndex returns nothing
if (IsRespawnUnitGroupValid(groupIndex)) then
if (IsRespawnUnitGroupValid(respawnUnitGroupIndex[index]) and respawnUnitUnit[index] != null) then
call GroupRemoveUnit(respawnUnitGroup[respawnUnitGroupIndex[index]], respawnUnitUnit[index])
endif
set respawnUnitGroupIndex[index] = groupIndex
call GroupAddUnit(respawnUnitGroup[groupIndex], respawnUnitUnit[index])
endif
endfunction
function GetRespawnUnitGroupIndex takes integer index returns integer
return respawnUnitGroupIndex[index]
endfunction
function AddRespawnUnitGroup takes nothing returns integer
local integer index = respawnUnitGroupFreeIndex
set respawnUnitGroupIsValid[index] = true
set respawnUnitGroup[index] = CreateGroup()
set respawnUnitGroupTimer[index] = CreateTimer()
set respawnUnitGroupTimeout[index] = UnitGroupRespawnSystemConfig_DEFAULT_TIMEOUT
call SaveInteger(respawnUnitHashTable, GetHandleId(respawnUnitGroupTimer[index]), 0, index)
set respawnUnitGroupEnabled[index] = true
loop
set respawnUnitGroupFreeIndex = respawnUnitGroupFreeIndex + 1
exitwhen (not IsRespawnUnitGroupValid(respawnUnitGroupFreeIndex))
endloop
if (respawnUnitGroupFreeIndex >= JASS_MAX_ARRAY_SIZE) then
call BJDebugMsg("Warning: Reached Warcraft maximum array size for respawn units: " + I2S(respawnUnitGroupFreeIndex))
endif
if (index >= respawnUnitGroupCounter) then
set respawnUnitGroupCounter = index + 1
endif
return index
endfunction
private function IsLivingUnitWithRespawn takes nothing returns boolean
local unit currentUnit = GetFilterUnit()
local player currentOwner = GetOwningPlayer(filterUnit)
local integer respawnUnitIndex = GetUnitRespawnUnitIndex(currentUnit)
local boolean result = currentUnit != filterUnit and IsUnitAliveBJ(currentUnit) and not IsUnitType(currentUnit, UNIT_TYPE_STRUCTURE) and (currentOwner == filterOwner or IsPlayerInForce(currentOwner, UnitGroupRespawnSystemConfig_AUTO_ADDED_GROUP_PLAYERS)) and IsRespawnUnitValid(respawnUnitIndex) and IsRespawnUnitGroupValid(GetRespawnUnitGroupIndex(respawnUnitIndex))
set currentUnit = null
set currentOwner = null
return result
endfunction
function AddRespawnUnitGroupFromUnit takes unit whichUnit returns integer
local group allNearbyUnitsFromTheSameOwner = CreateGroup()
local unit first = null
local integer groupIndex = -1
local integer index = -1
set filterUnit = whichUnit
set filterOwner = GetOwningPlayer(whichUnit)
call GroupEnumUnitsInRange(allNearbyUnitsFromTheSameOwner, GetUnitX(whichUnit), GetUnitY(whichUnit), UnitGroupRespawnSystemConfig_AUTO_ADDED_GROUP_MAX_DISTANCE, Filter(function IsLivingUnitWithRespawn))
set filterUnit = null
set filterOwner = null
set first = FirstOfGroup(allNearbyUnitsFromTheSameOwner)
call GroupClear(allNearbyUnitsFromTheSameOwner)
call DestroyGroup(allNearbyUnitsFromTheSameOwner)
set allNearbyUnitsFromTheSameOwner = null
if (first != null) then
set groupIndex = GetRespawnUnitGroupIndex(GetUnitRespawnUnitIndex(first))
set first = null
else
set groupIndex = AddRespawnUnitGroup()
endif
set index = AddRespawnUnit(whichUnit)
call SetRespawnUnitGroupIndex(index, groupIndex)
return groupIndex
endfunction
private function PolarProjectionX takes real x, real dist, real angle returns real
return x + dist * Cos(angle * bj_DEGTORAD)
endfunction
private function PolarProjectionY takes real y, real dist, real angle returns real
return y + dist * Sin(angle * bj_DEGTORAD)
endfunction
private function GetRandomLocInRange takes real x, real y, real range returns location
local real dist = GetRandomReal(0.0, range)
local real angle = GetRandomDirectionDeg()
return Location(PolarProjectionX(x, dist, angle), PolarProjectionY(y, dist, angle))
endfunction
function AddRespawnUnitGroupFromUnitPool takes unitpool whichUnitPool, integer countMembers, real x, real y, real range returns integer
local integer groupIndex = AddRespawnUnitGroup()
local location whichLocation = null
local integer i = 0
loop
exitwhen (i >= countMembers)
set whichLocation = GetRandomLocInRange(x, y, range)
call SetRespawnUnitGroupIndex(AddRespawnUnitPool(whichUnitPool, GetLocationY(whichLocation), GetLocationY(whichLocation)), groupIndex)
call RemoveLocation(whichLocation)
set whichLocation = null
set i = i + 1
endloop
return groupIndex
endfunction
function AddRespawnUnitGroupFromRandomCreepLevel takes integer minCreepLevel, integer maxCreepLevel, integer countMembers, real x, real y, real range returns integer
local integer groupIndex = AddRespawnUnitGroup()
local location whichLocation = null
local integer i = 0
loop
exitwhen (i >= countMembers)
set whichLocation = GetRandomLocInRange(x, y, range)
call SetRespawnUnitGroupIndex(AddRespawnUnitRandomCreep(GetRandomInt(minCreepLevel, maxCreepLevel), GetLocationY(whichLocation), GetLocationY(whichLocation)), groupIndex)
call RemoveLocation(whichLocation)
set whichLocation = null
set i = i + 1
endloop
return groupIndex
endfunction
function RemoveRespawnUnitGroup takes integer index returns boolean
if (IsRespawnUnitValid(index)) then
set respawnUnitGroupIsValid[index] = false
set respawnUnitGroupEnabled[index] = false
if (respawnUnitGroup[index] != null) then
call GroupClear(respawnUnitGroup[index])
call DestroyGroup(respawnUnitGroup[index])
set respawnUnitGroup[index] = null
endif
call PauseTimer(respawnUnitGroupTimer[index])
call FlushChildHashtable(respawnUnitHashTable, GetHandleId(respawnUnitGroupTimer[index]))
call DestroyTimer(respawnUnitGroupTimer[index])
set respawnUnitFreeIndex = index
if (index == respawnUnitCounter - 1) then
set respawnUnitCounter = respawnUnitCounter - 1
endif
return true
endif
return false
endfunction
function SetRespawnUnitGroupTimeout takes integer index, real timeout returns nothing
set respawnUnitGroupTimeout[index] = timeout
endfunction
function GetRespawnUnitGroupTimeout takes integer index returns real
return respawnUnitGroupTimeout[index]
endfunction
function SetRespawnUnitGroupEnabled takes integer index, boolean enabled returns nothing
set respawnUnitGroupEnabled[index] = enabled
endfunction
function IsRespawnUnitGroupEnabled takes integer index returns boolean
return respawnUnitGroupEnabled[index]
endfunction
function GetRespawnUnitGroupUnits takes integer index returns group
return respawnUnitGroup[index]
endfunction
function RespawnUnitGroup takes integer index returns boolean
local integer i = 0
if (not IsRespawnUnitGroupValid(index)) then
return false
endif
// We do not know the members anymore since we have removed them from the group. Hence, we need to find all matching respawns.
set i = 0
loop
exitwhen (i == GetRespawnUnitCounter())
if (IsRespawnUnitValid(i) and GetRespawnUnitGroupIndex(i) == index) then
call RespawnUnit(i)
endif
set i = i + 1
endloop
return true
endfunction
private function TimerFunctionRespawnUnitGroup takes nothing returns nothing
local integer index = LoadInteger(respawnUnitHashTable, GetHandleId(GetExpiredTimer()), 0)
call RespawnUnitGroup(index)
endfunction
function StartUnitGroupRespawn takes integer index returns nothing
local unit member = null
local integer memberIndex = -1
local integer i = 0
loop
exitwhen (i == BlzGroupGetSize(respawnUnitGroup[index]))
set member = BlzGroupUnitAt(respawnUnitGroup[index], i)
set memberIndex = GetRespawnUnitIndexByHandleID(GetHandleId(member))
set respawnUnitReadyForRespawn[memberIndex] = false
set member = null
call EvaluateAndExecuteCallbackRespawnStartsTriggers(memberIndex)
set i = i + 1
endloop
// Cleanup since we do not know how long the unit will decay etc.
set i = 0
loop
exitwhen (i == BlzGroupGetSize(respawnUnitGroup[index]))
set member = BlzGroupUnitAt(respawnUnitGroup[index], i)
set memberIndex = GetRespawnUnitIndexByHandleID(GetHandleId(member))
if (not IsUnitType(member, UNIT_TYPE_HERO)) then
if (respawnUnitHandleId[memberIndex] != 0) then
call ClearRespawnUnitIndex(respawnUnitHandleId[memberIndex])
endif
set respawnUnitUnit[memberIndex] = null
set respawnUnitHandleId[memberIndex] = 0
endif
set member = null
set i = i + 1
endloop
call GroupClear(respawnUnitGroup[index])
call TimerStart(respawnUnitGroupTimer[index], respawnUnitGroupTimeout[index], false, function TimerFunctionRespawnUnitGroup)
endfunction
private function TriggerConditionRespawnUnit takes nothing returns boolean
local integer index = GetUnitRespawnUnitIndex(GetTriggerUnit())
return IsRespawnUnitValid(index) and IsRespawnUnitEnabled(index)
endfunction
static if (UnitGroupRespawnSystemConfig_GET_UNIT_LEVEL_BY_TYPE) then
private function GetUnitLevelByType takes integer unitTypeId returns integer
local unit dummy = CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE), unitTypeId, 0.0, 0.0, 0.0)
local integer result = BlzGetUnitIntegerField(dummy , UNIT_IF_LEVEL)
call RemoveUnit(dummy)
set dummy = null
return result
endfunction
endif
private function GetMaxUnitLevelFromGroup takes group whichGroup returns integer
local integer maxLevel = 0
local integer unitLevel = 0
local integer i = 0
loop
exitwhen (i == BlzGroupGetSize(whichGroup))
static if (UnitGroupRespawnSystemConfig_GET_UNIT_LEVEL_BY_TYPE) then
set unitLevel = GetUnitLevelByType(GetUnitTypeId(BlzGroupUnitAt(whichGroup, i)))
else
set unitLevel = GetUnitLevel(BlzGroupUnitAt(whichGroup, i))
endif
set maxLevel = IMaxBJ(unitLevel, maxLevel)
set i = i + 1
endloop
return maxLevel
endfunction
private function IsUnitGroupReadyForRespawnEnum takes nothing returns nothing
local integer index = GetRespawnUnitIndexByHandleID(GetHandleId(GetEnumUnit()))
if not respawnUnitReadyForRespawn[index] then
set bj_isUnitGroupDeadResult = false
endif
endfunction
private function IsUnitGroupReadyForRespawn takes group g returns boolean
set bj_isUnitGroupDeadResult = true
call ForGroup(g, function IsUnitGroupReadyForRespawnEnum)
return bj_isUnitGroupDeadResult
endfunction
private function TriggerActionRespawnUnit takes nothing returns nothing
local unit triggerUnit = GetTriggerUnit()
local integer index = GetUnitRespawnUnitIndex(triggerUnit)
local integer groupIndex = GetRespawnUnitGroupIndex(index)
if (GetRespawnUnitUseDyingLoc(index)) then
call SetRespawnUnitFacing(index, GetUnitFacing(triggerUnit))
call SetRespawnUnitX(index, GetUnitX(triggerUnit))
call SetRespawnUnitY(index, GetUnitY(triggerUnit))
endif
set respawnUnitReadyForRespawn[index] = true
if (IsRespawnUnitGroupValid(groupIndex) and IsRespawnUnitGroupEnabled(groupIndex)) then
if (IsUnitGroupReadyForRespawn(GetRespawnUnitGroupUnits(groupIndex))) then
static if (UnitGroupRespawnSystemConfig_AUTO_ADDED_DROP_RANDOM_ITEMS) then
call UnitDropItem(triggerUnit, ChooseRandomItem(GetMaxUnitLevelFromGroup(GetRespawnUnitGroupUnits(groupIndex))))
endif
call StartUnitGroupRespawn(groupIndex)
endif
else
call StartUnitRespawn(index)
endif
set triggerUnit = null
endfunction
private function AddRespawnUnitGroupFromEnumUnit takes nothing returns nothing
static if (UnitGroupRespawnSystemConfig_AUTO_ADDED_GROUPS) then
call AddRespawnUnitGroupFromUnit(GetEnumUnit())
else
call AddRespawnUnit(GetEnumUnit())
endif
endfunction
static if (UnitGroupRespawnSystemConfig_AUTO_ADD_ALL_PREPLACED_CREEPS) then
private function AddAllPreplacedCreeps takes nothing returns nothing
local group allCreeps = CreateGroup()
call UnitGroupRespawnSystemConfig_AddAllUserSpecifiedPreplacedCreeps(allCreeps)
call ForGroup(allCreeps, function AddRespawnUnitGroupFromEnumUnit)
call GroupClear(allCreeps)
call DestroyGroup(allCreeps)
set allCreeps = null
endfunction
endif
private module Init
private static method onInit takes nothing returns nothing
call TriggerRegisterAnyUnitEventBJ(unitDeathOrCharmTrigger, EVENT_PLAYER_UNIT_DEATH)
call TriggerRegisterAnyUnitEventBJ(unitDeathOrCharmTrigger, EVENT_PLAYER_UNIT_CHANGE_OWNER)
call TriggerAddCondition(unitDeathOrCharmTrigger, Condition(function TriggerConditionRespawnUnit))
call TriggerAddAction(unitDeathOrCharmTrigger, function TriggerActionRespawnUnit)
static if (UnitGroupRespawnSystemConfig_AUTO_ADD_ALL_PREPLACED_CREEPS) then
call AddAllPreplacedCreeps()
endif
endmethod
endmodule
private struct S
implement Init
endstruct
private function RemoveUnitCleanup takes unit whichUnit returns nothing
local integer handleID = GetHandleId(whichUnit)
call ClearRespawnUnitIndex(handleID)
endfunction
hook RemoveUnit RemoveUnitCleanup
endlibrary