Builder Bob
Live free or don't
- Reaction score
- 249
JASS:
//====================================================================================================
// AreaEvent -- by Builder Bob -- v1.0
//====================================================================================================
//====================================================================================================
//
// ---------------------
// Table of Content:
// ---------------------
//
// 1. FUNCTION LIST
//
// 2. DOCUMENTATION
//
// 3. USER DEFINED CONSTANTS
//
// 4. CODE
//
//====================================================================================================
//====================================================================================================
// 1. FUNCTION LIST:
//====================================================================================================
//
// ------------------------------------------------------
// The following functions can be used from anywhere:
// ------------------------------------------------------
//
// RegisterAreaEventRect(real minx, real miny, real maxx, real maxy, boolexpr enterfunc, boolexpr leavefunc, integer data)
// returns integer
//
// RegisterAreaEventDisk(real x, real y, real radius, boolexpr enterfunc, boolexpr leavefunc, integer data)
// returns integer
//
// ...Suggest a new shape...
//
// DestroyAreaEvent(integer index)
// returns boolean
//
// ------------------------------------------------------------------------------------------
// The following functions should only be used within functions triggered by an AreaEvent
// ------------------------------------------------------------------------------------------
//
// GetAreaEventIndex()
// returns integer
//
// GetAreaEventTriggerUnit()
// returns unit
//
// GetAreaEventData()
// returns integer
//
//
//----------------------------------------------------------------------------------------------------
//==============================================================================
// 2. DOCUMENTATION:
//==============================================================================
//
// PURPOSE OF AREA EVENT:
// * To provide a simple and precise way to handle enter/leave region events.
//
// * The enter events also trigger on units already in the area you specify.
// Normal enter events does not.
//
// PROS:
// * You get the power to create a region event of various shapes with a single function call.
//
// * Integrated data attachment.
//
// * The system should be highly resistant to leaks and double free errors.
// However, it has only been alpha tested so far.
//
// * All triggers and regions used are recycled.
// DestroyTrigger() is not used, avoiding all bugs related to DestroyTrigger()
//
// CONS:
// * Once an area event has been registered, you have no access to the trigger or region involved.
// This means you will have to destroy and recreate the event should you want to move it later.
//
// * Incompatible with Warcraft 3 version 1.23a or lower
// To use the system in lower versions you will need a compatibility system.
//
// * A maximum of 8190 area events can exist at any time.
//
// HOW TO USE:
// * Register events with the RegisterAreaEvent... functions.
//
// * There are mutiple ways of destroying an existing area event.
// Any enter or leave function returning true will destroy the event.
// DestroyAreaEvent(index) will destroy the event with that index if one exist.
// Any attempt to destroy a non-existing event will simply be ignored.
//
// * To aqcuire the index for an area event you can do one of the following:
// All RegisterAreaEvent... functions will return the index of the registered event.
// Calling GetAreaEventIndex() from within an enter or leave function
// will give you the index of the event which called that function.
//
// * Units already in the defined area will trigger as GetFilterUnit(),
// while units entering/leaving will trigger as GetTriggerUnit().
// Use GetAreaEventTriggerUnit() to get the right unit.
// GetTriggerUnit() can safely be used inside leaveFunc.
//
// * Attach data to an event with the data argument in the register functions.
// Retrieve that data with GetAreaEventData() in enterFunc and leaveFunc.
//
// * Be sure to set the user defined constant MAX_COLLISION_SIZE
// to the collision size of the biggest unit in your map.
//
// THANKS TO:
// * Peppar - for RegionAddDisk and RegionClearDisk
//
// HOW TO IMPORT:
// * create a trigger
// * convert it to text and replace the whole trigger text with this one
//
//==============================================================================
//==============================================================================
// 3. USER DEFINED CONSTANTS:
//==============================================================================
library AreaEvent initializer onInit
globals
private constant real MAX_COLLISION_SIZE = 200.
endglobals
//==============================================================================
// 4. CODE:
//==============================================================================
private function interface EnumFunc takes nothing returns boolean
private function interface ClearFunc takes nothing returns boolean
globals
private hashtable Hashtable = InitHashtable()
private timer Timer = CreateTimer()
private group Group = CreateGroup()
private EnumFunc array EnumFuncs
private ClearFunc array ClearFuncs
private integer array Pos
private integer array Indexes
private trigger array EnterTriggers
private trigger array LeaveTriggers
private trigger array EnterExec
private trigger array LeaveExec
private real array MinX
private real array MinY
private real array MaxX
private real array MaxY
private real array Radius
private rect array Rects
private region array Regions
private integer array Datas
private integer TopIndex = 1
private integer MaxIndex = 1
private unit WhichUnit
private rect WhichRect = Rect(0., 0., 0., 0.)
private boolean TriggerEnabled
endglobals
globals
private integer First = 0
private integer This = 0
private integer array Next
endglobals
private function True takes nothing returns boolean
return true
endfunction
private function DisplayActive takes nothing returns nothing
debug local string s = ""
debug local integer i = 1
debug local integer topIndex = TopIndex
debug local integer maxIndex = MaxIndex
debug if topIndex > 17 then
debug set topIndex = 17
debug endif
debug if maxIndex > 17 then
debug set maxIndex = 17
debug endif
debug loop
debug exitwhen(i >= topIndex)
debug set s = s + "|c0000FF00[" + I2S(Indexes<i>) + "]|r"
debug set i = i + 1
debug endloop
debug loop
debug exitwhen(i >= maxIndex)
debug set s = s + "|c00FF0000[" + I2S(Indexes<i>) + "]|r"
debug set i = i + 1
debug endloop
debug call BJDebugMsg(s)
endfunction
private function RegionAddDisk takes region whichRegion, real x, real y, real radius returns nothing
local real r = 16.
local real w = 0.
loop
exitwhen(r > radius)
set w = Cos(Asin(r / radius)) * radius
call SetRect(WhichRect, x - w, y + r - 16., x + w, y + r + 16.)
call RegionAddRect(whichRegion, WhichRect)
call SetRect(WhichRect, x - w, y - r - 16., x + w, y - r + 16.)
call RegionAddRect(whichRegion, WhichRect)
set r = r + 32.
endloop
endfunction
private function RegionClearDisk takes region whichRegion, real x, real y, real radius returns nothing
local real r = 16.
local real w = 0.
loop
exitwhen(r > radius)
set w = Cos(Asin(r / radius)) * radius
call SetRect(WhichRect, x - w, y + r - 16., x + w, y + r + 16.)
call RegionClearRect(whichRegion, WhichRect)
call SetRect(WhichRect, x - w, y - r - 16., x + w, y - r + 16.)
call RegionClearRect(whichRegion, WhichRect)
set r = r + 32.
endloop
endfunction
private function RegionClearAreaRect takes nothing returns boolean
call RegionClearRect(Regions[This], Rects[This])
return false
endfunction
private function RegionClearAreaDisk takes nothing returns boolean
call RegionClearDisk(Regions[This], MinX[This], MinY[This], Radius[This])
return false
endfunction
function DestroyAreaEvent takes integer index returns boolean
local integer pos = Pos[index]
if pos != 0 and pos < TopIndex then
call ClearFuncs[index].evaluate()
call DisableTrigger(EnterTriggers[index])
call DisableTrigger(LeaveTriggers[index])
//swap pos
set TopIndex = TopIndex - 1
set Indexes[pos] = Indexes[TopIndex]
set Pos[Indexes[pos]] = pos
set Indexes[TopIndex] = index
set Pos[index] = TopIndex
if This == index then
set TriggerEnabled = false
endif
//debug call BJDebugMsg("|c00FF0000REM " + I2S(index))
//debug call DisplayActive()
return true
endif
return false
endfunction
private function FilterOutsideRegion takes nothing returns boolean
if TriggerEnabled then
if IsUnitInRegion(Regions[This], GetFilterUnit()) then
if TriggerEvaluate(EnterExec[This]) then
call DestroyAreaEvent(This)
endif
endif
endif
return false
endfunction
private function Leave takes nothing returns boolean
set This = LoadInteger(Hashtable, 0, GetHandleId(GetTriggeringTrigger()))
if TriggerEvaluate(LeaveExec[This]) then
call DestroyAreaEvent(This)
endif
return false
endfunction
private function Enter takes nothing returns boolean
set This = LoadInteger(Hashtable, 0, GetHandleId(GetTriggeringTrigger()))
if TriggerEvaluate(EnterExec[This]) then
call DestroyAreaEvent(This)
endif
return false
endfunction
private function EnumAreaRect takes nothing returns boolean
call SetRect(WhichRect, MinX[This] - MAX_COLLISION_SIZE, MinY[This] - MAX_COLLISION_SIZE, MaxX[This] + MAX_COLLISION_SIZE, MaxY[This] + MAX_COLLISION_SIZE)
call GroupEnumUnitsInRect(Group, WhichRect, Condition(function FilterOutsideRegion))
return false
endfunction
private function EnumAreaDisk takes nothing returns boolean
call GroupEnumUnitsInRange(Group, MinX[This], MinY[This], Radius[This] + MAX_COLLISION_SIZE, Condition(function FilterOutsideRegion))
return false
endfunction
private function Expire takes nothing returns boolean
local integer next
set This = First
set First = 0
loop
exitwhen(This == 0)
call EnableTrigger(EnterTriggers[This])
call EnableTrigger(LeaveTriggers[This])
set TriggerEnabled = true
set next = Next[This]
call EnumFuncs[This].evaluate()
set This = next
endloop
return false
endfunction
private function NewIndex takes boolexpr enterFunc, boolexpr leaveFunc, integer data returns integer
local integer index
debug if TopIndex == 8191 then
debug call BJDebugMsg("Warning: AreaEvent stack is full!")
debug return 0
debug endif
if TopIndex < MaxIndex then
set index = Indexes[TopIndex]
call TriggerClearConditions(EnterExec[index])
call TriggerClearConditions(LeaveExec[index])
set TopIndex = TopIndex + 1
//debug call BJDebugMsg("|c00FFFF00REUSE " + I2S(index))
//debug call DisplayActive()
else
set index = TopIndex
set EnterTriggers[index] = CreateTrigger()
set LeaveTriggers[index] = CreateTrigger()
set EnterExec[index] = CreateTrigger()
set LeaveExec[index] = CreateTrigger()
call DisableTrigger(EnterTriggers[index])
call DisableTrigger(LeaveTriggers[index])
set Rects[index] = Rect(0., 0., 0., 0.)
set Regions[index] = CreateRegion()
call TriggerRegisterEnterRegion(EnterTriggers[index], Regions[index], Condition(function True))
call TriggerAddCondition(EnterTriggers[index], Condition(function Enter))
call TriggerRegisterLeaveRegion(LeaveTriggers[index], Regions[index], Condition(function True))
call TriggerAddCondition(LeaveTriggers[index], Condition(function Leave))
call SaveInteger(Hashtable, 0, GetHandleId(EnterTriggers[index]), index)
call SaveInteger(Hashtable, 0, GetHandleId(LeaveTriggers[index]), index)
set Pos[index] = index
set Indexes[index] = index
set TopIndex = TopIndex + 1
set MaxIndex = MaxIndex + 1
//debug call BJDebugMsg("|c0000FF00NEW " + I2S(index))
//debug call DisplayActive()
endif
call TriggerAddCondition(EnterExec[index], enterFunc)
call TriggerAddCondition(LeaveExec[index], leaveFunc)
set Datas[index] = data
set Next[index] = First
set First = index
call TimerStart(Timer, 0., false, function Expire)
return index
endfunction
function RegisterAreaEventDisk takes real x, real y, real radius, boolexpr enterFunc, boolexpr leaveFunc, integer data returns integer
local integer index = NewIndex(enterFunc, leaveFunc, data)
debug if index == 0 then
debug return 0
debug endif
set MinX[index] = x
set MinY[index] = y
set Radius[index] = radius
set EnumFuncs[index] = EnumFunc.EnumAreaDisk
set ClearFuncs[index] = ClearFunc.RegionClearAreaDisk
call RegionAddDisk(Regions[index], x, y, radius)
return index
endfunction
function RegisterAreaEventRect takes real minx, real miny, real maxx, real maxy, boolexpr enterFunc, boolexpr leaveFunc, integer data returns integer
local integer index = NewIndex(enterFunc, leaveFunc, data)
debug if index == 0 then
debug return 0
debug endif
set MinX[index] = minx
set MinY[index] = miny
set MaxX[index] = maxx
set MaxY[index] = maxy
set EnumFuncs[index] = EnumFunc.EnumAreaRect
set ClearFuncs[index] = ClearFunc.RegionClearAreaRect
call SetRect(Rects[index], minx, miny, maxx, maxy)
call RegionAddRect(Regions[index], Rects[index])
return index
endfunction
function GetAreaEventIndex takes nothing returns integer
return This
endfunction
function GetAreaEventData takes nothing returns integer
return Datas[This]
endfunction
function GetAreaEventTriggerUnit takes nothing returns unit
set WhichUnit = GetTriggerUnit()
if WhichUnit != null then
return WhichUnit
endif
return GetFilterUnit()
endfunction
private function AreaEventStats takes nothing returns boolean
debug local integer i = 1
debug local integer topIndex = TopIndex
debug if topIndex > 17 then
debug set topIndex = 17
debug endif
debug loop
debug exitwhen(i >= topIndex)
debug call PingMinimap(MinX[Indexes<i>], MinY[Indexes<i>], 1.)
debug set i = i + 1
debug endloop
debug call BJDebugMsg("=============================================")
debug call BJDebugMsg("Biggest index ever = " + I2S(MaxIndex - 1))
debug call BJDebugMsg("Indexes in use = " + I2S(TopIndex - 1))
debug call BJDebugMsg("Released indexes = " + I2S(MaxIndex - TopIndex))
debug call BJDebugMsg("=============================================")
debug return false
endfunction
private function onInit takes nothing returns nothing
debug local trigger trig = CreateTrigger()
debug call TriggerRegisterPlayerChatEvent(trig, Player(0), "-AreaEventStats", true)
debug call TriggerAddCondition(trig, Condition(function AreaEventStats))
endfunction
endlibrary</i></i></i></i>
I will make a demo map if there is any interest.