Sgqvur
FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
- Reaction score
- 62
unitgroup is an attempt to replace the [ljass]native group[/ljass] type with a non native one (a struct type)
which is much easier to work with and is hopefully faster =).
Reasons that make the native group type "hard" to work with is that it doesn't know it's size (how many units there are),
requires enumeration callbacks (ForGroup) or FirstOfGroup enumerating for every operation performed on the group including
counting the number of units which are not really a good practice and doesn't have random access.
Requirements:
The API is presented in C/C++ / C# / cJass / Galaxy Script function notation for brevity in pairs in which the
top one is the [ljass]group[/ljass] function name (if present) and the bottom is the [ljass]unitgroup[/ljass] alternative:
[ljass]group CreateGroup()[/ljass]
[ljass]static unitgroup new()[/ljass]
-- no native or bj available but possible --
[ljass]static unitgroup copy(unitgroup which_unitgroup)[/ljass]
[ljass]void GroupClear(group g)[/ljass]
[ljass]void clear()[/ljass]
[ljass]void DestroyGroup(group g)[/ljass]
[ljass]void delete()[/ljass]
[ljass]void GroupRemoveUnit(group g, unit u)[/ljass]
[ljass]void remove_unit(unit u)[/ljass]
[ljass]void GroupAddUnit(group g, unit u)[/ljass]
[ljass]void push_unit(unit u)[/ljass]
[ljass]bool IsUnitInGroup(unit u, group g)[/ljass]
[ljass]bool has_unit(unit u)[/ljass]
[ljass]unit GroupPickRandomUnit(group g)[/ljass]
[ljass]unit random_unit()[/ljass]
[ljass]void GroupEnumUnitsOfType(group g, string unitname, boolexpr filter)[/ljass]
[ljass]void enum_units_by_id(int id, int player, int max_count)[/ljass]
[ljass]void GroupEnumUnitsOfTypeCounted(group g, string unitname, boolexpr filter, int max_count)[/ljass]
[ljass]void enum_units_by_id(int id, int p, int max_count)[/ljass]
[ljass]void GroupEnumUnitsOfPlayer(group g, player p, boolexpr filter)[/ljass]
[ljass]void enum_units_of_player(int p, int max_count)[/ljass]
[ljass]void GroupEnumUnitsInRect(group g, rect r, boolexpr filter)[/ljass]
[ljass]void enum_units_in_rect(rect r, int p, int max_count)[/ljass]
[ljass]void GroupEnumUnitsInRectCounted(group g, rect r, boolexpr filter, int max_count)[/ljass]
[ljass]void enum_units_in_rect(rect r, int p, int max_count)[/ljass]
[ljass]void GroupEnumUnitsInRange(group g, real x, real y, real radius, boolexpr filter)[/ljass]
[ljass]void enum_units_in_range(real x, real y, real radius, int p, int max_count)[/ljass]
[ljass]void GroupEnumUnitsInRangeCounted(group g, real x, real y, real radius, boolexpr filter, int max_count)[/ljass]
[ljass]void enum_units_in_range(real x, real y, real radius, int p, int max_count)[/ljass]
[ljass]void GroupEnumUnitsSelected(group g, player p, boolexpr filter)[/ljass]
[ljass]void enum_units_selected_by_player(int p, int max_count)[/ljass]
[ljass]bool GroupImmediateOrderById(group g, int order)[/ljass]
[ljass]void immediate_order(int order)[/ljass]
[ljass]bool GroupPointOrderById(group g, int order, real x, real y)[/ljass]
[ljass]void point_order(int order, real x, real y)[/ljass]
[ljass]bool GroupTargetOrderById(group g, int order, widget target)[/ljass]
[ljass]void target_order(int order, widget target)[/ljass]
-- no native or bj available but possible --
[ljass]unit nearest_unit_from_point(real x, real y)[/ljass]
-- no native or bj available but possible --
[ljass]unit nearest_unit_from_unit(unit u)[/ljass]
[ljass]void ForGroup(group g, code callback)[/ljass]
[ljass]unit FirstOfGroup(group g)[/ljass]
[ljass]|unitgroup_instance|[0][/ljass]
[ljass]int CountUnitsInGroup(group g)[/ljass]
[ljass]|unitgroup_instance|.size[/ljass]
which is much easier to work with and is hopefully faster =).
Reasons that make the native group type "hard" to work with is that it doesn't know it's size (how many units there are),
requires enumeration callbacks (ForGroup) or FirstOfGroup enumerating for every operation performed on the group including
counting the number of units which are not really a good practice and doesn't have random access.
Requirements:
jasshelper compiler by Vexorian
AutoIndex by grim001 or
UnitIndexer by Nestharus
The API is presented in C/C++ / C# / cJass / Galaxy Script function notation for brevity in pairs in which the
top one is the [ljass]group[/ljass] function name (if present) and the bottom is the [ljass]unitgroup[/ljass] alternative:
[ljass]group CreateGroup()[/ljass]
[ljass]static unitgroup new()[/ljass]
-- no native or bj available but possible --
[ljass]static unitgroup copy(unitgroup which_unitgroup)[/ljass]
[ljass]void GroupClear(group g)[/ljass]
[ljass]void clear()[/ljass]
[ljass]void DestroyGroup(group g)[/ljass]
[ljass]void delete()[/ljass]
[ljass]void GroupRemoveUnit(group g, unit u)[/ljass]
[ljass]void remove_unit(unit u)[/ljass]
[ljass]void GroupAddUnit(group g, unit u)[/ljass]
[ljass]void push_unit(unit u)[/ljass]
[ljass]bool IsUnitInGroup(unit u, group g)[/ljass]
[ljass]bool has_unit(unit u)[/ljass]
[ljass]unit GroupPickRandomUnit(group g)[/ljass]
[ljass]unit random_unit()[/ljass]
[ljass]void GroupEnumUnitsOfType(group g, string unitname, boolexpr filter)[/ljass]
[ljass]void enum_units_by_id(int id, int player, int max_count)[/ljass]
[ljass]void GroupEnumUnitsOfTypeCounted(group g, string unitname, boolexpr filter, int max_count)[/ljass]
[ljass]void enum_units_by_id(int id, int p, int max_count)[/ljass]
[ljass]void GroupEnumUnitsOfPlayer(group g, player p, boolexpr filter)[/ljass]
[ljass]void enum_units_of_player(int p, int max_count)[/ljass]
[ljass]void GroupEnumUnitsInRect(group g, rect r, boolexpr filter)[/ljass]
[ljass]void enum_units_in_rect(rect r, int p, int max_count)[/ljass]
[ljass]void GroupEnumUnitsInRectCounted(group g, rect r, boolexpr filter, int max_count)[/ljass]
[ljass]void enum_units_in_rect(rect r, int p, int max_count)[/ljass]
[ljass]void GroupEnumUnitsInRange(group g, real x, real y, real radius, boolexpr filter)[/ljass]
[ljass]void enum_units_in_range(real x, real y, real radius, int p, int max_count)[/ljass]
[ljass]void GroupEnumUnitsInRangeCounted(group g, real x, real y, real radius, boolexpr filter, int max_count)[/ljass]
[ljass]void enum_units_in_range(real x, real y, real radius, int p, int max_count)[/ljass]
[ljass]void GroupEnumUnitsSelected(group g, player p, boolexpr filter)[/ljass]
[ljass]void enum_units_selected_by_player(int p, int max_count)[/ljass]
[ljass]bool GroupImmediateOrderById(group g, int order)[/ljass]
[ljass]void immediate_order(int order)[/ljass]
[ljass]bool GroupPointOrderById(group g, int order, real x, real y)[/ljass]
[ljass]void point_order(int order, real x, real y)[/ljass]
[ljass]bool GroupTargetOrderById(group g, int order, widget target)[/ljass]
[ljass]void target_order(int order, widget target)[/ljass]
-- no native or bj available but possible --
[ljass]unit nearest_unit_from_point(real x, real y)[/ljass]
-- no native or bj available but possible --
[ljass]unit nearest_unit_from_unit(unit u)[/ljass]
[ljass]void ForGroup(group g, code callback)[/ljass]
JASS:
[ljass]unit FirstOfGroup(group g)[/ljass]
[ljass]|unitgroup_instance|[0][/ljass]
[ljass]int CountUnitsInGroup(group g)[/ljass]
[ljass]|unitgroup_instance|.size[/ljass]
JASS:
library unitgroup uses optional AutoIndex, optional UnitIndexer
globals
private constant boolean USE_ARRAYS_FOR_STORAGE = true
// if the above constant is true then only
// UNITGROUP_MAX_INSTANCES unitgroups are allowed
// each with maximum size of UNITGROUP_MAX_SIZE units
private constant integer UNITGROUP_MAX_INSTANCES = 320
private constant integer UNITGROUP_MAX_SIZE = 1024
private unit array UGC[UNITGROUP_MAX_INSTANCES][UNITGROUP_MAX_SIZE]
// Note: UNITGROUP_MAX_INSTANCES * UNITGROUP_MAX_SIZE <= 409 550 must be true
// else a compile time error will pop
// else if the above constant is false then
// 8190 unitgroups are allowed each with
// an "unlimited" (limited by ram only) amount of units
private hashtable HT
// in terms of speed the 2d array (UGC) should be faster
// so set the constant to true if you prefer speed
// else set it to false if you want to be safe than sorry =)
endglobals
globals
// used in some enum<*> methods
constant integer ANY_PLAYER = -1
// could be used in all enum<*> methods
constant integer NO_MAX_COUNT = 0
private unit array Units
private integer Units_count = 0
endglobals
private struct indexer extends array
static if LIBRARY_AutoIndex then
static method index takes unit u returns nothing
set Units[Units_count] = u
set Units_count = Units_count + 1
endmethod
static method deindex takes unit u returns nothing
local integer i = 0
local integer unit_index = 0
loop
exitwhen i >= Units_count
if Units<i> == u then
set unit_index = i
exitwhen true
endif
set i = i + 1
endloop
set Units[unit_index] = Units[Units_count - 1]
set Units_count = Units_count - 1
endmethod
static method onInit takes nothing returns nothing
call OnUnitIndexed(indexer.index)
call OnUnitDeindexed(indexer.deindex)
endmethod
elseif LIBRARY_UnitIndexer then
method index takes nothing returns nothing
set Units[Units_count] = unit
set Units_count = Units_count + 1
endmethod
method deindex takes nothing returns nothing
local integer i = 0
local integer unit_index = 0
loop
exitwhen i >= Units_count
if Units<i> == unit then
set unit_index = i
exitwhen true
endif
set i = i + 1
endloop
set Units[unit_index] = Units[Units_count - 1]
set Units_count = Units_count - 1
endmethod
implement optional UnitIndexStruct
else
compiletimeexeption e = "You need a unit indexing library =)"
throw e
endif
endstruct
struct unitgroup
// the number of units in the unitgroup
readonly integer size
//! textmacro unitgroup_assert_size takes METHOD_NAME, RETURN_VALUE
debug if size <= 0 then
debug call BJDebugMsg("unitgroup error: method $METHOD_NAME$: " + "instance id = " + I2S(this) + ": unitgroup is empty")
debug return $RETURN_VALUE$
debug endif
//! endtextmacro
method operator[] takes integer i returns unit
debug if i < 0 or i >= size then
debug call BJDebugMsg("unitgroup error: method operator[]: " + "instance id = " + I2S(this) + ": array index out of bounds")
debug return null
debug endif
static if USE_ARRAYS_FOR_STORAGE then
return UGC[this]<i>
else
return LoadUnitHandle(HT, this, i)
endif
endmethod
method operator[]= takes integer i, unit u returns nothing
debug if i < 0 or i >= size then
debug call BJDebugMsg("unitgroup error: method operator[]=: " + "instance id = " + I2S(this) + ": array index out of bounds")
debug return
debug endif
static if USE_ARRAYS_FOR_STORAGE then
set UGC[this]<i> = u
else
call SaveUnitHandle(HT, this, i, u)
endif
endmethod
method has_unit takes unit which_unit returns boolean
local integer i = 0
local unit u = null
local boolean found = false
//! runtextmacro unitgroup_assert_size("has_unit", "false")
loop
exitwhen i >= size
static if USE_ARRAYS_FOR_STORAGE then
set u = UGC[this]<i>
else
set u = LoadUnitHandle(HT, this, i)
endif
if u == which_unit then
set found = true
exitwhen true
endif
set i = i + 1
endloop
set u = null
return found
endmethod
method remove_unit takes unit u returns nothing
local integer i = 0
local integer unit_index = -1
//! runtextmacro unitgroup_assert_size("remove_unit", "")
if u == null then
debug call BJDebugMsg("unitgroup error: method remove_unit: " + "instance id = " + I2S(this) + ": attempt to remove null unit")
return
endif
loop
exitwhen i >= size
static if USE_ARRAYS_FOR_STORAGE then
if u == UGC[this]<i> then
set unit_index = i
exitwhen true
endif
else
if u == LoadUnitHandle(HT, this, i) then
set unit_index = i
exitwhen true
endif
endif
set i = i + 1
endloop
// if the unit was found
if unit_index != -1 then
static if USE_ARRAYS_FOR_STORAGE then
set UGC[this][unit_index] = UGC[this][size -1]
set size = size - 1
else
call SaveUnitHandle(HT, this, unit_index, LoadUnitHandle(HT, this, size - 1))
set size = size -1
endif
endif
endmethod
method push_unit takes unit u returns nothing
static if USE_ARRAYS_FOR_STORAGE then
set UGC[this][size] = u
else
call SaveUnitHandle(HT, this, size, u)
endif
set size = size + 1
endmethod
method random_unit takes nothing returns unit
//! runtextmacro unitgroup_assert_size("random_unit", "null")
static if USE_ARRAYS_FOR_STORAGE then
return UGC[this][GetRandomInt(0, size -1)]
else
return LoadUnitHandle(HT, this, GetRandomInt(0, size - 1))
endif
endmethod
method enum_units_of_player takes integer p, integer max_count returns nothing
local integer i = 0
local integer n = 0
local player P = null
local unit u = null
if p == ANY_PLAYER then
debug call BJDebugMsg("unitgroup error: method enum_units_of_player: " + "instance id = " + I2S(this) + ": expected a specific player not ANY_PLAYER")
return
endif
set P = Player(p)
loop
exitwhen i >= Units_count or (n >= max_count and max_count != NO_MAX_COUNT)
set u = Units<i>
if P == GetOwningPlayer(u) then
// push the unit
static if USE_ARRAYS_FOR_STORAGE then
set UGC[this][size] = u
else
call SaveUnitHandle(HT, this, size, u)
endif
set size = size + 1
endif
set i = i + 1
set n = n + 1
endloop
set u = null
endmethod
method enum_units_by_id takes integer id, integer p, integer max_count returns nothing
local integer i = 0
local integer n = 0
local player P = null
local unit u = null
if id == 0 then
debug call BJDebugMsg("unitgroup error: method enum_units_by_id: " + "instance id = " + I2S(this) + ": invalid unit id")
return
endif
if p != ANY_PLAYER then
set P = Player(p)
endif
loop
exitwhen i >= Units_count or (n >= max_count and max_count != NO_MAX_COUNT)
set u = Units<i>
if (p == ANY_PLAYER or P == GetOwningPlayer(u)) and id == GetUnitTypeId(u) then
static if USE_ARRAYS_FOR_STORAGE then
set UGC[this][size] = u
else
call SaveUnitHandle(HT, this, size, u)
endif
set size = size + 1
endif
set i = i + 1
set n = n + 1
endloop
set u = null
endmethod
method enum_units_in_rect takes rect r, integer p, integer max_count returns nothing
local integer i = 0
local integer n = 0
local player P = null
local unit u = null
local real ux = 0
local real uy = 0
local real minx = 0
local real maxx = 0
local real miny = 0
local real maxy = 0
if r == null then
debug call BJDebugMsg("unitgroup error: method enum_units_in_rect: " + "instance id = " + I2S(this) + ": expected a non null rect")
return
endif
if p != ANY_PLAYER then
set P = Player(p)
endif
set minx = GetRectMinX(r)
set maxx = GetRectMaxX(r)
set miny = GetRectMinY(r)
set maxy = GetRectMaxY(r)
loop
exitwhen i >= Units_count or (n >= max_count and max_count != NO_MAX_COUNT)
set u = Units<i>
set ux = GetUnitX(u)
set uy = GetUnitY(u)
if (p == ANY_PLAYER or P == GetOwningPlayer(u)) and minx < ux and ux < maxx and miny < uy and uy < maxy then
static if USE_ARRAYS_FOR_STORAGE then
set UGC[this][size] = u
else
call SaveUnitHandle(HT, this, size, u)
endif
set size = size + 1
endif
set i = i + 1
set n = n + 1
endloop
set u = null
endmethod
method enum_units_in_range takes real x, real y, real radius, integer p, integer max_count returns nothing
local integer i = 0
local integer n = 0
local player P = null
local unit u = null
if radius <= 0 then
debug call BJDebugMsg("unitgroup error: method enum_units_in_range: " + "instance id = " + I2S(this) + ": invalid radius")
return
endif
if p != ANY_PLAYER then
set P = Player(p)
endif
loop
exitwhen i >= Units_count or (n >= max_count and max_count != NO_MAX_COUNT)
set u = Units<i>
if (p == ANY_PLAYER or P == GetOwningPlayer(u)) and IsUnitInRangeXY(u, x, y, radius) then
static if USE_ARRAYS_FOR_STORAGE then
set UGC[this][size] = u
else
call SaveUnitHandle(HT, this, size, u)
endif
set size = size + 1
endif
set i = i + 1
set n = n + 1
endloop
set u = null
endmethod
method enum_units_selected_by_player takes integer p, integer max_count returns nothing
local integer i = 0
local integer n = 0
local player P = null
local unit u = null
if p == ANY_PLAYER then
debug call BJDebugMsg("unitgroup error: method enum_units_selected_by_player: " + "instance id = " + I2S(this) + ": expected a specific player not ANY_PLAYER")
return
endif
set P = Player(p)
loop
exitwhen i >= Units_count or (n >= max_count and max_count != NO_MAX_COUNT)
set u = Units<i>
if IsUnitSelected(u, P) then
static if USE_ARRAYS_FOR_STORAGE then
set UGC[this][size] = u
else
call SaveUnitHandle(HT, this, size, u)
endif
set size = size + 1
endif
set i = i + 1
set n = n + 1
endloop
set u = null
endmethod
method immediate_order takes integer order returns nothing
local integer i = 0
local unit u = null
//! runtextmacro unitgroup_assert_size("immediate_order", "")
if order == 0 then
debug call BJDebugMsg("unitgroup error: method immediate_order: " + "instance id = " + I2S(this) + ": ivalid order")
return
endif
loop
exitwhen i >= size
static if USE_ARRAYS_FOR_STORAGE then
set u = UGC[this]<i>
else
set u = LoadUnitHandle(HT, this, i)
endif
call IssueImmediateOrderById(u, order)
set i = i + 1
endloop
set u = null
endmethod
method point_order takes integer order, real x, real y returns nothing
local integer i = 0
local unit u = null
//! runtextmacro unitgroup_assert_size("point_order", "")
if order == 0 then
debug call BJDebugMsg("unitgroup error: method point_order: " + "instance id = " + I2S(this) + ": ivalid order")
return
endif
loop
exitwhen i >= size
static if USE_ARRAYS_FOR_STORAGE then
set u = UGC[this]<i>
else
set u = LoadUnitHandle(HT, this, i)
endif
call IssuePointOrderById(u, order, x, y)
set i = i + 1
endloop
set u = null
endmethod
method target_order takes integer order, widget target returns nothing
local integer i = 0
local unit u = null
//! runtextmacro unitgroup_assert_size("target_order", "")
if order == 0 then
debug call BJDebugMsg("unitgroup error: method target_order: " + "instance id = " + I2S(this) + ": ivalid order")
return
elseif target == null then
debug call BJDebugMsg("unitgroup error: method target_order: " + "instance id = " + I2S(this) + ": expected a non null target")
return
endif
loop
exitwhen i >= size
static if USE_ARRAYS_FOR_STORAGE then
set u = UGC[this]<i>
else
set u = LoadUnitHandle(HT, this, i)
endif
call IssueTargetOrderById(u, order, target)
set i = i + 1
endloop
set u = null
endmethod
method nearest_unit_from_point takes real x, real y returns unit
local integer i = 0
local unit u = null
local real ux = 0
local real uy = 0
local real curr_min_distance = 9999999
local unit nearest_unit = null
local real dx = 0
local real dy = 0
local real distance = 0
//! runtextmacro unitgroup_assert_size("nearest_unit_from_point", "null")
loop
exitwhen i >= size
static if USE_ARRAYS_FOR_STORAGE then
set u = UGC[this]<i>
else
set u = LoadUnitHandle(HT, this, i)
endif
set ux = GetUnitX(u)
set uy = GetUnitY(u)
set dx = ux - x
set dy = uy - y
set distance = SquareRoot(dx * dx + dy * dy)
if curr_min_distance > distance then
set curr_min_distance = distance
set nearest_unit = u
endif
set i = i + 1
endloop
set u = null
return nearest_unit
endmethod
method nearest_unit_from_unit takes unit which_unit returns unit
local integer i = 0
local unit u = null
local real ux = 0
local real uy = 0
local real curr_min_distance = 9999999
local unit nearest_unit = null
local real dx = 0
local real dy = 0
local real distance = 0
local real x = GetUnitX(which_unit)
local real y = GetUnitY(which_unit)
//! runtextmacro unitgroup_assert_size("nearest_unit_from_unit", "null")
loop
exitwhen i >= size
static if USE_ARRAYS_FOR_STORAGE then
set u = UGC[this]<i>
else
set u = LoadUnitHandle(HT, this, i)
endif
if u != which_unit then
set ux = GetUnitX(u)
set uy = GetUnitY(u)
set dx = ux - x
set dy = uy - y
set distance = SquareRoot(dx * dx + dy * dy)
if curr_min_distance > distance then
set curr_min_distance = distance
set nearest_unit = u
endif
endif
set i = i + 1
endloop
set u = null
return nearest_unit
endmethod
static method new takes nothing returns unitgroup
local unitgroup ug = allocate()
debug static if USE_ARRAYS_FOR_STORAGE then
debug if integer(ug) > UNITGROUP_MAX_INSTANCES then
debug call BJDebugMsg("unitgroup error: constructor: out of unitgroups")
debug return unitgroup(0)
debug endif
debug endif
set ug.size = 0
return ug
endmethod
static method copy takes unitgroup which_unitgroup returns unitgroup
local unitgroup ug = allocate()
local integer i = 0
debug static if USE_ARRAYS_FOR_STORAGE then
debug if integer(ug) > UNITGROUP_MAX_INSTANCES then
debug call BJDebugMsg("unitgroup error: copy constructor: out of unitgroups")
debug return unitgroup(0)
debug endif
debug endif
loop
exitwhen i >= which_unitgroup.size
static if USE_ARRAYS_FOR_STORAGE then
set UGC[ug]<i> = UGC[which_unitgroup]<i>
else
call SaveUnitHandle(HT, ug, i, LoadUnitHandle(HT, which_unitgroup, i))
endif
set i = i + 1
endloop
set ug.size = which_unitgroup.size
return ug
endmethod
method clear takes nothing returns nothing
set size = 0
endmethod
method delete takes nothing returns nothing
call deallocate()
endmethod
private static method onInit takes nothing returns nothing
static if USE_ARRAYS_FOR_STORAGE then
else
set HT = InitHashtable()
endif
endmethod
endstruct
endlibrary
</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>