- Joined
- Mar 25, 2005
- Messages
- 252
UnitIndexer v0.91 and UnitStructer v0.91
This thread contains two small systems: UnitIndexer and UnitStructer (both of which use vJass).
UnitStructer creates a struct for each unit that enters the map and destroys the struct after the unit has been removed from the game. This struct is stored to the unit's UserData also known as Custom Value.
UnitIndexer does the same except that it 'creates' indexes instead of structs. The creation algorithm it uses is basically the same and produces the same integers as the one of structs, but is inlined and a bit faster. UnitIndexer is basically as light as any system that does the same can be.
The index or struct of a unit can be acquired with a GetUnitUserData call or in GUI with 'Unit - Custom Value of Unit'. The use of SetUnitUserData or 'Unit - Set Custom Value of Unit' screws up the system and as such is prohibited. Also you can't use RemoveUnit() or 'Unit - Remove' on living units. Instead you either have to use RemoveIndexerUnit() or 'Unit - Hide' and 'Unit - Explode'.
The structs created by the UnitStructer can be used just like any other structs are except that you must not destroy the ones that the system itself has assigned to units.
Both these structs as well as the indexes created by the UnitIndexer can be used to make things multi-unit instanceable the same way player numbers can be used to make things multi-player instanceable.
[jass=JASS Example]set someArrayVariable[GetUnitUserData(u)] = data
set data = someArrayVariable[GetUnitUserData(u)][/code][TRIGGER=GUI Example]Set someArrayVariable[(Custom value of (Last created unit))] = data
Set data = someArrayVariable[(Custom value of (Last created unit))]
[/TRIGGER]
Take note that these indexes as well as structs are recycled when units die meaning that values stored to arrays using them as indexes aren't always initialized or nulled unless you do that yourself.[trigger=GUI Example]NullUnitData
Events
Unit - A unit enters (Playable map area)
Conditions
Actions
Set someArrayVariable[(Custom value of (Triggering unit))] = 0
[/trigger]
Anything that uses the UnitIndexer works with the UnitStructer, but not always the other way around. Anything that uses Cohadar's PUI can be made to work with either of these by using this:
Last important note: neither one of these systems are active by the time structs' onInit methods are called. This means that units won't have indexes or structs by the time of their execution.
This thread contains two small systems: UnitIndexer and UnitStructer (both of which use vJass).
UnitStructer creates a struct for each unit that enters the map and destroys the struct after the unit has been removed from the game. This struct is stored to the unit's UserData also known as Custom Value.
UnitIndexer does the same except that it 'creates' indexes instead of structs. The creation algorithm it uses is basically the same and produces the same integers as the one of structs, but is inlined and a bit faster. UnitIndexer is basically as light as any system that does the same can be.
The index or struct of a unit can be acquired with a GetUnitUserData call or in GUI with 'Unit - Custom Value of Unit'. The use of SetUnitUserData or 'Unit - Set Custom Value of Unit' screws up the system and as such is prohibited. Also you can't use RemoveUnit() or 'Unit - Remove' on living units. Instead you either have to use RemoveIndexerUnit() or 'Unit - Hide' and 'Unit - Explode'.
The structs created by the UnitStructer can be used just like any other structs are except that you must not destroy the ones that the system itself has assigned to units.
Both these structs as well as the indexes created by the UnitIndexer can be used to make things multi-unit instanceable the same way player numbers can be used to make things multi-player instanceable.
[jass=JASS Example]set someArrayVariable[GetUnitUserData(u)] = data
set data = someArrayVariable[GetUnitUserData(u)][/code][TRIGGER=GUI Example]Set someArrayVariable[(Custom value of (Last created unit))] = data
Set data = someArrayVariable[(Custom value of (Last created unit))]
[/TRIGGER]
Take note that these indexes as well as structs are recycled when units die meaning that values stored to arrays using them as indexes aren't always initialized or nulled unless you do that yourself.[trigger=GUI Example]NullUnitData
Events
Unit - A unit enters (Playable map area)
Conditions
Actions
Set someArrayVariable[(Custom value of (Triggering unit))] = 0
[/trigger]
Anything that uses the UnitIndexer works with the UnitStructer, but not always the other way around. Anything that uses Cohadar's PUI can be made to work with either of these by using this:
JASS:
library PUI uses UnitIndexer
// All you need to do when switching from PUI to UnitIndexer is to copy paste
// the UnitIndexer library to your map, disable the previous PUI library that
// you had in your map and replace RemoveUnit calls with RemoveIndexedUnit calls
function GetUnitIndex takes unit u returns integer
return GetUnitUserData(u)
endfunction
endlibrary
JASS:
//==============================================================================
//---DiscipleOfLife's-----------------------------------------------------------
//
// UnitIndexer // v0.91
//
//------------------------------------------------------------------------------
library UnitIndexer initializer init
globals
//----------------------------------------------------------------------
private constant real TOTAL_DECAY_TIME = 90.00
// This should match the sum of the 'Decay Time (sec) - Bones'
// and 'Decay Time (sec) - Flesh' gameplay constants.
// The system will work with any value but is most optimized with
// the correct value.
//----------------------------------------------------------------------
//----------------------------------------------------------------------
private constant integer UNIT_LIMIT = 4096
// If the amount of units in the map at once exceedes this limit
// the UnitIndexer will malfunction.
// Higher value increases the operations done in the systems init
// function and might hit the op limit.
//----------------------------------------------------------------------
private integer FreeIndex = 1
private integer array NextFreeIndex
private boolean array IsAlive
endglobals
//==========================================================================
// If the native RemoveUnit is used on a living unit the removed unit's
// index won't be recycled which is why this function must be used instead
// in such cases.
//
function RemoveIndexedUnit takes unit u returns nothing
local integer uind = GetUnitUserData(u)
if IsAlive[uind] then
set IsAlive[uind] = false
set NextFreeIndex[uind] = FreeIndex
set FreeIndex = uind
endif
call RemoveUnit(u)
endfunction
//==========================================================================
// This function is run whenever any unit enters the map.
//
private function onEnter takes nothing returns boolean
set IsAlive[FreeIndex] = true
call SetUnitUserData(GetFilterUnit(), FreeIndex)
set FreeIndex = NextFreeIndex[FreeIndex]
return false
endfunction
//==========================================================================
// This function is run whenever any unit dies.
//
private function onDeath takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer uind = GetUnitUserData(u)
if IsAlive[uind] then
set IsAlive[uind] = false
call TriggerSleepAction(3)
loop
//--------------------------------------------------------------
// Most units that decay exit this loop at the second iteration.
// Those that don't decay exit right at the first iteration.
// Heroes stay in the loop untill revived or removed.
//--------------------------------------------------------------
exitwhen GetUnitUserData(u) == 0
if GetWidgetLife(u) >= 0.405 then
set IsAlive[uind] = true
set u = null
return
endif
call TriggerSleepAction(TOTAL_DECAY_TIME)
endloop
set NextFreeIndex[uind] = FreeIndex
set FreeIndex = uind
endif
set u = null
endfunction
private function init takes nothing returns nothing
local trigger trig = CreateTrigger()
local region rectRegion = CreateRegion()
local group tempGroup = CreateGroup()
local integer i = UNIT_LIMIT - 1
loop
exitwhen i <= 0
set NextFreeIndex[i] = i + 1
set i = i - 1
endloop
call GroupEnumUnitsInRect(tempGroup, bj_mapInitialPlayableArea, Filter(function onEnter))
call DestroyGroup(tempGroup)
set tempGroup = null
call RegionAddRect(rectRegion, bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(CreateTrigger(), rectRegion, Filter(function onEnter))
call TriggerAddAction(trig, function onDeath)
call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_DEATH)
endfunction
endlibrary // End of the UnitIndexer
//==============================================================================
JASS:
//==============================================================================
//---DiscipleOfLife's-----------------------------------------------------------
//
// UnitStructer // v0.91
//
//------------------------------------------------------------------------------
library UnitStructer initializer init
//**************************************************************************
//* Editable Part *
//**************************************************************************
globals
private constant real TOTAL_DECAY_TIME = 90.00
// This should match the sum of the 'Decay Time (sec) - Bones'
// and 'Decay Time (sec) - Flesh' gameplay constants.
// The system will work with any value but is most optimized with
// the correct one.
endglobals
struct UnitStruct
boolean someSystemFlag = true // don't touch this line
endstruct
private function onEnter takes nothing returns boolean
local UnitStruct uind = UnitStruct.create()
local unit u = GetFilterUnit()
call SetUnitUserData(u, integer(uind))
// I suggest that you don't edit the existing lines in this
// function unless you know exactly what you're doing
set u = null
return false
endfunction
//**************************************************************************
//* End of Editable Part *
//**************************************************************************
//==========================================================================
// If the native RemoveUnit is used on a living unit, the unit's struct
// won't be recycled. If there is a possibility that the unit is alive
// use this instead of RemoveUnit().
//
function RemoveIndexedUnit takes unit u returns nothing
local UnitStruct uind = UnitStruct(GetUnitUserData(u))
if uind.someSystemFlag then
set uind.someSystemFlag = false
call uind.destroy()
endif
call RemoveUnit(u)
endfunction
//==========================================================================
// This function is run whenever any unit dies.
//
private function onDeath takes nothing returns nothing
local unit u = GetTriggerUnit()
local UnitStruct uind = UnitStruct(GetUnitUserData(u))
if uind.someSystemFlag then
set uind.someSystemFlag = false
call TriggerSleepAction(3)
loop
//--------------------------------------------------------------
// Most units that decay exit this loop at the second iteration.
// Those that don't decay exit right at the first iteration.
// Heroes stay in the loop untill revived or removed.
//--------------------------------------------------------------
exitwhen GetUnitUserData(u) == 0
if GetWidgetLife(u) >= 0.405 then
set uind.someSystemFlag = true
set u = null
return
endif
call TriggerSleepAction(TOTAL_DECAY_TIME)
endloop
call uind.destroy()
endif
set u = null
endfunction
private function init takes nothing returns nothing
local trigger trig = CreateTrigger()
local region rectRegion = CreateRegion()
local group tempGroup = CreateGroup()
call GroupEnumUnitsInRect(tempGroup, bj_mapInitialPlayableArea, Filter(function onEnter))
call DestroyGroup(tempGroup)
set tempGroup = null
call RegionAddRect(rectRegion, bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(CreateTrigger(), rectRegion, Filter(function onEnter))
call TriggerAddAction(trig, function onDeath)
call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_DEATH)
endfunction
endlibrary // End of the UnitStructer
library UnitIndexer uses UnitStructer
// Anything that uses UnitIndexer works with UnitStructer
endlibrary
//==============================================================================
v0.91
- removed dynamic array feature
- posted UnitStructer
- drastically improved the performance of the system
- rewrote parts of the readme
Last edited: