• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • Create a faction for Warcraft 3 and enter Hive's 19th Techtree Contest: Co-Op Commanders! Click here to enter!
  • Create a void inspired texture for Warcraft 3 and enter Hive's 34th Texturing Contest: Void! Click here to enter!
  • The Hive's 21st Texturing Contest: Upgrade is now concluded, time to vote for your favourite set of icons! Click here to vote!

unit indexer with correct initialization

Status
Not open for further replies.
Level 10
Joined
Sep 19, 2011
Messages
527
i'm not a fan of unitindexing systems but when i tried to build one, my initialization was wrong. so today i was kinda boring and i decided to give it a try and make it right. here is the result:

JASS:
//! external ObjectMerger w3a Adef !e@$ anam "UnitIndexer" ansf "(UnitIndexer)" aart "" acat "" arac 0 

library UnitIndexer
	globals
		public constant integer UNIT_INDEXER_ABILITY = '!e@$'
		private constant boolean USE_HASHTABLE = false
	endglobals
	
	private function GlobalUnitFilter takes unit whichUnit returns boolean
		return true
	endfunction
	
	static if ( USE_HASHTABLE ) then
		globals
			private hashtable hashTable = InitHashtable()
		endglobals
	endif
	
	module UnitIndexerModule
		private static method onInit takes nothing returns nothing
			static if ( thistype.onIndex.exists ) then
				call UnitIndexer.onIndex(function thistype.onIndex)
			endif
			static if ( thistype.onDeIndex.exists ) then
				call UnitIndexer.onDeIndex(function thistype.onDeIndex)
			endif
		endmethod
	endmodule
	
	private module UnitIndexerInit
		private static method initialize takes nothing returns nothing		
			set thistype.initialized = true
			
			loop
				exitwhen integer(thistype.lastUnitIndexer) == 0
				call thistype.lastUnitIndexer.fireEvent(true)
				set thistype.lastUnitIndexer = thistype.lastUnitIndexer - 1
			endloop
			
			call DestroyTimer(GetExpiredTimer())
		endmethod
		
		private static method onInit takes nothing returns nothing
			call thistype.indexInitialUnits()
            call TimerStart(CreateTimer(), 0, false, function thistype.initialize)
		endmethod
	endmodule
	
	struct UnitIndexer
		readonly static thistype triggered = 0
		private static trigger eventTrigger = CreateTrigger()
		
		private static integer array locked
		
		private static boolean initialized = false
		private static thistype lastUnitIndexer = 0
	
		readonly unit unit
		static if ( USE_HASHTABLE ) then
			readonly integer handleId
		endif
		
		method isLocked takes nothing returns boolean
			return thistype.locked[this] > 0
		endmethod
		
		method lock takes boolean lock returns nothing
			if ( lock ) then
				set thistype.locked[this] = thistype.locked[this] + 1
			else
				set thistype.locked[this] = thistype.locked[this] - 1
			endif
		endmethod
		
		static method onIndex takes code c returns nothing
			call TriggerAddCondition(thistype.eventTrigger, Condition(c))
		endmethod
		
		static method onDeIndex takes code c returns nothing
			call TriggerAddAction(thistype.eventTrigger, c)
		endmethod
		
		private method fireEvent takes boolean indexEvent returns nothing
			local thistype prevTriggered = thistype.triggered
			set thistype.triggered = this
						
			if ( indexEvent ) then
				if ( thistype.initialized ) then
					call TriggerEvaluate(thistype.eventTrigger)
				endif
			else
				if ( thistype.initialized ) then
					call TriggerExecute(thistype.eventTrigger)
				else
					set thistype.initialized = true
				
					call this.fireEvent(true)
					call this.fireEvent(false)
					
					set thistype.initialized = false
				endif
			endif
			
			set thistype.triggered = prevTriggered
		endmethod
		
		method destroy takes nothing returns nothing
			call this.fireEvent(false)
			
			call UnitRemoveAbility(this.unit, UNIT_INDEXER_ABILITY)
		
			static if ( USE_HASHTABLE ) then
				call FlushChildHashtable(hashTable, this.handleId)
				set this.handleId = 0
			else
				call SetUnitUserData(this.unit, 0)
			endif
			
			set this.unit = null
			
			if ( not(this.isLocked()) ) then
				call this.deallocate()
			endif
		endmethod
		
		static method get takes unit whichUnit returns thistype
			static if ( USE_HASHTABLE ) then
				return LoadInteger(hashTable, GetHandleId(whichUnit), 0)
			else
				return GetUnitUserData(whichUnit)
			endif
		endmethod
		
		static method create takes unit whichUnit returns thistype
			local thistype this
			
			if ( thistype.get(whichUnit) == 0 and GlobalUnitFilter(whichUnit) ) then
				set this = thistype.allocate()
				
				if ( integer(thistype.lastUnitIndexer) < integer(this) ) then
					set thistype.lastUnitIndexer = this
				endif
				
				static if ( USE_HASHTABLE ) then
					set this.handleId = GetHandleId(whichUnit)
					call SaveInteger(hashTable, this.handleId, 0, this)
				else
					call SetUnitUserData(whichUnit, this)
				endif
				
				set this.unit = whichUnit
				
				call UnitAddAbility(whichUnit, UNIT_INDEXER_ABILITY)
				call UnitMakeAbilityPermanent(whichUnit, true, UNIT_INDEXER_ABILITY)
				
				call this.fireEvent(true)
			endif
			
			return this
		endmethod
		
		private static method onEnter takes nothing returns boolean
			call thistype.create(GetFilterUnit())
			return false
		endmethod
		
		private static method onLeave takes nothing returns boolean
			local unit leavingUnit = GetFilterUnit()
			local thistype unitIndexer = thistype.get(leavingUnit)
		
			if ( GetUnitAbilityLevel(leavingUnit, UNIT_INDEXER_ABILITY) == 0 and unitIndexer != 0 ) then
				call unitIndexer.destroy()
			endif
			
			set leavingUnit = null
			
			return false
		endmethod
		
		private static method indexInitialUnits takes nothing returns nothing
            local trigger onLeaveTrigger = CreateTrigger()
			local boolexpr onEnterCondition = Condition(function thistype.onEnter)
            local boolexpr onLeaveCondition = Condition(function thistype.onLeave)
            local region worldBoundsRegion = CreateRegion()
			local integer maxPlayers = bj_MAX_PLAYERS
            local player enumPlayer
           
            loop
                exitwhen maxPlayers < 0
               
                set enumPlayer = Player(maxPlayers)
               
                call TriggerRegisterPlayerUnitEvent(onLeaveTrigger, enumPlayer, EVENT_PLAYER_UNIT_ISSUED_ORDER, onLeaveCondition)
                call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, enumPlayer, onEnterCondition)
				call SetPlayerAbilityAvailable(enumPlayer, UNIT_INDEXER_ABILITY, false)
				
                set maxPlayers = maxPlayers - 1
            endloop
			
            call RegionAddRect(worldBoundsRegion, GetWorldBounds())
            call TriggerRegisterEnterRegion(CreateTrigger(), worldBoundsRegion, onEnterCondition)
            
            set onLeaveTrigger = null
			set onEnterCondition = null
            set onLeaveCondition = null
            set worldBoundsRegion = null
            set enumPlayer = null
		endmethod
		
		implement UnitIndexerInit
	endstruct
endlibrary

as you can see, pretty simple, nothing special but (hopefully) with correct initialization (i tested and works good).

i hope this serves to anyone to understand a little bit more how unit indexers work.
 
Status
Not open for further replies.
Top