- Joined
- Jun 20, 2011
- Messages
- 249
JASS:
library GroupTools uses /*
*/ Table /* thehelper.net/forums/showthread.php/162582-Table
*/ Alloc /* thehelper.net/forums/showthread.php/163457-Alloc
// A system that provides optimal speed for group handling instead of
// using slow natives such as ForGroup or FirstOfGroup. Also provides
// multiple tools for grouping that Wc3 doesn't have internalized. This
// system is entirely dedicated to speed freaks and advanced JASSers.
// Sadly there is no ForGroup call, and adding one would kill all the
// performance this system provides. How to enumerate units through the
// group then? An example is provided:
function Enumerate takes nothing returns nothing
local UnitGroup SomeGroup=UnitGroup.create() //Allocates a new
//group, this group is currently empty
local integer i=0 //This is needed to go through all units inside
//the group using a loop
local unit u //This variable is a must. NextOfGroup always returns
//a different unit from the group, so be sure to use it only once
//inside each loop.
call SomeGroup.enumUnitsInRange(0,0,600) //Adds EVERY unit within
//the specified range, there's no way to filter these units, but
//there is a way to prevent actios to be done to those you do not
//want.
loop
exitwhen i==SomeGroup.count //The loop ends once i reachs the
//number of units inside the group
set u=SomeGroup.NextOfGroup //Use the variable u to handle the
//curent unit
if GetUnitTypeId(u)='hfoo' then //Checks if u is a footman
call KillUnit(u) //If the unit passes the filter actions
//are fired
endif
set i=i+1 //Iterates the loop
endloop
//A great advantage of doing this is that the group isn't empty now,
//If you were using the common FirstOfGroup loop, the group would
//be right now, so you couldn't save those units for later use
call SomeGroup.destroy() //Deallocates the group, deleting units
//inside of it
set u=null
endfunction
// This system also provides the SimpleGroup struct, it works similar
// to UnitGroup, it's faster, but doesn't have some of the tools it
// provides.
***********************************************************************
API for the UnitGroup struct:
-Allocation Methods:
static method create takes nothing returns thistype
// Allocates a group, this group is initially empty.
method destroy takes nothing returns nothing
// Deallocates a group and all of the units inside of it.
method addUnit takes unit whichUnit returns nothing
// Adds an unit to the group.
method removeUnit takes unit whichUnit returns nothing
// Removes an unit from the group.
method hasUnit takes unit whichUnit returns boolean
// Returns true if the group has the given unit.
method clear takes nothing returns nothing
// Removes every unit from the group.
***********************************************************************
API for the SimpleGroup struct:
static method create takes nothing returns thistype
// Allocates a group, this group is initially empty.
method destroy takes nothing returns nothing
// Deallocates a group and all of the units inside of it.
method addUnit takes unit whichUnit returns nothing
// Adds an unit to the group.
method clear takes nothing returns nothing
// Removes every unit from the group.
***********************************************************************
API of the Enumeration methods:
method enumUnitsInRange takes real x, real y, real radius returns nothing
// Picks all units within a radius and adds them to the group.
method enumUnitsInRing takes real x, real y, real innerRadius, real outerRadius returns nothing
// Only adds the units inside the given parameters.
method enumUnitsInCone takes real x, real y, real radius, real facing, real coneAngle returns nothing
// Only adds the units inside the given parameters.
method enumUnitsInRect takes real x1, real y1, real x2, real y2 returns nothing
// Only adds the units inside the given parameters.
***********************************************************************
-Group Handling:
method isEmpty takes nothing returns boolean
// Returns true if the group is empty.
// Using "0==this.count" gives the same result.
***********************************************************************
-Additional Tools:
method closestToLoc takes real x, real y returns unit
// Returns the closest unit from the group to the given location.
method farthestToLoc takes real x, real y returns unit
// Returns the farthest unit from the group to the given location.
***********************************************************************
VARIABLES:
-Globals provided
group ENUM_GROUP
// Initialized group.
-Non Static Members (this.Variable)
readonly integer count
// The amount of units inside the group (Non modifiable).
readonly unit FirstOfGroup
// The first unit of the group, when another is added or removed it changes (Non modifiable).
readonly unit NextOfGroup
// Basically cycles between units from the group everytime it's used (Non modifiable).
integer data
// This variable exists for those who wants to attach instances to the group.
**********************************************************************/
// Basically this is how the OOP works:
// A group is allocated, this group has record of 1 random unit from an UnitList, this unit knows the next and previous
// instance from it's list. In the example below the group G has 6 units A - B - C - D - E - F
// B - C
// / \
// G - A D
// \ /
// F - E
// If the unit "Z" is added to the group then it would look like this
// B - C - D
// / \
// G - Z E
// \ /
// A - F
// If the unit "D" is removed from the group then it would look like this
// F - A
// / \
// G - E Z
// \ /
// C - B
// Notice how the unit the group points to changed from A to Z and now is E. It doesn't really matter which unit the group
// points to, as long as it belongs to the same unit list the group will remain complete. The reason of why it changed from
// being "Z" to "E" is explained below.
globals
group ENUM_GROUP=CreateGroup()
endglobals
private struct UnitList extends array
implement Alloc
//The way this system keeps track of units it's similar to a linked list, only that there's no head. UnitList only keeps
//track of the next and prev units from an unit. In a list where units "A" "B" and "C" are linked it's representation would be
//like this:
// A - B - C - A - B...
//If this were a normal linked list, "A" would be the head, but instead is just another echelon.
thistype next
thistype prev
unit unit
endstruct
struct UnitGroup extends array
implement Alloc
//the "first variable works like a pointer the group has towards any unit allocated inside UnitList, and because this
//list keeps track units that follow it, there's no need for the group to know all of them.
private UnitList first
//keeps track of how many units are inside the group.
readonly integer count
//For those who want to attach instances to the group
integer data
//Table is used almost exclusively for the Group.hasUnit method.
private Table lookUp
method addUnit takes unit whichUnit returns nothing
local UnitList thisUnit
local UnitList start
local integer id=GetHandleId(whichUnit)
//prevent adding the same unit to the group twice
if this.lookUp.has(id) then
return
endif
set thisUnit=UnitList.allocate()
//This is for creating a LinkedList without a head.
if 0==this.count then
set start=thisUnit
//It dosn't really matter which UnitList is stored in the "first" variable
set this.first=thisUnit
else
set start=this.first
endif
set start.next.prev=thisUnit
set thisUnit.next=start.next
set start.next=thisUnit
set thisUnit.prev=start
set thisUnit.unit=whichUnit
set this.lookUp[GetHandleId(thisUnit.unit)]=thisUnit
set this.count=this.count+1
endmethod
method removeUnit takes unit whichUnit returns nothing
local integer h=GetHandleId(whichUnit)
local UnitList thisUnit=this.lookUp[h]
set thisUnit.next.prev=thisUnit.prev
set thisUnit.prev.next=thisUnit.next
//This would be only necessary if the deallocated unit IS the "first" of the group, but to avoid useless checks
//it's performed everytime an unit is deallocated. Again, it dosn't matter which UnitList is the "first" variable
//unless it's an unit from outside the group.
set this.first=thisUnit.next
call thisUnit.deallocate()
call this.lookUp.remove(h)
set this.count=this.count-1
endmethod
//Safety method to remove Non-existant units from the unit group
private method safeIteration takes nothing returns nothing
loop
exitwhen not(0==GetUnitTypeId(this.first.unit)) or (0==this.count)
call this.removeUnit(this.first.unit)
endloop
endmethod
method operator FirstOfGroup takes nothing returns unit
call this.safeIteration()
return this.first.unit
endmethod
//This method always returns a different unit from the group, won't return the same unit until it goes through all
//the units inside the group.
method operator NextOfGroup takes nothing returns unit
set this.first=this.first.next
call this.safeIteration()
return this.first.unit
endmethod
//Destroys the whole list looping through it once.
method destroy takes nothing returns nothing
local UnitList next
loop
exitwhen 0==this.count
set next=this.first.next
call this.lookUp.remove(GetHandleId(this.first.unit))
call this.first.deallocate()
set this.first=next
set this.count=this.count-1
endloop
call this.deallocate()
endmethod
method clear takes nothing returns nothing
local UnitList next
loop
exitwhen 0==this.count
set next=this.first.next
call this.lookUp.remove(GetHandleId(this.first.unit))
call this.first.deallocate()
set this.first=next
set this.count=this.count-1
endloop
endmethod
method isEmpty takes nothing returns boolean
return (0==this.count)
endmethod
method hasUnit takes unit whichUnit returns boolean
return this.lookUp.has(GetHandleId(whichUnit))
endmethod
//! runtextmacro GroupTools_GroupEnumerationTools()
static method create takes nothing returns thistype
local thistype this=thistype.allocate()
set this.lookUp=Table.create()
set this.count=0
return this
endmethod
endstruct
struct SimpleGroup extends array
implement Alloc
//This one works like a number stack, it's a lot faster for unit groups that doesn't need
//removing units or checking for units inside of it.
private UnitList first
readonly integer count
integer data
private method safeIteration takes nothing returns nothing
local integer i=this.count
loop
exitwhen not(0==GetUnitTypeId(this.first.unit)) or 0==i
set this.first=this.first.next
set i=i-1
endloop
endmethod
method operator FirstOfGroup takes nothing returns unit
call this.safeIteration()
return this.first.unit
endmethod
method operator NextOfGroup takes nothing returns unit
set this.first=this.first.next
call this.safeIteration()
return this.first.unit
endmethod
method isEmpty takes nothing returns boolean
return (0==this.count)
endmethod
method addUnit takes unit whichUnit returns nothing
local UnitList thisUnit=UnitList.allocate()
local UnitList start
if 0==this.count then
set start=thisUnit
set this.first=thisUnit
else
set start=this.first
endif
set thisUnit.next=start.next
set start.next=thisUnit
set thisUnit.unit=whichUnit
set this.count=this.count+1
endmethod
method clear takes nothing returns nothing
local UnitList next
loop
exitwhen 0==this.count
set next=this.first.next
call this.first.deallocate()
set this.first=next
set this.count=this.count-1
endloop
endmethod
method destroy takes nothing returns nothing
local UnitList next
loop
exitwhen 0==this.count
set next=this.first.next
call first.deallocate()
set first=next
set this.count=this.count-1
endloop
call this.deallocate()
endmethod
//! runtextmacro GroupTools_GroupEnumerationTools()
static method create takes nothing returns thistype
local thistype this=thistype.allocate()
set this.count=0
return this
endmethod
endstruct
//! textmacro GroupTools_GroupEnumerationTools
//These two methods use Selection Sort, i can't think of a faster method to compare units
method farthestToLoc takes real x, real y returns unit
local integer i=0
local unit u=this.first.unit
local real od
local real ox=GetUnitX(u)-x
local real oy=GetUnitY(u)-y
local real min=SquareRoot(ox*ox+oy*oy)
local unit r=u
loop
exitwhen i==this.count
set this.first=this.first.next
call this.safeIteration()
set u=this.first.unit
set ox=GetUnitX(u)-x
set oy=GetUnitY(u)-y
set od=SquareRoot(ox*ox+oy*oy)
if od>min then
set r=u
set min=od
endif
set i=i+1
endloop
set u=null
return r
endmethod
method closestToLoc takes real x, real y returns unit
local integer i=0
local unit u=this.first.unit
local real od
local real ox=GetUnitX(u)-x
local real oy=GetUnitY(u)-y
local real min=SquareRoot(ox*ox+oy*oy)
local unit r=u
loop
exitwhen i==this.count
set this.first=this.first.next
call this.safeIteration()
set u=this.first.unit
set ox=GetUnitX(u)-x
set oy=GetUnitY(u)-y
set od=SquareRoot(ox*ox+oy*oy)
if od<min then
set r=u
set min=od
endif
set i=i+1
endloop
set u=null
return r
endmethod
private static thistype enumGroup
private static method addTo takes nothing returns boolean
call enumGroup.addUnit(GetFilterUnit())
return false
endmethod
method enumUnitsInRange takes real x, real y, real radius returns nothing
set enumGroup=this
call GroupEnumUnitsInRange(ENUM_GROUP,x,y,radius,Filter(function thistype.addTo))
endmethod
private static real tempX
private static real tempY
private static real tempN
private static real tempM
private static method addConeTo takes nothing returns boolean
local unit u=GetFilterUnit()
local real a=Atan2(GetUnitY(u)-tempY,GetUnitX(u)-tempX)
if a<0 then
set a=a+bj_PI*2
endif
if (a>=tempN-tempM) and (a<=tempN+tempM) then
call enumGroup.addUnit(u)
endif
set u=null
return false
endmethod
method enumUnitsInCone takes real x, real y, real radius, real facing, real coneAngle returns nothing
if 0==coneAngle then
debug call BJDebugMsg("the given cone angle was 0, group won't pick anything")
return
endif
set enumGroup=this
set tempX=x
set tempY=y
set tempN=facing
set tempM=coneAngle/2
call GroupEnumUnitsInRange(ENUM_GROUP,x,y,radius,Filter(function thistype.addConeTo))
endmethod
private static method addRingTo takes nothing returns boolean
local unit u=GetFilterUnit()
local real x=GetUnitX(u)-tempX
local real y=GetUnitY(u)-tempY
local real d=SquareRoot(x*x+y*y)
if d>=tempN then
call enumGroup.addUnit(u)
endif
set u=null
return false
endmethod
method enumUnitsInRing takes real x, real y, real innerRadius, real outerRadius returns nothing
if innerRadius>=outerRadius then
debug call BJDebugMsg("You can't have a ring with an inner radius bigger than the outer radius")
return
endif
set enumGroup=this
set tempX=x
set tempY=y
set tempN=innerRadius
call GroupEnumUnitsInRange(ENUM_GROUP,x,y,outerRadius,Filter(function thistype.addRingTo))
endmethod
method enumUnitsInRect takes rect r returns nothing
set enumGroup=this
call GroupEnumUnitsInRect(ENUM_GROUP,r,Filter(function thistype.addTo))
endmethod
//! endtextmacro
endlibrary