//TESH.scrollpos=0
//TESH.alwaysfold=0
function VeryUniquelyNamedFunction takes nothing returns nothing
endfunction
Name | Type | is_array | initial_value |
Demo_Angle | real | No | |
Demo_Effect | effect | No | |
Demo_Unit | unit | No | |
Dummy_Angle | real | Yes | |
Dummy_Count | integer | Yes | |
Dummy_CountHead | integer | Yes | |
Dummy_CountNext | integer | Yes | |
Dummy_CountPrev | integer | Yes | |
Dummy_Hashtable | hashtable | No | |
Dummy_LastInstance | integer | No | |
Dummy_Lower | integer | No | |
Dummy_Next | integer | Yes | |
Dummy_Prev | integer | Yes | |
Dummy_Unit | unit | Yes | |
Dummy_UnitCount | integer | No | |
Dummy_Upper | integer | No | |
Dummy_X | real | No | |
Dummy_Y | real | No |
//=============================================================
// How to Import:
//=============================================================
// 1. Copy the DummyRecycler Folder to your map.
//
// 2. Go to your map, before pasting make sure that you check the "Automatically
// create unknown variables while pasting trigger data" in File->Preferences
// of World Editor. After the variables are created, you can then delete the
// corresponding Variable Creator you copied.
//
// 3. Delete DummyRecycler Variable Creator and this trigger instruction.
// DummyRecycler v1.25
// by Flux
//
// A system that recycles dummy units while considering their facing angle.
// It can be used as attachment dummies for visual effects or as dummy caster.
//
// Why is recycling a unit important?
// Because creating a unit is is one of the slowest function in the game
// and there are reports that will always leave a permanent tiny bit of
// memory (0.04 KB).
// On average, retrieving a pending Dummy is approximately 4x faster compared
// to creating a new one and recycling a Dummy compared to removing it is
// approximately 1.3x faster.
// Furthermore, if you're using a lot of "Unit has entered map" events,
// using this system will even result to even more better performance
// because retrieving Dummy units does not cause that event to run.
//
//
// Features:
//
// -- Dummy Sharing
// When a Dummy List gets low on unit count, it will borrow Dummy Units
// from the Dummy List with the highest unit count. The transfer is not
// instant because the shared Dummy Unit has to turn to the appropriate
// angle of its new Dummy List before it can be recycled.
// See BORROW_REQUEST.
//
// -- Self-balancing recycling algorithm
// Recycled Dummy Units will be thrown to the List having the least number
// of Dummy Units.
//
// -- Recycling least used
// Allows recycling a Dummy from the Dummy List with the highest
// unit count. It is useful when the facing angle of the Dummy Unit
// does not matter.
// See GetRecycledDummyAnyAngle.
//
// -- Self-adaptation
// When there are no free Dummy Units from a Dummy List, it will end up creating
// a new unit instead but that unit will be permanently added as a Dummy
// Unit to be recycled increasing the overall total Dummy Unit count.
//
// -- Count control
// Allows limiting the overall number of Dummy Units.
// See MAX_DUMMY_COUNT.
//
// -- Delayed Recycle
// Allows recycling Dummy Units after some delay to allocate time for the
// death animation of Special Effects to be seen.
// See DummyAddRecycleTimer.
//
// ******************************************************************
// ***************************** API: *******************************
// ******************************************************************
//
// function GetRecycledDummy takes real x, real y, real z, real facing returns unit
// - Retrieve an unused Dummy Unit from the List.
// - The equivalent of CreateUnit.
// - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
// function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
// - Use this function if the facing angle of the Dummy doesn't matter to you.
// - It will return a unit from the list having the highest number of unused Dummy Units.
// - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
// function RecycleDummy takes unit u returns nothing
// - Recycle the Dummy unit for it to be used again later.
// - The equivalent of RemoveUnit.
//
// function DummyAddRecycleTimer takes unit u, real time returns nothing
// - Recycle the Dummy unit after a certain time.
// - Use this to allocate time for the the death animation of an effect attached to the
// Dummy Unit to finish..
// - The equivalent of UnitApplyTimedLife.
//
// function ShowDummy takes unit u, boolean flag returns nothing
// - Shows/hides Dummy Unit without conflicting with the Locust ability.
//
//--------------------
// CREDITS
//--------------------
// Bribe - for the MissileRecycler (vJASS) where I got this concept from
// http://www.hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
// - for the optional Table
// http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
// Vexorian - for the Attachable and Pitch Animation Model (dummy.mdx)
// http://www.wc3c.net/showthread.php?t=101150
// Maker and IcemanBo - for the unit permanent 0.04 KB memory leak of units.
// http://www.hiveworkshop.com/forums/trigger-gui-editor-tutorials-279/memory-leaks-263410/
// Nestharus - for the data structure
// http://www.hiveworkshop.com/forums/2809461-post7.html
// - for the optional WorldBounds
// http://githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j
// =============================================================== //
// ====================== CONFIGURATION ========================== //
// =============================================================== //
//The rawcode of the Dummy Unit
constant function Dummy_Rawcode takes nothing returns integer
return 'dumi'
endfunction
//The owner of the Dummy Unit
constant function Dummy_Owner takes nothing returns player
return Player(14)
endfunction
//The number of indexed angle. The higher the value the:
// - Lesser the turning time for the Dummy Units.
// - Higher the total number of Dummy Units created at Map Initialization.
// Recommended Value: 10 (Max difference of 18 degrees)
constant function Dummy_Angles takes nothing returns integer
return 10
endfunction
//The number of Dummy units per Dummy_Angle. The higher the value the:
// - Higher the number of units that can be recycled per angle, when
// no more units are in queue, the system will resort to use CreateUnit.
// - Higher the total number of Dummy Units created at Map Initialization.
// Recommended Value: 3 to 5 (for less overhead in Map Loading Screen)
constant function Dummy_StoredUnits takes nothing returns integer
return 3
endfunction
//The maximum number of Dummy units that can exist. When the system resort
//to using CreateUnit, the unit will be permanently added to the Dummy
//List. To avoid spamming Dummy Units and having too much free Dummy
//Units to allocate, the maximum number of Dummy Units is capped.
// Recommended Value: 80 to 120
constant function Dummy_MaxCount takes nothing returns integer
return 100
endfunction
//When a certain angle have less than BORROW_REQUEST units in its list,
//it will start to borrow Dummy Units from the list with the highest
//Dummy Unit count.
// Recommended Value: Half of maximum Dummy_StoredUnits
constant function Dummy_BorrowRequest takes nothing returns integer
return 5
endfunction
//It will only return a Dummy if the current dummy is close
//to it's appropriate facing angle. This is to avoid returning
//a Dummy which is still turning to face it's list angle.
constant function Dummy_AngleTolerance takes nothing returns real
return 10.0
endfunction
//An additional option to automatically hide recycled dummy units in the
//corner of the map camera bounds
constant function Dummy_HideOnMapCorner takes nothing returns boolean
return true
endfunction
// =============================================================== //
// ==================== END CONFIGURATION ======================== //
// =============================================================== //
function Dummy_GetHead takes integer facing returns integer
if facing < 0 or facing >= 360 then
set facing = facing - (facing/360)*360
if facing < 0 then
set facing = facing + 360
endif
endif
return R2I((facing*Dummy_Angles()/360.0))
endfunction
function ShowDummy takes unit u, boolean flag returns nothing
if IsUnitHidden(u) == flag then
call ShowUnit(u, flag)
if flag and GetUnitTypeId(u) == Dummy_Rawcode() then
call UnitRemoveAbility(u, 'Aloc')
call UnitAddAbility(u, 'Aloc')
endif
endif
endfunction
function GetRecycledDummy takes real x, real y, real z, real facing returns unit
local integer head = Dummy_GetHead(R2I(facing + 180.0/Dummy_Angles()))
local integer this = udg_Dummy_Next[head]
local integer countHead
//If there are Dummy Units in the Queue List already facing the appropriate angle
if this != head and RAbsBJ(GetUnitFacing(udg_Dummy_Unit[this]) - udg_Dummy_Angle[head]) <= Dummy_AngleTolerance() then
//Remove from the Queue List
set udg_Dummy_Next[udg_Dummy_Prev[this]] = udg_Dummy_Next[this]
set udg_Dummy_Prev[udg_Dummy_Next[this]] = udg_Dummy_Prev[this]
//For double free protection
set udg_Dummy_Next[this] = -1
//Unit Properties
set bj_lastCreatedUnit = udg_Dummy_Unit[this]
call SetUnitX(bj_lastCreatedUnit, x)
call SetUnitY(bj_lastCreatedUnit, y)
call SetUnitFacing(bj_lastCreatedUnit, facing)
call SetUnitVertexColor(bj_lastCreatedUnit, 255, 255, 255, 255)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
call ShowDummy(bj_lastCreatedUnit, true)
//------------------------------------------------
// Comment out resets you don't need
//------------------------------------------------
call SetUnitScale(bj_lastCreatedUnit, 1, 0, 0)
call SetUnitAnimationByIndex(bj_lastCreatedUnit, 90)
//Update Count and Bounds
set udg_Dummy_Count[head] = udg_Dummy_Count[head] - 1
//------------------------------------------------
// Unit Sharing
//------------------------------------------------
if udg_Dummy_Count[head] < Dummy_BorrowRequest() and udg_Dummy_Count[udg_Dummy_CountNext[udg_Dummy_CountHead[udg_Dummy_Upper]]] > udg_Dummy_Count[head] then
set udg_Dummy_Count[head] = udg_Dummy_Count[head] + 1
//Take an instance from the UpperBound list
set this = udg_Dummy_Next[udg_Dummy_CountNext[udg_Dummy_CountHead[udg_Dummy_Upper]]]
call SetUnitFacing(udg_Dummy_Unit[this], udg_Dummy_Angle[head])
//Remove
set udg_Dummy_Next[udg_Dummy_Prev[this]] = udg_Dummy_Next[this]
set udg_Dummy_Prev[udg_Dummy_Next[this]] = udg_Dummy_Prev[this]
//Add to the Current List
set udg_Dummy_Next[this] = head
set udg_Dummy_Prev[this] = udg_Dummy_Prev[head]
set udg_Dummy_Next[udg_Dummy_Prev[this]] = this
set udg_Dummy_Prev[udg_Dummy_Next[this]] = this
set head = udg_Dummy_CountNext[udg_Dummy_CountHead[udg_Dummy_Upper]]
set udg_Dummy_Count[head] = udg_Dummy_Count[head] - 1
endif
//---------------------------
//Update Count Lists
//---------------------------
//Remove from the current Count List
set udg_Dummy_CountNext[udg_Dummy_CountPrev[head]] = udg_Dummy_CountNext[head]
set udg_Dummy_CountPrev[udg_Dummy_CountNext[head]] = udg_Dummy_CountPrev[head]
//Add to the new Count List
set countHead = udg_Dummy_CountHead[udg_Dummy_Count[head]]
set udg_Dummy_CountNext[head] = countHead
set udg_Dummy_CountPrev[head] = udg_Dummy_CountPrev[countHead]
set udg_Dummy_CountNext[udg_Dummy_CountPrev[head]] = head
set udg_Dummy_CountPrev[udg_Dummy_CountNext[head]] = head
//---------------------------
// Update Bounds
//---------------------------
set countHead = udg_Dummy_CountHead[udg_Dummy_Upper]
if udg_Dummy_CountNext[countHead] == countHead then
set udg_Dummy_Upper = udg_Dummy_Upper - 1
endif
if udg_Dummy_Count[head] < udg_Dummy_Lower then
set udg_Dummy_Lower = udg_Dummy_Count[head]
endif
else
set bj_lastCreatedUnit = CreateUnit(Dummy_Owner(), Dummy_Rawcode(), x, y, facing)
call PauseUnit(bj_lastCreatedUnit, true)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
if udg_Dummy_UnitCount < Dummy_MaxCount() then
set this = udg_Dummy_LastInstance
set udg_Dummy_Next[this] = -1
set udg_Dummy_Unit[this] = bj_lastCreatedUnit
call SaveInteger(udg_Dummy_Hashtable, GetHandleId(bj_lastCreatedUnit), 0, this)
set udg_Dummy_LastInstance = udg_Dummy_LastInstance + 1
endif
set udg_Dummy_UnitCount = udg_Dummy_UnitCount + 1
endif
return bj_lastCreatedUnit
endfunction
function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
return GetRecycledDummy(x, y, z, udg_Dummy_Angle[udg_Dummy_CountNext[udg_Dummy_CountHead[udg_Dummy_Upper]]])
endfunction
function RecycleDummy takes unit u returns nothing
local integer this = LoadInteger(udg_Dummy_Hashtable, GetHandleId(u), 0)
local integer head
local integer countHead
//If the unit is a legit Dummy Unit
if this > 0 and udg_Dummy_Next[this] == -1 then
//Find where to insert based on the list having the least number of units
set head = udg_Dummy_CountNext[udg_Dummy_CountHead[udg_Dummy_Lower]]
set udg_Dummy_Next[this] = head
set udg_Dummy_Prev[this] = udg_Dummy_Prev[head]
set udg_Dummy_Next[udg_Dummy_Prev[this]] = this
set udg_Dummy_Prev[udg_Dummy_Next[this]] = this
//Update Status
call SetUnitFacing(u, udg_Dummy_Angle[head])
call PauseUnit(u, true)
call SetUnitOwner(u, Dummy_Owner(), false)
if Dummy_HideOnMapCorner() then
call SetUnitX(u, udg_Dummy_X)
call SetUnitY(u, udg_Dummy_Y)
else
call SetUnitVertexColor(u, 0, 0, 0, 0)
endif
set udg_Dummy_Count[head] = udg_Dummy_Count[head] + 1
//---------------------------
// Update Count Lists
//---------------------------
//Remove
set udg_Dummy_CountNext[udg_Dummy_CountPrev[head]] = udg_Dummy_CountNext[head]
set udg_Dummy_CountPrev[udg_Dummy_CountNext[head]] = udg_Dummy_CountPrev[head]
//Add to the new Count List
set countHead = udg_Dummy_CountHead[udg_Dummy_Count[head]]
set udg_Dummy_CountNext[head] = countHead
set udg_Dummy_CountPrev[head] = udg_Dummy_CountPrev[countHead]
set udg_Dummy_CountNext[udg_Dummy_CountPrev[head]] = head
set udg_Dummy_CountPrev[udg_Dummy_CountNext[head]] = head
//---------------------------
// Update Bounds
//---------------------------
set countHead = udg_Dummy_CountHead[udg_Dummy_Lower]
if udg_Dummy_CountNext[countHead] == countHead then
set udg_Dummy_Lower = udg_Dummy_Lower + 1
endif
if udg_Dummy_Count[head] > udg_Dummy_Upper then
set udg_Dummy_Upper = udg_Dummy_Count[head]
endif
elseif this == 0 then
//User tries to recycle an invalid unit, remove the unit instead
call RemoveUnit(u)
endif
endfunction
function Dummy_TimerExpires takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
call RecycleDummy(LoadUnitHandle(udg_Dummy_Hashtable, id, 0))
call FlushChildHashtable(udg_Dummy_Hashtable, id)
call DestroyTimer(t)
set t = null
endfunction
function DummyAddRecycleTimer takes unit u, real time returns nothing
local timer t = CreateTimer()
call SaveUnitHandle(udg_Dummy_Hashtable, GetHandleId(t), 0, u)
call TimerStart(t, time, false, function Dummy_TimerExpires)
set t = null
endfunction
function InitTrig_DummyRecycler_JASS takes nothing returns nothing
local integer id = Dummy_Rawcode()
local player owner = Dummy_Owner()
local integer unitCount = Dummy_StoredUnits()
local real add = 360.0/Dummy_Angles()
local real angle = 0
local integer this = Dummy_Angles()
local integer head = 0
local integer countHead = JASS_MAX_ARRAY_SIZE - 1 //avoid allocation collision
local integer i = R2I(Dummy_MaxCount()/Dummy_Angles() + 0.5)
local rect r
set udg_Dummy_Hashtable = InitHashtable()
set udg_Dummy_Upper = unitCount
set udg_Dummy_Lower = unitCount
//Create Map Bounds
if Dummy_HideOnMapCorner() then
set r = GetWorldBounds()
set udg_Dummy_X = GetRectMaxX(r)
set udg_Dummy_Y = GetRectMaxY(r)
call RemoveRect(r)
set r = null
endif
//Initialize countHeads
loop
exitwhen i < 0
set udg_Dummy_CountNext[countHead] = countHead
set udg_Dummy_CountPrev[countHead] = countHead
set udg_Dummy_CountHead[i] = countHead
set countHead = countHead - 1
set i = i - 1
endloop
set countHead = udg_Dummy_CountHead[unitCount] //All heads will be inserted here initially
//Create the Dummy units
loop
exitwhen angle >= 360
//Initialize head
set udg_Dummy_Next[head] = head
set udg_Dummy_Prev[head] = head
set udg_Dummy_Count[head] = unitCount
set udg_Dummy_Angle[head] = angle
//Insert head in the Count List
set udg_Dummy_CountNext[head] = countHead
set udg_Dummy_CountPrev[head] = udg_Dummy_CountPrev[countHead]
set udg_Dummy_CountNext[udg_Dummy_CountPrev[head]] = head
set udg_Dummy_CountPrev[udg_Dummy_CountNext[head]] = head
set i = 0
loop
exitwhen i >= unitCount
//Queued Linked List
set udg_Dummy_Next[this] = head
set udg_Dummy_Prev[this] = udg_Dummy_Prev[head]
set udg_Dummy_Next[udg_Dummy_Prev[this]] = this
set udg_Dummy_Prev[udg_Dummy_Next[this]] = this
//The actual unit
set udg_Dummy_Unit[this] = CreateUnit(owner, id, udg_Dummy_X, udg_Dummy_Y, angle)
call PauseUnit(udg_Dummy_Unit[this], true)
call SaveInteger(udg_Dummy_Hashtable, GetHandleId(udg_Dummy_Unit[this]), 0, this)
set this = this + 1
set i = i + 1
endloop
set head = head + 1
set angle = angle + add
endloop
set udg_Dummy_LastInstance = this
set udg_Dummy_UnitCount = Dummy_Angles()*unitCount
endfunction
// runtextmacro DUMMY_DEBUG_TOOLS_JASS()
library DummyRecycler /*
// DummyRecycler v1.25
// by Flux
//
// A system that recycles dummy units while considering their facing angle.
// It can be used as attachment dummies for visual effects or as dummy caster.
//
// Why is recycling a unit important?
// Because creating a unit is is one of the slowest function in the game
// and there are reports that will always leave a permanent tiny bit of
// memory (0.04 KB).
// On average, retrieving a pending Dummy is approximately 4x faster compared
// to creating a new one and recycling a Dummy compared to removing it is
// approximately 1.3x faster.
// Furthermore, if you're using a lot of "Unit has entered map" events,
// using this system will even result to even more better performance
// because retrieving Dummy units does not cause that event to run.
*/ requires /*
nothing
*/ optional Table/*
if not found, this system will use a hashtable. Hashtables are limited to
255 per map.
*/ optional WorldBounds /*
if not found, this system will initialize its own Map Boundaries.
//
//
// Features:
//
// -- Dummy Sharing
// When a Dummy List gets low on unit count, it will borrow Dummy Units
// from the Dummy List with the highest unit count. The transfer is not
// instant because the shared Dummy Unit has to turn to the appropriate
// angle of its new Dummy List before it can be recycled.
// See BORROW_REQUEST.
//
// -- Self-balancing recycling algorithm
// Recycled Dummy Units will be thrown to the List having the least number
// of Dummy Units.
//
// -- Recycling least used
// Allows recycling a Dummy from the Dummy List with the highest
// unit count. It is useful when the facing angle of the Dummy Unit
// does not matter.
// See GetRecycledDummyAnyAngle.
//
// -- Self-adaptation
// When there are no free Dummy Units from a Dummy List, it will end up creating
// a new unit instead but that unit will be permanently added as a Dummy
// Unit to be recycled increasing the overall total Dummy Unit count.
//
// -- Count control
// Allows limiting the overall number of Dummy Units.
// See MAX_DUMMY_COUNT.
//
// -- Delayed Recycle
// Allows recycling Dummy Units after some delay to allocate time for the
// death animation of Special Effects to be seen.
// See DummyAddRecycleTimer.
//
// ******************************************************************
// ***************************** API: *******************************
// ******************************************************************
//
// function GetRecycledDummy takes real x, real y, real z, real facing returns unit
// - Retrieve an unused Dummy Unit from the List.
// - The equivalent of CreateUnit.
// - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
// function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
// - Use this function if the facing angle of the Dummy doesn't matter to you.
// - It will return a unit from the list having the highest number of unused Dummy Units.
// - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
// function RecycleDummy takes unit u returns nothing
// - Recycle the Dummy unit for it to be used again later.
// - The equivalent of RemoveUnit.
//
// function DummyAddRecycleTimer takes unit u, real time returns nothing
// - Recycle the Dummy unit after a certain time.
// - Use this to allocate time for the the death animation of an effect attached to the
// Dummy Unit to finish..
// - The equivalent of UnitApplyTimedLife.
//
// function ShowDummy takes unit u, boolean flag returns nothing
// - Shows/hides Dummy Unit without conflicting with the Locust ability.
//
//--------------------
// CREDITS
//--------------------
// Bribe - for the MissileRecycler (vJASS) where I got this concept from
// http://www.hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
// - for the optional Table
// http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
// Vexorian - for the Attachable and Pitch Animation Model (dummy.mdx)
// http://www.wc3c.net/showthread.php?t=101150
// Maker and IcemanBo - for the unit permanent 0.04 KB memory leak of units.
// http://www.hiveworkshop.com/forums/trigger-gui-editor-tutorials-279/memory-leaks-263410/
// Nestharus - for the data structure
// http://www.hiveworkshop.com/forums/2809461-post7.html
// - for the optional WorldBounds
// http://githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j
// =============================================================== //
// ====================== CONFIGURATION ========================== //
// =============================================================== */
globals
//The rawcode of the Dummy Unit
private constant integer DUMMY_ID = 'dumi'
//The owner of the Dummy Unit
private constant player OWNER = Player(14)
//The number of indexed angle. The higher the value the:
// - Lesser the turning time for the Dummy Units.
// - Higher the total number of Dummy Units created at Map Initialization.
// Recommended Value: 10 (Max difference of 18 degrees)
private constant integer ANGLES_COUNT = 10
//The number of Dummy units per ANGLES_COUNT. The higher the value the:
// - Higher the number of units that can be recycled per angle, when
// no more units are in queue, the system will resort to use CreateUnit.
// - Higher the total number of Dummy Units created at Map Initialization.
// Recommended Value: 3 to 5 (for less overhead in Map Loading Screen)
private constant integer STORED_UNIT_COUNT = 3
//The maximum number of Dummy units that can exist. When the system resort
//to using CreateUnit, the unit will be permanently added to the Dummy
//List. To avoid spamming Dummy Units and having too much free Dummy
//Units to allocate, the maximum number of Dummy Units is capped.
// Recommended Value: 80 to 120
private constant integer MAX_DUMMY_COUNT = 100
//When a certain angle have less than BORROW_REQUEST units in its list,
//it will start to borrow Dummy Units from the list with the highest
//Dummy Unit count.
// Recommended Value: Half of maximum STORED_UNIT_COUNT
private constant integer BORROW_REQUEST = 5
//It will only return a Dummy if the current dummy is close
//to it's appropriate facing angle. This is to avoid returning
//a Dummy which is still turning to face it's list angle.
private constant real ANGLE_TOLERANCE = 10.0
//An additional option to automatically hide recycled dummy units in the
//corner of the map camera bounds
private constant boolean HIDE_ON_MAP_CORNER = true
endglobals
//Every time a new dummy unit is retrieved, it will apply this resets
//If it is redundant/you dont need it, remove it.
//! textmacro DUMMY_UNIT_RESET
call SetUnitScale(bj_lastCreatedUnit, 1, 0, 0)
call SetUnitVertexColor(bj_lastCreatedUnit, 255, 255, 255, 255)
call SetUnitAnimationByIndex(bj_lastCreatedUnit, 90)
call ShowDummy(bj_lastCreatedUnit, true)
//! endtextmacro
// =============================================================== //
// ==================== END CONFIGURATION ======================== //
// =============================================================== //
globals
private integer dummyCount = ANGLES_COUNT*STORED_UNIT_COUNT
private real array angle
private integer array count
private integer array countHead
private integer array countNext
private integer array countPrev
private integer array next
private integer array prev
private unit array dummy
private integer upper
private integer lower
private integer lastInstance
private constant real FACING_OFFSET = 180.0/ANGLES_COUNT
endglobals
static if HIDE_ON_MAP_CORNER and not LIBRARY_WorldBounds then
private module BoundsInit
readonly static real x
readonly static real y
private static method onInit takes nothing returns nothing
local rect map = GetWorldBounds()
set thistype.x = GetRectMaxX(map)
set thistype.y = GetRectMaxY(map)
call RemoveRect(map)
set map = null
endmethod
endmodule
private struct Bounds extends array
implement BoundsInit
endstruct
endif
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable hash = InitHashtable()
endif
private static method onInit takes nothing returns nothing
local real add = 360.0/ANGLES_COUNT
local real a = 0
local integer this = ANGLES_COUNT
local integer head = 0
local integer cHead = JASS_MAX_ARRAY_SIZE - 1 //avoid allocation collision
local integer i = R2I(MAX_DUMMY_COUNT/ANGLES_COUNT + 0.5)
set upper = STORED_UNIT_COUNT
set lower = STORED_UNIT_COUNT
static if LIBRARY_Table then
set tb = Table.create()
endif
//Initialize countHeads
loop
exitwhen i < 0
set countNext[cHead] = cHead
set countPrev[cHead] = cHead
set countHead[i] = cHead
set cHead = cHead - 1
set i = i - 1
endloop
set cHead = countHead[STORED_UNIT_COUNT] //All heads will be inserted here initially
//Create the Dummy units
loop
exitwhen a >= 360
//Initialize head
set next[head] = head
set prev[head] = head
set count[head] = STORED_UNIT_COUNT
set angle[head] = a
//Insert head in the Count List
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
set i = 0
loop
exitwhen i >= STORED_UNIT_COUNT
//Queued Linked List
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
static if HIDE_ON_MAP_CORNER then
static if LIBRARY_WorldBounds then
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, WorldBounds.maxX, WorldBounds.maxY, a)
else
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, Bounds.x, Bounds.y, a)
endif
else
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, 0, 0, a)
endif
call PauseUnit(dummy[this], true)
static if LIBRARY_Table then
set tb[GetHandleId(dummy[this])] = this
else
call SaveInteger(hash, GetHandleId(dummy[this]), 0, this)
endif
set this = this + 1
set i = i + 1
endloop
set head = head + 1
set a = a + add
endloop
set lastInstance = this
endmethod
endmodule
private struct S extends array
implement M
endstruct
private function GetHead takes integer facing returns integer
if facing < 0 or facing >= 360 then
set facing = facing - (facing/360)*360
if facing < 0 then
set facing = facing + 360
endif
endif
return R2I((facing*ANGLES_COUNT/360.0))
endfunction
function ShowDummy takes unit u, boolean flag returns nothing
if IsUnitHidden(u) == flag then
call ShowUnit(u, flag)
if flag and GetUnitTypeId(u) == DUMMY_ID then
call UnitRemoveAbility(u, 'Aloc')
call UnitAddAbility(u, 'Aloc')
endif
endif
endfunction
function GetRecycledDummy takes real x, real y, real z, real facing returns unit
local integer head = GetHead(R2I(facing + FACING_OFFSET))
local integer this = next[head]
local integer cHead
//If there are Dummy Units in the Queue List already facing close to the appropriate angle
if this != head and RAbsBJ(GetUnitFacing(dummy[this]) - angle[head]) <= ANGLE_TOLERANCE then
//Remove from the Queue List
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
//For double free protection
set next[this] = -1
//Unit Properties
set bj_lastCreatedUnit = dummy[this]
call SetUnitX(bj_lastCreatedUnit, x)
call SetUnitY(bj_lastCreatedUnit, y)
call SetUnitFacing(bj_lastCreatedUnit, facing)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
//! runtextmacro DUMMY_UNIT_RESET()
//Update Count and Bounds
set count[head] = count[head] - 1
//------------------------------------------------
// Unit Sharing
//------------------------------------------------
if count[head] < BORROW_REQUEST and count[countNext[countHead[upper]]] > count[head] then
set count[head] = count[head] + 1
set this = next[countNext[countHead[upper]]]
call SetUnitFacing(dummy[this], angle[head])
//Remove
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
//Add to the Current List
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
set head = countNext[countHead[upper]]
set count[head] = count[head] - 1
endif
//---------------------------
//Update Count Lists
//---------------------------
//Remove from the current Count List
set countNext[countPrev[head]] = countNext[head]
set countPrev[countNext[head]] = countPrev[head]
//Add to the new Count List
set cHead = countHead[count[head]]
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
//---------------------------
// Update Bounds
//---------------------------
set cHead = countHead[upper]
if countNext[cHead] == cHead then
set upper = upper - 1
endif
if count[head] < lower then
set lower = count[head]
endif
else
set bj_lastCreatedUnit = CreateUnit(OWNER, DUMMY_ID, x, y, facing)
call PauseUnit(bj_lastCreatedUnit, true)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
if dummyCount < MAX_DUMMY_COUNT then
set this = lastInstance
//For double free protection
set next[this] = -1
set dummy[this] = bj_lastCreatedUnit
static if LIBRARY_Table then
set S.tb[GetHandleId(bj_lastCreatedUnit)] = this
else
call SaveInteger(S.hash, GetHandleId(bj_lastCreatedUnit), 0, this)
endif
set lastInstance = lastInstance + 1
endif
set dummyCount = dummyCount + 1
endif
return bj_lastCreatedUnit
endfunction
function RecycleDummy takes unit u returns nothing
static if LIBRARY_Table then
local integer this = S.tb[GetHandleId(u)]
else
local integer this = LoadInteger(S.hash, GetHandleId(u), 0)
endif
local integer head
local integer cHead
//If the unit is a legit Dummy Unit
if this > 0 and next[this] == -1 then
//Find where to insert based on the list having the least number of units
set head = countNext[countHead[lower]]
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
//Update Status
call SetUnitFacing(u, angle[head])
call PauseUnit(u, true)
call SetUnitOwner(u, OWNER, false)
static if HIDE_ON_MAP_CORNER then
static if LIBRARY_WorldBounds then
call SetUnitX(u, WorldBounds.maxX)
call SetUnitY(u, WorldBounds.maxY)
else
call SetUnitX(u, Bounds.x)
call SetUnitY(u, Bounds.y)
endif
else
call SetUnitScale(u, 0, 0, 0)
call SetUnitVertexColor(u, 0, 0, 0, 0)
endif
set count[head] = count[head] + 1
//---------------------------
// Update Count Lists
//---------------------------
//Remove
set countNext[countPrev[head]] = countNext[head]
set countPrev[countNext[head]] = countPrev[head]
//Add to the new Count List
set cHead = countHead[count[head]]
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
//---------------------------
// Update Bounds
//---------------------------
set cHead = countHead[lower]
if countNext[cHead] == cHead then
set lower = lower + 1
endif
if count[head] > upper then
set upper = count[head]
endif
elseif this == 0 then
call RemoveUnit(u)
debug elseif next[this] != -1 then
debug call BJDebugMsg("|cffffcc00[DummyRecycler]:|r Attempted to recycle a pending/free Dummy Unit.")
endif
endfunction
private function Expires takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
static if LIBRARY_Table then
call RecycleDummy(S.tb.unit[id])
call S.tb.unit.remove(id)
else
call RecycleDummy(LoadUnitHandle(S.hash, id, 0))
call FlushChildHashtable(S.hash, id)
endif
call DestroyTimer(t)
set t = null
endfunction
function DummyAddRecycleTimer takes unit u, real time returns nothing
local timer t = CreateTimer()
static if LIBRARY_Table then
set S.tb.unit[GetHandleId(t)] = u
else
call SaveUnitHandle(S.hash, GetHandleId(t), 0, u)
endif
call TimerStart(t, time, false, function Expires)
set t = null
endfunction
function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
return GetRecycledDummy(x, y, z, angle[countNext[countHead[upper]]])
endfunction
// runtextmacro DUMMY_DEBUG_TOOLS()
endlibrary
/*
To automatically create a Dummy Unit in Object Editor, enable this trigger (Dummy Object Merger)
and configure it if you want.
Then save the map, it should take time in "Executing external commands"
Close the map, then re-open it. You should be able to see "Dummy (General Purpose)" in Object Editor.
After that, delete/disable this trigger (Dummy Object Merger).
*/
//! externalblock extension=lua ObjectMerger $FILENAME$
//Configuration
//! i local dummyRawcode = "dumi"
//! i local dummyModel = "war3mapImported\\dummy.mdl"
//End Configuration
//Create new units
//! i setobjecttype("units")
//! i createobject("ewsp",dummyRawcode)
//! i makechange(current,"uabi","Aloc,Arav")
//! i makechange(current,"ucbs","0.0")
//! i makechange(current,"ushr","0")
//! i makechange(current,"uico","ReplaceableTextures\\CommandButtons\\BTNSelectHeroOn.blp")
//! i makechange(current,"umxp","0.0")
//! i makechange(current,"umxr","0.0")
//! i makechange(current,"umdl",dummyModel)
//! i makechange(current,"uspa","")
//! i makechange(current,"umvr","3")
//! i makechange(current,"umvt","")
//! i makechange(current,"ufoo","0")
//! i makechange(current,"urac","creeps")
//! i makechange(current,"usid","0")
//! i makechange(current,"usin","0")
//! i makechange(current,"utyp","")
//! i makechange(current,"ubui","")
//! i makechange(current,"upgr","")
//! i makechange(current,"unam","Dummy")
//! i makechange(current,"unsf"," (General Purpose)")
//! endexternalblock
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.
One map, one hashtable. Welcome to NewTable 4.1.0.1
This newest iteration of Table introduces the new HashTable struct.
You can now instantiate HashTables which enables the use of large
parent and large child keys, just like a standard hashtable. Previously,
the user would have to instantiate a Table to do this on their own which -
while doable - is something the user should not have to do if I can add it
to this resource myself (especially if they are inexperienced).
This library was originally called NewTable so it didn't conflict with
the API of Table by Vexorian. However, the damage is done and it's too
late to change the library name now. To help with damage control, I
have provided an extension library called TableBC, which bridges all
the functionality of Vexorian's Table except for 2-D string arrays &
the ".flush(integer)" method. I use ".flush()" to flush a child hash-
table, because I wanted the API in NewTable to reflect the API of real
hashtables (I thought this would be more intuitive).
API
------------
struct Table
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush all stored values inside of it
|
| method remove takes integer key returns nothing
| remove the value at index "key"
|
| method operator []= takes integer key, $TYPE$ value returns nothing
| assign "value" to index "key"
|
| method operator [] takes integer key returns $TYPE$
| load the value at index "key"
|
| method has takes integer key returns boolean
| whether or not the key was assigned
|
----------------
struct TableArray
| static method operator [] takes integer array_size returns TableArray
| create a new array of Tables of size "array_size"
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush and destroy it
|
| method operator size takes nothing returns integer
| returns the size of the TableArray
|
| method operator [] takes integer key returns Table
| returns a Table accessible exclusively to index "key"
*/
globals
private integer less = 0 //Index generation for TableArrays (below 0).
private integer more = 8190 //Index generation for Tables.
//Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
private hashtable ht = InitHashtable()
private key sizeK
private key listK
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return sizeK
endmethod
static method operator list takes nothing returns Table
return listK
endmethod
endstruct
private struct handles extends array
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private struct agents extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(ht, this, key, value)
endmethod
endstruct
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSaved$SUPER$(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
struct Table extends array
// Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
implement realm
implement integerm
implement booleanm
implement stringm
implement playerm
implement widgetm
implement destructablem
implement itemm
implement unitm
implement abilitym
implement timerm
implement triggerm
implement triggerconditionm
implement triggeractionm
implement eventm
implement forcem
implement groupm
implement locationm
implement rectm
implement boolexprm
implement soundm
implement effectm
implement unitpoolm
implement itempoolm
implement questm
implement questitemm
implement defeatconditionm
implement timerdialogm
implement leaderboardm
implement multiboardm
implement multiboarditemm
implement trackablem
implement dialogm
implement buttonm
implement texttagm
implement lightningm
implement imagem
implement ubersplatm
implement regionm
implement fogstatem
implement fogmodifierm
implement hashtablem
method operator handle takes nothing returns handles
return this
endmethod
method operator agent takes nothing returns agents
return this
endmethod
//set this = tb[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key) //return this.integer[key]
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb) //set this.integer[key] = tb
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key) //return this.integer.has(key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key) //call this.integer.remove(key)
endmethod
//Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
//local Table tb = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set this = more + 1
set more = this
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this) //Clear hashed memory
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call tb.destroy()
//
method destroy takes nothing returns nothing
debug if dex.list[this] != -1 then
debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
debug return
debug endif
call this.flush()
set dex.list[this] = dex.list[0]
set dex.list[0] = this
endmethod
//! runtextmacro optional TABLE_BC_METHODS()
endstruct
//! runtextmacro optional TABLE_BC_STRUCTS()
struct TableArray extends array
//Returns a new TableArray to do your bidding. Simply use:
//
// local TableArray ta = TableArray[array_size]
//
static method operator [] takes integer array_size returns TableArray
local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = tb[0] //The last-destroyed TableArray that had this array size
debug if array_size <= 0 then
debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
debug return 0
debug endif
if this == 0 then
set this = less - array_size
set less = this
else
set tb[0] = tb[this] //Set the last destroyed to the last-last destroyed
call tb.remove(this) //Clear hashed memory
endif
set dex.size[this] = array_size //This remembers the array size
return this
endmethod
//Returns the size of the TableArray
method operator size takes nothing returns integer
return dex.size[this]
endmethod
//This magic method enables two-dimensional[array][syntax] for Tables,
//similar to the two-dimensional utility provided by hashtables them-
//selves.
//
//ta[integer a].unit[integer b] = unit u
//ta[integer a][integer c] = integer d
//
//Inline-friendly when not running in debug mode
//
method operator [] takes integer key returns Table
static if DEBUG_MODE then
local integer i = this.size
if i == 0 then
call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
return 0
elseif key < 0 or key >= i then
call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
return 0
endif
endif
return this + key
endmethod
//Destroys a TableArray without flushing it; I assume you call .flush()
//if you want it flushed too. This is a public method so that you don't
//have to loop through all TableArray indices to flush them if you don't
//need to (ie. if you were flushing all child-keys as you used them).
//
method destroy takes nothing returns nothing
local Table tb = dex.size[this.size]
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
debug return
debug endif
if tb == 0 then
//Create a Table to index recycled instances with their array size
set tb = Table.create()
set dex.size[this.size] = tb
endif
call dex.size.remove(this) //Clear the array size from hash memory
set tb[this] = tb[0]
set tb[0] = this
endmethod
private static Table tempTable
private static integer tempEnd
//Avoids hitting the op limit
private static method clean takes nothing returns nothing
local Table tb = .tempTable
local integer end = tb + 0x1000
if end < .tempEnd then
set .tempTable = end
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
else
set end = .tempEnd
endif
loop
call tb.flush()
set tb = tb + 1
exitwhen tb == end
endloop
endmethod
//Flushes the TableArray and also destroys it. Doesn't get any more
//similar to the FlushParentHashtable native than this.
//
method flush takes nothing returns nothing
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
set .tempTable = this
set .tempEnd = this + this.size
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
call this.destroy()
endmethod
endstruct
//NEW: Added in Table 4.0. A fairly simple struct but allows you to do more
//than that which was previously possible.
struct HashTable extends array
//Enables myHash[parentKey][childKey] syntax.
//Basically, it creates a Table in the place of the parent key if
//it didn't already get created earlier.
method operator [] takes integer index returns Table
local Table t = Table(this)[index]
if t == 0 then
set t = Table.create()
set Table(this)[index] = t //whoops! Forgot that line. I'm out of practice!
endif
return t
endmethod
//You need to call this on each parent key that you used if you
//intend to destroy the HashTable or simply no longer need that key.
method remove takes integer index returns nothing
local Table t = Table(this)[index]
if t != 0 then
call t.destroy()
call Table(this).remove(index)
endif
endmethod
//Added in version 4.1
method has takes integer index returns boolean
return Table(this).has(index)
endmethod
//HashTables are just fancy Table indices.
method destroy takes nothing returns nothing
call Table(this).destroy()
endmethod
//Like I said above...
static method create takes nothing returns thistype
return Table.create()
endmethod
endstruct
endlibrary
library WorldBounds /* v2.0.0.0
************************************************************************************
*
* struct WorldBounds extends array
*
* Fields
* -------------------------
*
* readonly static integer maxX
* readonly static integer maxY
* readonly static integer minX
* readonly static integer minY
*
* readonly static integer centerX
* readonly static integer centerY
*
* readonly static rect world
* readonly static region worldRegion
*
************************************************************************************/
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world = GetWorldBounds()
set maxX = R2I(GetRectMaxX(world))
set maxY = R2I(GetRectMaxY(world))
set minX = R2I(GetRectMinX(world))
set minY = R2I(GetRectMinY(world))
set centerX = R2I((maxX + minX)/2)
set centerY = R2I((minY + maxY)/2)
set playMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
set playMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
set playMinX = GetRectMinX(bj_mapInitialPlayableArea)
set playMinY = GetRectMinY(bj_mapInitialPlayableArea)
set worldRegion = CreateRegion()
call RegionAddRect(worldRegion, world)
endmethod
endmodule
struct WorldBounds extends array
readonly static integer maxX
readonly static integer maxY
readonly static integer minX
readonly static integer minY
readonly static integer centerX
readonly static integer centerY
readonly static rect world
readonly static region worldRegion
readonly static real playMaxX
readonly static real playMaxY
readonly static real playMinX
readonly static real playMinY
implement WorldBoundInit
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
static if LIBRARY_DummyRecycler then
//! textmacro DUMMY_DEBUG_TOOLS
//insert using //! runtextmacro DUMMY_DEBUG_TOOLS()
function DisplayDummyList takes integer count returns nothing
local integer head = countHead[count]
local integer this = countNext[head]
loop
exitwhen this == head
call BJDebugMsg("Head = " + I2S(this) + " (Angle: " + R2S(angle[this]) + ")")
set this = countNext[this]
endloop
endfunction
function DisplayAllList takes nothing returns nothing
local integer i = 0
call BJDebugMsg("|cffffcc00-------------------------------|r")
loop
exitwhen i > R2I(MAX_DUMMY_COUNT/ANGLES_COUNT + 0.5)
call BJDebugMsg("|cffffcc00About to Display Lists at count = " + I2S(i) + "|r")
call DisplayDummyList(i)
set i = i + 1
endloop
endfunction
//! endtextmacro
else
//insert using //! runtextmacro DUMMY_DEBUG_TOOLS_JASS()
//! textmacro DUMMY_DEBUG_TOOLS_JASS
//insert using //! runtextmacro DUMMY_DEBUGTOOLS()
function DisplayDummyList takes integer count returns nothing
local integer head = udg_Dummy_CountHead[count]
local integer this = udg_Dummy_CountNext[head]
loop
exitwhen this == head
call BJDebugMsg("Head = " + I2S(this) + " (Angle: " + R2S(udg_Dummy_Angle[this]) + ")")
set this = udg_Dummy_CountNext[this]
endloop
endfunction
function DisplayAllList takes nothing returns nothing
local integer i = 0
call BJDebugMsg("|cffffcc00-------------------------------|r")
loop
exitwhen i > R2I(Dummy_MaxCount()/Dummy_Angles() + 0.5)
call BJDebugMsg("|cffffcc00About to Display Lists at count = " + I2S(i) + "|r")
call DisplayDummyList(i)
set i = i + 1
endloop
endfunction
//! endtextmacro
endif
function Trig_Demo_Any_Angle_Actions takes nothing returns nothing
local string chat = GetEventPlayerChatString()
local real angle = S2R(SubString(chat, 1, StringLength(chat)))
local unit u = GetRecycledDummy(100, 100, 100, angle)
local effect e = AddSpecialEffectTarget("abilities\\weapons\\WyvernSpear\\WyvernSpearMissile.mdl", u, "origin")
call BJDebugMsg("[JASS] Retrieved a Dummy with Attached Effect where facing = " + R2S(angle))
call SetUnitScale(u, 2, 0, 0)
call TriggerSleepAction(3.00)
call DestroyEffect(e)
call RecycleDummy(u)
set u = null
set e = null
endfunction
//===========================================================================
function InitTrig_Demo_Any_Angle takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, Player(0), "=", false)
call TriggerAddAction(t, function Trig_Demo_Any_Angle_Actions )
call BJDebugMsg("Type '|cffffcc00=???|r' where ??? is the facing angle of the recycled unit")
call FogMaskEnable(false)
call FogEnable(false)
endfunction
function Trig_Demo_All_Angles_Actions takes nothing returns nothing
local string chat = GetEventPlayerChatString()
local real difference = S2R(SubString(chat, 3, StringLength(chat)))
local real angle = 60
loop
exitwhen angle >= 360
call DummyAddRecycleTimer(GetRecycledDummy(0, 0, 0, angle), 2)
set angle = angle + difference
endloop
endfunction
//===========================================================================
function InitTrig_Demo_All_Angles takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, Player(0), "all", false)
call TriggerAddAction(t, function Trig_Demo_All_Angles_Actions )
endfunction