package UnitIndexer import OnUnitEnterLeave /* UnitIndexer The goal of this system is to give every unit an array-indexable unique ID as well as to standardize handling of class instances when units are created and destroyed. WARNING: Other systems that use SetUnitUserData functionality are going to BREAK THIS SYSTEM since it depends on being the only system to use it. Conversely, it is very likely that this system is going to break them, too. Always make sure you only have one system using SetUnitUserData. The maximum number of indexed units at any time is 32760, so if you have more units than that at any time, the system will not be able to index them. ---------------------------------------------------------------------------- 1) System initialization In order to initialize the system, the user needs to import UnitIndexer into one of his scripts. import UnitIndexer 2) Getting the index of a unit and getting the unit with an index To get a unit's unique index as an int: unit.getIndex() To get a unit's unique index as an UnitIndex object (this will also give the unit an index if the unit doesn't already have it): unit.toUnitIndex() Remember, it is possible to cast between their return types: unit.toUnitIndex() castTo int unit.getIndex() castTo UnitIndex However, if you save a UnitIndex to a variable, it's possible to get a unit with that object directly, whereas it is necessary to cast into UnitIndex if we're using the int: let id = someunit.getIndex() var u = id.getUnit() // Doesn't work! var u = id castTo UnitIndex.getUnit() // Works! var unitIndex = someunit.toUnitIndex() var u = unitIndex.getUnit() // Works 3) Handlers for indexing and deindexing events Handlers are useful because they allow us quickly write code that destroys instances of classes that refer to some unit. onUnitDeindex(() -> destroy Class.getInstanceThatBelongsTo(getDeindexedUnit())) Conversely, if we want to create a class instance that belongs to a unit that had just been indexed, we can do the same: onUnitIndex(() -> new Class(getIndexedUnit())) If you have never worked with lambda functions, the above code might look like black magic, but it essentially translates to this: function someFunction() new Class(getIndexedUnit()) ...in code somewhere... onUnitIndex(function someFunction()) You can call onUnitDeindex and onUnitIndex as many times as you want, accross your code, and every time a unit gets indexed/deindexed the function will be ran. As can be seen from the code, it is possible to refer to the unit that is being indexed/deindexed using calls getIndexedUnit() and getDeindexedUnit(), respectively It should also be noted that this is not the only use for handlers, but merely the most often utilized one. 4) Control over indexing/deindexing For safety and consistency reasons, the system will always attempt to: a) Index the unit when it enters the map (is created) b) Deindex the unit when it leaves the map (is removed or decays) However, that doesn't stop the user from (de)indexing the unit at any time. For example, if you are using a general unit recycling system, the units will never decay, so they will remain indexed. However, this is also going to cause bugs with systems that keep data that refers to the unit, since they are never going to die and/or be removed. For this reason, it's possible to index/deindex units at your leisure. You can use a) unit.toUnitIndex() to index the unit whenever you want b) unit.deindex() to deindex it whenever you want The system prevents you from indexing/deindexing the unit if it already has/doesn't have the index, so no double indexing or double deindexing of the same unit is possible. */ trigger onIndexTrigger = CreateTrigger() trigger onDeindexTrigger = CreateTrigger() /* Unit stack for nested usage */ unit array tempUnits var tempUnitsCount = 0 function pushUnit(unit u) tempUnits[tempUnitsCount] = u tempUnitsCount++ function popUnit() tempUnitsCount-- @configurable public function shouldIndex(unit _u) returns boolean return true /** Returns the int index of this unit. Can be configured by the user to supply a custom unit indexer which won't break other stdlib components. */ @configurable public function unit.getIndex() returns int return this.getUserData() /** Returns the UnitIndex associated with this unit, creating a new one if necessary. */ @configurable public function unit.toUnitIndex() returns UnitIndex UnitIndex instance = this.getUserData() castTo UnitIndex if (instance == null) instance = new UnitIndex(this) return instance /** Returns the currently indexing unit. That's the last indexed unit or the one about to be deindexed. */ public function getIndexingUnit() returns unit return tempUnits[tempUnitsCount - 1] /** Adds a function to be called before a unit is deindexed. Use the getIndexingUnit() function to refer to the deindexing unit. */ public function onUnitDeindex(code func) onDeindexTrigger.addCondition(Condition(func)) /** Adds a function to be called after a unit is indexed. Use the getIndexingUnit() function to refer to the indexed unit. */ public function onUnitIndex(code func) onIndexTrigger.addCondition(Condition(func)) /** Deindexes a unit. Returns whether the unit was originally indexed. */ public function unit.deindex() returns bool if this.getUserData() == 0 return false else destroy this.toUnitIndex() return true public class UnitIndex protected unit _unit function getUnit() returns unit return this._unit function getIndex() returns int return this castTo int construct(unit whichUnit) this._unit = whichUnit this._unit.setUserData(this castTo int) pushUnit(whichUnit) onIndexTrigger.evaluate() popUnit() ondestroy pushUnit(this._unit) onDeindexTrigger.evaluate() popUnit() this._unit.setUserData(0) // Auto instanciation/destruction init onEnter(() -> shouldIndex(getEnterLeaveUnit()) ? getEnterLeaveUnit().toUnitIndex() : 0) onLeave(() -> shouldIndex(getEnterLeaveUnit()) ? getEnterLeaveUnit().deindex() : 0)