- Joined
- Sep 12, 2008
- Messages
- 657
hey.. i found a code i did some time ago.. before i quitted wc,
i wondered if theres a way making this more..efficet..
since im pretty sure this is quite bad
And more to the problems.
this is 10% mui ^^
i have no clue why the other 90% fails hard..
but it wont allow to take over 2 places at same time (2 diffrent people),
and when the percents reach 0, it wont destroy the texttag.
Thx in advance =]
i wondered if theres a way making this more..efficet..
since im pretty sure this is quite bad
JASS:
library NodeAndMiningSystem initializer init requires Table, ClosestUnit, AutoIndex
globals
private constant integer CapturingUnitRaw = 'hpea'
private constant integer CaptureSpeed = 1
private constant integer DecaptureSpeed = 1
private constant integer maxUnitsPerNode = 3
private constant integer ItemTypeLimitPerNode = 50
private constant real CapturingNodeRange = 250
private Table table // 0, 1, 2, 3
endglobals
public struct NodeAndMiningData
public integer itemCount
public integer unitRawCode
public integer mineRawCode
public integer nodeRawCode
public integer array itemRawCode[ItemTypeLimitPerNode]
public integer array percents[ItemTypeLimitPerNode]
public method addItem takes integer rawCode, integer percentsToGetItem returns nothing
if itemCount < ItemTypeLimitPerNode then
set itemCount = itemCount + 1
set itemRawCode[itemCount] = rawCode
set percents[itemCount] = percentsToGetItem
else
call BJDebugMsg("You have surpassed the itemType limit!")
endif
endmethod
public method addWorker takes integer rawCode returns nothing
set unitRawCode = rawCode
endmethod
public method addNode takes integer rawCode returns nothing
set nodeRawCode = rawCode
endmethod
public method addMine takes integer rawCode returns nothing
set mineRawCode = rawCode
endmethod
endstruct
globals
public NodeAndMiningData array codeData
public group mine_dummyGroup
public group node_dummyGroup
public group mine_allMapGroup
public group node_allMapGroup
public group capturingUnitsGroup
public unit array closestUnit
public Table percentsTable
public Table array unitCaptureCount
public texttag array textTag
private unit loopUnitA = null
private unit loopUnitB = null
endglobals
public function createNewData takes nothing returns nothing
set table[0] = table[0] + 1
endfunction
private function getNodesBoolexpr takes nothing returns boolean
return IsUnitInGroup(GetFilterUnit(), node_allMapGroup)
endfunction
private function DistanceBetweenPointsXY takes real x1, real y1, real x2, real y2 returns real
return SquareRoot((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))
endfunction
private function boolexprCheck takes nothing returns boolean
return GetUnitTypeId(GetFilterUnit()) == CapturingUnitRaw and /*
*/ DistanceBetweenPointsXY(GetUnitX(GetFilterUnit()), GetUnitY(GetFilterUnit()), /*
*/ GetUnitX(loopUnitB), GetUnitY(loopUnitB)) <= CapturingNodeRange
endfunction
public function createTextTag takes unit target, string str returns texttag
local texttag t = CreateTextTag()
call SetTextTagText(t, str, TextTagSize2Height(10))
call SetTextTagPosUnit(t, target, 0)
call SetTextTagColor(t, PercentTo255(0), PercentTo255(100), PercentTo255(0), PercentTo255(100.0))
call SetTextTagPermanent(t, true)
return t
endfunction
public function handleActionsNode takes nothing returns nothing
local unit u = null
local unit b = null
local boolean MoreThenOnePlayer = false
local integer i = 0
set capturingUnitsGroup = GetUnitsOfTypeIdAll(CapturingUnitRaw)
loop
set u = FirstOfGroup(capturingUnitsGroup)
set loopUnitA = u
exitwhen u == null
call GroupEnumUnitsInRange(node_dummyGroup, GetUnitX(u), GetUnitY(u), CapturingNodeRange, function getNodesBoolexpr)
loop
set b = FirstOfGroup(node_dummyGroup)
set loopUnitB = b
exitwhen b == null
if percentsTable[GetUnitId(b)] >= 100 then
if percentsTable[GetUnitId(b)] >= 100 then
call SetUnitOwner(b, GetOwningPlayer(u), true)
call SetUnitOwner(closestUnit[GetUnitId(b)], GetOwningPlayer(u), true)
set percentsTable[GetUnitId(b)] = 0
call DestroyTextTag(textTag[GetUnitId(b)])
endif
else
if percentsTable[GetUnitId(b)] <= 0 then
set textTag[GetUnitId(b)] = createTextTag(b, "")
endif
//Finding units of capture raw type, and owned by that player with specific range from node. ================== *
call GroupEnumUnitsOfPlayer(mine_dummyGroup, GetOwningPlayer(u), function boolexprCheck) // *
set unitCaptureCount[GetPlayerId(GetOwningPlayer(u))][GetUnitId(b)] = CountUnitsInGroup(mine_dummyGroup) // *
if unitCaptureCount[GetPlayerId(GetOwningPlayer(u))][GetUnitId(b)] > maxUnitsPerNode then // *
set unitCaptureCount[GetPlayerId(GetOwningPlayer(u))][GetUnitId(b)] = maxUnitsPerNode // *
endif // *
//============================================================================================================= *
set table[3] = 0
loop
set table[3] = table[3] + 1
if unitCaptureCount[table[3]][GetUnitId(b)] > 0 then
set i = i + 1
endif
if i > 1 then
set MoreThenOnePlayer = true
endif
exitwhen table[3] == 12 or MoreThenOnePlayer == true
endloop
if MoreThenOnePlayer == true then
if (percentsTable[GetUnitId(b)] > 0) then
set percentsTable[GetUnitId(b)] = percentsTable[GetUnitId(b)] - DecaptureSpeed
else
call DestroyTextTag(textTag[GetUnitId(b)])
endif
else
if IsUnitOwnedByPlayer(b, GetOwningPlayer(u)) == false then
set percentsTable[GetUnitId(b)] = percentsTable[GetUnitId(b)] + (CaptureSpeed * unitCaptureCount[GetPlayerId(GetOwningPlayer(u))][GetUnitId(b)])
else
if (percentsTable[GetUnitId(b)] > 0) then
set percentsTable[GetUnitId(b)] = percentsTable[GetUnitId(b)] - (DecaptureSpeed * unitCaptureCount[GetPlayerId(GetOwningPlayer(u))][GetUnitId(b)])
else
call DestroyTextTag(textTag[GetUnitId(b)])
endif
endif
endif
call SetTextTagText(textTag[GetUnitId(b)], I2S(percentsTable[GetUnitId(b)]) , TextTagSize2Height(10))
endif
call GroupRemoveUnit(node_dummyGroup, b)
endloop
call GroupRemoveUnit(capturingUnitsGroup, u)
endloop
endfunction
public function arrangeItemList takes nothing returns nothing
endfunction
public function forceCapture takes unit node, player owner, boolean resetPercent returns nothing
call SetUnitOwner(node, owner, true)
call SetUnitOwner(closestUnit[GetUnitId(node)], owner, true)
if resetPercent then
set percentsTable[GetUnitId(node)] = 0
call DestroyTextTag(textTag[GetUnitId(node)])
endif
endfunction
public function AddNode takes unit node returns nothing
set table[2] = 0
loop
set table[2] = table[2] + 1
set node_dummyGroup = GetUnitsOfTypeIdAll(codeData[table[2]].nodeRawCode)
if IsUnitInGroup(node, node_dummyGroup) then
set mine_dummyGroup = GetUnitsOfTypeIdAll(codeData[table[2]].mineRawCode)
set closestUnit[GetUnitId(node)] = GetClosestUnit(mine_dummyGroup, GetUnitX(node), GetUnitY(node))
set table[2] = table[0]
endif
exitwhen table[2] == table[0]
endloop
endfunction
public function loadGroups takes nothing returns nothing
local unit u = null
set table[1] = 0
loop
set table[1] = table[1] + 1
set node_dummyGroup = GetUnitsOfTypeIdAll(codeData[table[1]].nodeRawCode)
set mine_dummyGroup = GetUnitsOfTypeIdAll(codeData[table[1]].mineRawCode)
call GroupAddGroup(node_dummyGroup, node_allMapGroup)
call GroupAddGroup(mine_dummyGroup, mine_allMapGroup)
loop
set u = FirstOfGroup(node_dummyGroup)
exitwhen u == null
set closestUnit[GetUnitId(u)] = GetClosestUnit(mine_dummyGroup, GetUnitX(u), GetUnitY(u))
call GroupRemoveUnit(node_dummyGroup, u)
endloop
exitwhen table[1] == table[0]
endloop
endfunction
public function init takes nothing returns nothing
set table = Table.create()
set percentsTable = Table.create()
set capturingUnitsGroup = CreateGroup()
set mine_dummyGroup = CreateGroup()
set node_dummyGroup = CreateGroup()
set node_allMapGroup = CreateGroup()
set mine_allMapGroup = CreateGroup()
set unitCaptureCount[0] = Table.create()
set unitCaptureCount[1] = Table.create()
set unitCaptureCount[2] = Table.create()
set unitCaptureCount[3] = Table.create()
set unitCaptureCount[4] = Table.create()
set unitCaptureCount[5] = Table.create()
set unitCaptureCount[6] = Table.create()
set unitCaptureCount[7] = Table.create()
set unitCaptureCount[8] = Table.create()
set unitCaptureCount[9] = Table.create()
set unitCaptureCount[10] = Table.create()
set unitCaptureCount[11] = Table.create()
set unitCaptureCount[12] = Table.create()
call TimerStart(CreateTimer(), 0.5, true, function handleActionsNode)
//=============================================================================== *
// Edit Here
//=============================================================================== *
call createNewData()
call codeData[table[0]].addWorker('unit')
call codeData[table[0]].addMine('mine')
call codeData[table[0]].addNode('node')
call codeData[table[0]].addItem('item', 100)
call arrangeItemList() // i used this line to show you how to use it,
// but since i made only 1 type of an item, that line is pretty much useless, dont use it on
// places you use only 1 type of item.
//================================================================================ *
//Loads groups data into the right variables. dont touch this
call loadGroups()
endfunction
endlibrary
JASS:
library Table
//***************************************************************
//* Table object 3.0
//* ------------
//*
//* set t=Table.create() - instanceates a new table object
//* call t.destroy() - destroys it
//* t[1234567] - Get value for key 1234567
//* (zero if not assigned previously)
//* set t[12341]=32 - Assigning it.
//* call t.flush(12341) - Flushes the stored value, so it
//* doesn't use any more memory
//* t.exists(32) - Was key 32 assigned? Notice
//* that flush() unassigns values.
//* call t.reset() - Flushes the whole contents of the
//* Table.
//*
//* call t.destroy() - Does reset() and also recycles the id.
//*
//* If you use HandleTable instead of Table, it is the same
//* but it uses handles as keys, the same with StringTable.
//*
//* You can use Table on structs' onInit if the struct is
//* placed in a library that requires Table or outside a library.
//*
//* You can also do 2D array syntax if you want to touch
//* mission keys directly, however, since this is shared space
//* you may want to prefix your mission keys accordingly:
//*
//* set Table["thisstring"][ 7 ] = 2
//* set Table["thisstring"][ 5 ] = Table["thisstring"][7]
//*
//***************************************************************
//=============================================================
globals
private constant integer MAX_INSTANCES=8100 //400000
//Feel free to change max instances if necessary, it will only affect allocation
//speed which shouldn't matter that much.
//=========================================================
private hashtable ht
endglobals
private struct GTable[MAX_INSTANCES]
method reset takes nothing returns nothing
call FlushChildHashtable(ht, integer(this) )
endmethod
private method onDestroy takes nothing returns nothing
call this.reset()
endmethod
//=============================================================
// initialize it all.
//
private static method onInit takes nothing returns nothing
set ht = InitHashtable()
endmethod
endstruct
//Hey: Don't instanciate other people's textmacros that you are not supposed to, thanks.
//! textmacro Table__make takes name, type, key
struct $name$ extends GTable
method operator [] takes $type$ key returns integer
return LoadInteger(ht, integer(this), $key$)
endmethod
method operator []= takes $type$ key, integer value returns nothing
call SaveInteger(ht, integer(this) ,$key$, value)
endmethod
method flush takes $type$ key returns nothing
call RemoveSavedInteger(ht, integer(this), $key$)
endmethod
method exists takes $type$ key returns boolean
return HaveSavedInteger( ht, integer(this) ,$key$)
endmethod
static method flush2D takes string firstkey returns nothing
call $name$(- StringHash(firstkey)).reset()
endmethod
static method operator [] takes string firstkey returns $name$
return $name$(- StringHash(firstkey) )
endmethod
endstruct
//! endtextmacro
//! runtextmacro Table__make("Table","integer","key" )
//! runtextmacro Table__make("StringTable","string", "StringHash(key)" )
//! runtextmacro Table__make("HandleTable","handle","GetHandleId(key)" )
endlibrary
JASS:
library ClosestUnit
function GetClosestUnit takes group g, real x, real y returns unit
local real dx
local real dy
local group tempGroup
local real maxDist=999999.0
local real dist
local unit u = null
local unit closest = null
if (bj_wantDestroyGroup == true) then
set tempGroup = g
else
set tempGroup = CreateGroup()
call GroupAddGroup(g, tempGroup)
endif
set bj_wantDestroyGroup = false
loop
set u = FirstOfGroup(tempGroup)
call GroupRemoveUnit(tempGroup, u)
exitwhen (u == null)
set dx = GetUnitX(u) - x
set dy = GetUnitY(u) - y
set dist = SquareRoot(dx*dx+dy*dy)
if (dist < maxDist) then
set closest = u
set maxDist = dist
endif
endloop
call DestroyGroup(tempGroup)
set tempGroup = null
return closest
endfunction
endlibrary
JASS:
library AutoIndex
//===========================================================================
// Information:
//==============
//
// AutoIndex is a very simple script to utilize. Just call GetUnitId(unit) to
// get get the unique value assigned to a particular unit. AutoIndex differs from
// other unit indexing libraries because it automatically assigns an ID to each
// unit as it enters the map, and automatically frees that ID as the unit leaves
// the map. This gives you several advantages as the user:
// -The GetUnitId function inlines directly to a GetUnitUserData call (or a
// LoadInteger call if the UseUnitUserData constant is set to false.)
// -You don't need to manually free IDs as units leave the map.
// -Detecting removing units to free their indexes is O(1), and less costly
// performance-wise than a timer scanning the map for removed units.
//
// If you turn on debug mode, AutoIndex will become slower, but it will be able
// to display several helpful error messages. It can detect the following issues:
// -Passing a removed or decayed unit to GetUnitId
// -Code outside of AutoIndex has overwritten a unit's UserData value.
// -GetUnitId was used on a filtered unit (a unit you don't want indexed).
//
// AutoIndex uses UnitUserData by default. If something else in your map
// would conflict with that, you can set the UseUnitUserData configuration
// constant to false, and a hashtable will be used instead. Note that hash-
// tables are about 60% slower.
//
// AutoIndex provides events upon indexing or deindexing units. This
// effectively allows you to notice when units enter or leave the game, and
// handle the creation or destruction of attached data or other things. Also
// included is the AutoStruct module, which automatically creates and destroys
// struct instances associated with units as they enter and leave the game.
//
//===========================================================================
// How to install AutoIndex:
//===========================
//
// 1.) Copy and paste this script into your map.
// 2.) Save it to allow the ObjectMerger macro to generate the "Leave Detect"
// ability for you. Close and re-open the map. After that, disable the macro
// to prevent the save delay.
//
//===========================================================================
// How to use AutoIndex:
//=======================
//
// So you can get a unique integer for each unit, but how do you use that to
// attach data to a unit? GetUnitId will always return a number in the range of
// 1-8190. This means it can be used as an array index, as demonstrated below:
//
// globals
// integer array IntegerData
// real array RealData
// SomeStruct array SomeStructData
// englobals
//
// function Example takes nothing returns nothing
// local unit u = CreateUnit(...)
// local integer id = GetUnitId(u)
// //You now have a unique index for the unit, so you can
// //attach or retrieve data about the unit using arrays.
// set IntegerData[id] = 5
// set RealData[id] = 25.0
// set SomeStructData[id] = SomeStruct.create()
// //If you have access to the same unit in another function, you can
// //retrieve the data by using GetUnitId() and reading the arrays.
// endfunction
//
// The UnitFilter function in the configuration section is provided so that
// you can make AutoIndex completely ignore certain unit-types. Ignored units
// won't be indexed or fire indexed/deindexed events. You may want to filter out
// dummy casters or system-private units, especially ones that use UnitUserData
// internally. xe dummy units are automatically filtered.
//
//===========================================================================
// How to use OnUnitIndexed / OnUnitDeindexed:
//=============================================
//
// AutoIndex will fire the OnUnitIndexed event when a unit enters the map,
// and the OnUnitDeindexed event when a unit leaves the map. Functions used
// as events must take a unit and return nothing. An example is given below:
//
// function UnitEntersMap takes unit u returns nothing
// call BJDebugMsg(GetUnitName(u)+" was indexed with the ID "+I2S(GetUnitId(u)))
// endfunction
//
// function UnitLeavesMap takes unit u returns nothing
// call BJDebugMsg(GetUnitName(u)+" was deindexed with the ID "+I2S(GetUnitId(u)))
// endfunction
//
// function Init takes nothing returns nothing
// call OnUnitIndexed(UnitEntersMap)
// call OnUnitDeindexed(UnitLeavesMap)
// endfunction
//
// As you can see, it works perfectly fine to call GetUnitId() on a unit
// during either of these events.
//
// If you call OnUnitIndexed during map initialization, every existing
// unit will be considered as entering the map. This saves you from needing
// to manually enumerate preplaced units (or units created by initialization
// code that ran before OnUnitIndexed was called).
//
// OnUnitDeindexed runs while a unit still exists, which means you can
// still do things such as destroy special effects attached to the unit.
// The unit will cease to exist immediately after the event is over.
//
//===========================================================================
// AutoIndex API:
//================
//
// GetUnitId(unit) -> integer
// This function returns a unique ID in the range of 1-8190 for the
// specified unit. Ruturns 0 if a null unit was passed. This function
// inlines directly to GetUnitUserData or LoadInteger if debug mode
// is disabled. If debug mode is enabled, this function will print
// an error message when passed a decayed or filtered unit.
//
// OnUnitIndexed(IndexFunc)
// This function accepts an IndexFunc, which must take a unit and
// return nothing. The IndexFunc will be fired instantly whenever
// a unit enters the map. You may use GetUnitId on the unit. When
// you call this function during map initialization, every existing
// unit will be considered as entering the map.
//
// OnUnitDeindexed(IndexFunc)
// Same as above, but runs whenever a unit is leaving the map. When
// this event runs, the unit still exists, but it will cease to exist
// as soon as the event ends. You may use GetUnitId on the unit.
//
//===========================================================================
// How to use the AutoStruct module:
//===================================
//
// The AutoStruct module allows you to automatically create and destroy
// struct instances as units enter and leave the game. An instance of the
// implementing struct will be created each time a unit enters the game,
// and destroyed when that unit leaves the game. (You cannot create or dest-
// roy instances manually.) This means that you should consider each inst-
// ance of an AutoStruct to be "owned by" a specific unit.
//
// AutoStruct restrictions:
// -You may not implement AutoStruct in any struct that declares its
// own create, destroy, or operator[] methods.
//
// -You may not manually create or destroy structs implementing AutoStruct.
// Creation and destruction are handled automatically as units enter/leave.
//
// -AutoStruct may be implemented in structs that extend interfaces or
// other structs, but only if the allocate() method takes no parameters.
// (The restriction of allocate() taking no parameters may be changed soon.)
//
// -AutoStruct will not work with structs that extend array, since they
// can't be created or destroyed.
//
// AutoStruct features:
// -An instance of the implementing struct will be created each time a
// unit enters the game. That instance will be destroyed when that unit
// leaves the game. The struct will always exist while the unit does.
//
// -You can retrieve an AutoStruct from a unit by using the syntax:
// local StructName mystruct = StructName[unit]
// //This inlines to a GetUnitId() call + array lookup.
//
// -You can refer to the unit that owns the instance by the member "me":
// //Outside of the struct: call BJDebugMsg(GetUnitName(mystruct.me))
// //From within the struct: call BJDebugMsg(GetUnitName(me))
//
// -You can use the optional methods onCreate and onDestroy to notice when
// instances of a struct implementing AutoStruct are created and destroyed.
// This is equivalent to detecting a unit entering and leaving the game.
// struct Example
// private method onCreate takes nothing returns nothing
// call BJDebugMsg(GetUnitName(me)+" has entered the game!")
// endmethod
// private method onDestroy takes nothing returns nothing
// call BJDebugMsg(GetUnitName(me)+" has left the game!")
// endmethod
// implement AutoStruct
// endstruct
//
// -You can filter which units will recieve an AutoStruct by using the
// optional static method createFilter. The createFiler method must take
// a unit parameter and return a boolean (true if created, false if not).
// struct Example
// private static method createFiler takes unit u returns boolean
// return GetUnitTypeId(u) == 'hfoo' //Only Footmen will recieve this AutoStruct.
// endmethod
// implement AutoStruct
// endstruct
//
//===========================================================================
// Configuration:
//================
// external ObjectMerger w3a Adef lvdt anam "Leave Detect" aart "" arac 0
//Save your map with this Object Merger call enabled, then close and reopen your
//map. Disable it by removing the exclamation to remove the delay while saving.
globals
private constant integer LeaveDetectAbilityID = 'lvdt'
//This rawcode must match the parameter after "Adef" in the
//ObjectMergermacro above. You can change both if you want.
private constant boolean UseUnitUserData = true
//If this is set to true, UnitUserData will be used. You should only set
//this to false if something else in your map already uses UnitUserData.
//A hashtable will be used instead, but it is about 60% slower.
endglobals
private function UnitFilter takes unit u returns boolean
return true
endfunction
//Make this function return false for any unit-types you wish to be ignored.
//Ignored units won't be indexed or fire OnUnitIndexed/OnUnitDeindexed
//events. Use the unit u parameter to refer to the unit being filtered.
//You do not need to filter out xe dummy units; they are already filtered.
//===========================================================================
// AutoStruct module:
//====================
function interface AutoStructCreator takes unit u returns integer
function interface AutoStructDestroyer takes unit u returns nothing
module AutoStruct
private static thistype array data
unit me //The unit that "owns" this struct instance is referred to as "me".
static method operator [] takes unit u returns thistype
return data[GetUnitId(u)] //Return the struct instance associated with the unit.
endmethod
private static method create takes unit u returns thistype
local thistype this
static if thistype.createFilter.exists then //If the createFiler exists...
if not createFilter(u) then //If the unit fails the createFilter...
return 0 //Don't allocate a struct for this unit.
endif
endif
set this = allocate() //Allocate the struct.
set me = u //Assign the "me" unit of this struct to the entering unit.
set data[GetUnitId(u)] = this //Attach this instance to the unit.
static if thistype.onCreate.exists then //If onCreate exists...
call onCreate() //Call the onCreate() method for this struct.
endif
return this
endmethod
//create is private; the user doesn't create AutoStructs. They are created
//automatically when a unit enters the game and passes the createFilter.
private method destroy takes nothing returns nothing
endmethod
//destroy is private; the user doesn't destroy AutoStructs. They are destroyed
//automatically when their corresponding units leave the game.
private static method destroyer takes unit u returns nothing
local thistype this = thistype[u] //Get the instance of the struct for this unit.
if this != 0 then //If it has an instance...
call deallocate() //Deallocate it, calling onDestroy.
set .data[GetUnitId(me)] = 0 //Null the unit's associated struct instance.
set me = null //Null the unit reference.
endif
endmethod
private static method onInit takes nothing returns nothing
call AutoIndex.addAutoStruct(thistype.create, thistype.destroyer)
//Pass pointers to the create and destroyer functions to AutoIndex, so
//that it can create/destroy instances as units are indexed/deindexed.
endmethod
endmodule
//===========================================================================
// AutoIndex struct:
//===================
hook RemoveUnit AutoIndex.hook_RemoveUnit
hook ReplaceUnitBJ AutoIndex.hook_ReplaceUnitBJ
debug hook SetUnitUserData AutoIndex.hook_SetUnitUserData
struct AutoIndex
private static trigger enter = CreateTrigger()
private static trigger order = CreateTrigger()
private static trigger creepdeath = CreateTrigger()
private static group preplaced = CreateGroup()
private static timer allowdecay = CreateTimer()
private static hashtable ht
private static boolean array dead
private static boolean array summoned
private static boolean array animated
private static boolean array nodecay
private static boolean array removing
private static IndexFunc array indexfuncs
private static integer indexfuncs_n = -1
private static IndexFunc array deindexfuncs
private static integer deindexfuncs_n = -1
private static IndexFunc indexfunc
private static AutoStructCreator array creators
private static AutoStructDestroyer array destroyers
private static integer autostructs_n = -1
private static unit array allowdecayunit
private static integer allowdecay_n = -1
private static boolean duringinit = true
private static boolean array altered
private static unit array idunit
//===========================================================================
static method getIndex takes unit u returns integer
static if UseUnitUserData then
return GetUnitUserData(u)
else
return LoadInteger(ht, 0, GetHandleId(u))
endif
endmethod
//Resolves to an inlinable one-liner after the static if.
static method getIndexDebug takes unit u returns integer
if GetUnitTypeId(u) == 0 then
call BJDebugMsg("AutoIndex error: Removed or decayed unit passed to GetUnitId.")
elseif idunit[getIndex(u)] != u and GetIssuedOrderId() != 852056 then
call BJDebugMsg("AutoIndex error: "+GetUnitName(u)+" is a filtered unit.")
endif
return getIndex(u)
endmethod
//If debug mode is enabled, use the getIndex method that shows errors.
static method setIndex takes unit u, integer index returns nothing
static if UseUnitUserData then
call SetUnitUserData(u, index)
else
call SaveInteger(ht, 0, GetHandleId(u), index)
endif
endmethod
//Resolves to an inlinable one-liner after the static if.
static method isUnitAnimateDead takes unit u returns boolean
return animated[getIndex(u)]
endmethod
//Don't use this; use IsUnitAnimateDead from AutoEvents instead.
//===========================================================================
private static method onUnitIndexed_sub takes nothing returns nothing
call indexfunc.evaluate(GetEnumUnit())
endmethod
static method onUnitIndexed takes IndexFunc func returns nothing
set indexfuncs_n = indexfuncs_n + 1
set indexfuncs[indexfuncs_n] = func
if duringinit then
set indexfunc = func
//During initialization, evaluate the indexfunc for every preplaced unit.
call ForGroup(preplaced, function AutoIndex.onUnitIndexed_sub)
endif
endmethod
static method onUnitDeindexed takes IndexFunc func returns nothing
set deindexfuncs_n = deindexfuncs_n + 1
set deindexfuncs[deindexfuncs_n] = func
endmethod
static method addAutoStruct takes AutoStructCreator creator, AutoStructDestroyer destroyer returns nothing
set autostructs_n = autostructs_n + 1
set creators[autostructs_n] = creator
set destroyers[autostructs_n] = destroyer
endmethod
//===========================================================================
private static method hook_RemoveUnit takes unit whichUnit returns nothing
set removing[getIndex(whichUnit)] = true
endmethod
private static method hook_ReplaceUnitBJ takes unit whichUnit, integer newUnitId, integer unitStateMethod returns nothing
set removing[getIndex(whichUnit)] = true
endmethod
//Intercepts whenever RemoveUnit or ReplaceUnitBJ is called and sets a flag.
private static method hook_SetUnitUserData takes unit whichUnit, integer data returns nothing
static if UseUnitUserData then
if idunit[getIndex(whichUnit)] == whichUnit then
if getIndex(whichUnit) == data then
call BJDebugMsg("AutoIndex error: Code outside AutoIndex attempted to alter "+GetUnitName(whichUnit)+"'s index.")
else
call BJDebugMsg("AutoIndex error: Code outside AutoIndex altered "+GetUnitName(whichUnit)+"'s index.")
if idunit[data] != null then
call BJDebugMsg("AutoIndex error: "+GetUnitName(whichUnit)+" and "+GetUnitName(idunit[data])+" now have the same index.")
endif
set altered[data] = true
endif
endif
endif
endmethod
//In debug mode, intercepts whenever SetUnitUserData is used on an indexed unit.
//Displays an error message if outside code tries to alter a unit's index.
//===========================================================================
private static method allowDecay takes nothing returns nothing
local integer n = allowdecay_n
loop
exitwhen n < 0
set nodecay[getIndex(allowdecayunit[n])] = false
set allowdecayunit[n] = null
set n = n - 1
endloop
set allowdecay_n = -1
endmethod
//Iterate through all the units in the stack and allow them to decay again.
private static method detectStatus takes nothing returns boolean
local unit u = GetTriggerUnit()
local integer index = getIndex(u)
local integer n
if idunit[index] == u then //Ignore non-indexed units.
if not IsUnitType(u, UNIT_TYPE_DEAD) then
if dead[index] then //The unit was dead, but now it's alive.
set dead[index] = false //The unit has been resurrected.
//! runtextmacro optional RunAutoEvent("Resurrect")
//If AutoEvents is in the map, run the resurrection events.
if IsUnitType(u, UNIT_TYPE_SUMMONED) and not summoned[index] then
set summoned[index] = true //If the unit gained the summoned flag,
set animated[index] = true //it's been raised with Animate Dead.
//! runtextmacro optional RunAutoEvent("AnimateDead")
//If AutoEvents is in the map, run the Animate Dead events.
endif
endif
else
if not removing[index] and not dead[index] and not animated[index] then
set dead[index] = true //The unit was alive, but now it's dead.
set nodecay[index] = true //A dead unit can't decay for at least 0. seconds.
set allowdecay_n = allowdecay_n + 1 //Add the unit to a stack. After the timer
set allowdecayunit[allowdecay_n] = u //expires, allow the unit to decay again.
call TimerStart(allowdecay, 0., false, function AutoIndex.allowDecay)
//! runtextmacro optional RunAutoEvent("Death")
//If AutoEvents is in the map, run the Death events.
//! runtextmacro optional TransportUnload()
//If TransportEvents is in the map, remove the dead unit from whatever transport it's in.
elseif removing[index] or (dead[index] and not nodecay[index]) or (not dead[index] and animated[index]) then
//If .nodecay was false and the unit is dead and was previously dead, the unit decayed.
//If .animated was true and the unit is dead, the unit died and exploded.
//If .removing was true, the unit is being removed or replaced.
//! runtextmacro optional TransportUnload()
//If TransportEvents is in the map, remove the leaving unit from whatever transport it's in.
set n = deindexfuncs_n
loop //Run the OnUnitDeindexed events.
exitwhen n < 0
call deindexfuncs[n].evaluate(u)
set n = n - 1
endloop
//! runtextmacro optional DestroyUnitLists()
//If UnitListModule is in the map, destroy all of the UnitLists associated with the leaving unit.
set n = autostructs_n
loop //Destroy AutoStructs for the leaving unit.
exitwhen n < 0
call destroyers[n].evaluate(u)
set n = n - 1
endloop
//! runtextmacro optional TransportClean()
//If TransportEvents is in the map, and the leaving unit is a
//transport, clean the transport- related data from the unit.
call AutoIndex(index).destroy() //Free the index by destroying the AutoIndex struct.
set idunit[index] = null //Null this unit reference to prevent a leak.
endif
endif
endif
set u = null
return false
endmethod
//===========================================================================
private static method unitEntersMap takes unit u returns nothing
local integer index
local integer n = 0
if getIndex(u) != 0 then
return //Don't index a unit that already has an ID.
endif
static if LIBRARY_xebasic then
if GetUnitTypeId(u) == XE_DUMMY_UNITID then
return //Don't index xe dummy units.
endif
endif
if not UnitFilter(u) then
return //Don't index units that fail the unit filter.
endif
set index = create()
call setIndex(u, index) //Assign an index to the entering unit.
call UnitAddAbility(u, LeaveDetectAbilityID) //Add the leave detect ability to the entering unit.
call UnitMakeAbilityPermanent(u, true, LeaveDetectAbilityID) //Prevent it from disappearing on morph.
set dead[index] = IsUnitType(u, UNIT_TYPE_DEAD) //Reset all of the flags for the entering
set summoned[index] = IsUnitType(u, UNIT_TYPE_SUMMONED) //unit. These flags are necessary to detect
set animated[index] = false //when the unit leaves the map.
set nodecay[index] = false
set removing[index] = false
debug set altered[index] = false //In debug mode, this flag tracks wheter a unit's index was altered.
set idunit[index] = u //Attach the unit that is supposed to have this index to the index.
if duringinit then
call GroupAddUnit(preplaced, u) //Add units that are created during initialization to the preplaced
//units group. This ensures that all units are noticed by OnUnitIndexed during initialization.
endif
loop //Create AutoStructs for the entering unit.
exitwhen n > autostructs_n
call creators[n].evaluate(u)
set n = n + 1
endloop
set n = 0
loop //Run the OnUnitIndexed events.
exitwhen n > indexfuncs_n
call indexfuncs[n].evaluate(u)
set n = n + 1
endloop
endmethod
private static method onIssuedOrder takes nothing returns boolean
if getIndex(GetTriggerUnit()) == 0 then
call unitEntersMap(GetTriggerUnit())
endif //If the unit doesn't have an index at this point, assign it one.
//This is necessary to catch units with default-on autocast abilities
//when using GetUnitId on a newly created unit within an order event.
//! runtextmacro optional TransportUnloadCheck()
//If TransportEvents is in the map, check whether a unit is unloading.
return GetIssuedOrderId() == 852056 //If the order is Undefend, allow detectStatus to run.
endmethod
private static method initEnteringUnit takes nothing returns boolean
call unitEntersMap(GetFilterUnit())
return false
endmethod
//===========================================================================
private static method afterInit takes nothing returns nothing
set duringinit = false //Initialization is over; set a flag.
call DestroyTimer(GetExpiredTimer()) //Destroy the timer.
call GroupClear(preplaced) //The preplaced units group is
call DestroyGroup(preplaced) //no longer needed, so clean it.
set preplaced = null
endmethod
private static method onInit takes nothing returns nothing
local region maparea = CreateRegion()
local rect bounds = GetWorldBounds()
local group g = CreateGroup()
local integer i = 15
static if not UseUnitUserData then
set ht = InitHashtable() //Only create a hashtable if it will be used.
endif
loop
exitwhen i < 0
call SetPlayerAbilityAvailable(Player(i), LeaveDetectAbilityID, false)
//Make the LeaveDetect ability unavailable so that it doesn't show up on the command card of every unit.
call TriggerRegisterPlayerUnitEvent(order, Player(i), EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
//Register the "EVENT_PLAYER_UNIT_ISSUED_ORDER" event for each player.
call GroupEnumUnitsOfPlayer(g, Player(i), function AutoIndex.initEnteringUnit)
//Enum every non-filtered unit on the map during initialization and assign it a unique
//index. By using GroupEnumUnitsOfPlayer, even units with Locust can be detected.
set i = i - 1
endloop
call TriggerAddCondition(order, And(function AutoIndex.onIssuedOrder, function AutoIndex.detectStatus))
//The detectStatus method will fire every time a non-filtered unit recieves an undefend order.
//And() is used here to avoid using a trigger action, which starts a new thread and is slower.
call TriggerRegisterPlayerUnitEvent(creepdeath, Player(12), EVENT_PLAYER_UNIT_DEATH, null)
call TriggerAddCondition(creepdeath, function AutoIndex.detectStatus)
//The detectStatus method must also fire when a neutral hostile creep dies, in case it was
//sleeping. Sleeping creeps don't fire undefend orders on non-damaging deaths.
call RegionAddRect(maparea, bounds) //GetWorldBounds() includes the shaded boundry areas.
call TriggerRegisterEnterRegion(enter, maparea, function AutoIndex.initEnteringUnit)
//The filter function of an EnterRegion trigger runs instantly when a unit is created.
call TimerStart(CreateTimer(), 0., false, function AutoIndex.afterInit)
//After any time elapses, perform after-initialization actions.
call GroupClear(g)
call DestroyGroup(g)
call RemoveRect(bounds)
set g = null
set bounds = null
endmethod
endstruct
//===========================================================================
// User functions:
//=================
function GetUnitId takes unit u returns integer
static if DEBUG_MODE then
return AutoIndex.getIndexDebug(u)
else
return AutoIndex.getIndex(u)
endif
endfunction
function interface IndexFunc takes unit u returns nothing
function OnUnitIndexed takes IndexFunc func returns nothing
call AutoIndex.onUnitIndexed(func)
endfunction
function OnUnitDeindexed takes IndexFunc func returns nothing
call AutoIndex.onUnitDeindexed(func)
endfunction
endlibrary
And more to the problems.
this is 10% mui ^^
i have no clue why the other 90% fails hard..
but it wont allow to take over 2 places at same time (2 diffrent people),
and when the percents reach 0, it wont destroy the texttag.
Thx in advance =]