• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Wurst] UnitIndex

Status
Not open for further replies.
Level 14
Joined
Jun 27, 2008
Messages
1,325
Simple manual UnitIndexing system for Wurst.

Unlike most of the vJass UnitIndexing systems this one does not automatically detect units entering/leaving the map but only provides the basic functionality to (de)index units and get their current index.
The indexer may be used directly or wrapped in another package which adds automated (de)indexing.

Requires:
  • Event (which is hopefully part of the Stdlib soon)

System:
Wurst:
/** Basic manual UnitIndexing system. */
package UnitIndex
import Event

/** Returns the units index. Returns 0 if not indexed. */
public function unit.getIndex() returns int
	return this.getUserData()

/** Returns the units UnitIndex object. Returns null if not indexed. */
public function unit.getIndexObject() returns UnitIndex
	return this.getUserData() castTo UnitIndex 

/**
 * Assigns a unique index to the unit.
 * Fires a UnitIndexEvent if the unit did not have a index assigned yet.
 */
public function indexUnit(unit u)
	if u.getUserData() == 0
		new UnitIndex(u)

/**
 * Removes the units index and recycles it.
 * Fires a UnitDeindexEvent if the unit had an index assigned.
 */
public function deindexUnit(unit u)
	if u.getUserData() != 0
		destroy u.getUserData() castTo UnitIndex

/**
 * Fires when a unit gets a index assigned.
 * The field .u references the indexed unit.
 */
public class UnitIndexEvent
	use EventModule
	static unit indexedUnit
/**
 * Fires when a unit has its index removed.
 * The field .u references the deindexed unit.
 */
public class UnitDeindexEvent
	use EventModule
	static unit deindexedUnit

/** Extend this class to create a unit wrapper. */
public class UnitIndex
	unit u
	
	/** Assigns a unique index to the unit and fires UnitIndexEvent. */
	construct(unit u)
		this.u = u
		u.setUserData(this castTo int)
		let tmp = UnitIndexEvent.indexedUnit
		UnitIndexEvent.indexedUnit = u
		UnitIndexEvent.fire()
		UnitIndexEvent.indexedUnit = tmp
	
	ondestroy
		let tmp = UnitDeindexEvent.deindexedUnit
		UnitDeindexEvent.deindexedUnit = u
		UnitDeindexEvent.fire()
		UnitDeindexEvent.deindexedUnit = tmp
		u.setUserData(0)

GitHub:
https://github.com/muzzel/APT/tree/master/wurst/lib/UnitIndex
 
Last edited:
Neat implementation.

Some notes:
  • You should support recursion in your event with a temp variable:
    Wurst:
    temp = UnitIndexEvent.indexedUnit
    UnitIndexEvent.indexedUnit = u
    UnitIndexEvent.fire()
    UnitIndexEvent.indexedUnit = temp
    Otherwise it can fail in some cases, e.g.:
    Wurst:
    function onIndex()
        dum = CreateUnit(Player(0), 'n000', 0, 0, 0)
        BJDebugMsg(UnitIndexEvent.indexedUnit.getName())
        index = dum.getIndex()
        BJDebugMsg(UnitIndexEvent.indexedUnit.getName())
    Let's say that onIndex fires when a footman is indexed. That code will show "Footman" then "Dummy" (also assume it has a condition so that it won't run on dummies, so it won't infinitely loop :p). You can always assign a local to point to the indexedUnit at the top of the function, but IMO that is restrictive to the user.
  • Can there be some method for casting an index to a unit?
  • I'm not the biggest fan of manual deindexing, especially when you have awesome wurst compiletime functions. But I suppose since you have control of when you remove units, you can always just deindex then. Eh, it is still a little weird that the user has to implement deindexing for decays/deaths though, and that the user has to check to see if the unit is indexed before retrieving it. I understand that it can be wrapped, but I think in its current state I'd always end up writing a wrapper for it.

    IMO, you could just make an improved wurst-version of PUI. Idk if I'd use the system directly in its current state unless I was working with a set of units (e.g. dummies). With arbitrary units (where a unit may or may not be already indexed), you'll likely need a wrapper to avoid extra fluff code.

Those are just some suggestions/considerations. ;D
 
Level 14
Joined
Jun 27, 2008
Messages
1,325
Thx for the input. While i was away i had another pretty cool idea which im gonna add soon.

You should support recursion in your event with a temp variable: [...]
I thought about adding support for recoursion in my eventlib using instanciated objectdata, but i didnt like it because the eventlib is supposed to be simple and fast. But your solution is much better, thx a lot.

I'm not the biggest fan of manual deindexing, especially when you have awesome wurst compiletime functions.
I dont understand how compiletime functions help with indexing :S?

But I suppose since you have control of when you remove units, you can always just deindex then. Eh, it is still a little weird that the user has to implement deindexing for decays/deaths though, and that the user has to check to see if the unit is indexed before retrieving it. I understand that it can be wrapped, but I think in its current state I'd always end up writing a wrapper for it.
Well the idea of this package was to provide only manual indexing, an automated indexing library can be written on top of this.
One of the reasons for this decision is that i think a unit should not always be deindexed when its dying. For instance if you recycle units by preventing them from dying then the death event will never fire but it might still be a good idea to deindex the unit for the time its not active.
Also for vJass there are too many different indexers so you often end up having to import two or more indexing systems (or modify them for compatibility). This package is pretty much the smallest common denominator of indexing systems, so no matter if you use an automated indexing on top of it, all the other systems can simply be based on this package.

and that the user has to check to see if the unit is indexed before retrieving it.
You want "unit.getIndex" to create a index if the unit doesnt have one? I thought about it, but im not sure :/

Can there be some method for casting an index to a unit?
Yep, gonna add that.
 
Level 14
Joined
Jun 27, 2008
Messages
1,325
Update:
- Added support for recoursive events
- Moved most of the functionality into the UnitIndex class. In addition to the old API this class can now be extended to work as a wrapper for units.

Extending the UnitIndex class allows to add members and initialize/clean them explicitely. Compared to using onIndex/onDeindex events this has the advantage that the user can call the actions in a well-defined order and pass additional arguments by adding them to the constructor.

Here is an example of the UnitIndex system used as a wrapperclass:
https://github.com/muzzel/APT/blob/master/wurst/src/units/UnitX.wurst

To do:
- find a name for a "index2Unit" function (and for "index2UnitIndexObject")
- decide if "unit.getIndex" should index the unit if its not indexed instead of returning 0
 
Level 14
Joined
Jun 27, 2008
Messages
1,325
property u from UnitIndex should be private|protected (same for the UnitIndexEvent & UnitDeIndexEvent unit property).
I cant make it private, or subclasses wont be able to access it. I could make it protected.
Ultimately i want to make it "publicread", but that feature does not exist yet, so if i write a getterfunction il need to break compatibility once the publicread feature is available. -> For now il keep it public.
About UnitIndexEvent/UnitDeindexEvent - their unit property is supposed to be used outside of the class, so it has to be public.

fire deindex event before deleting the index from the unit.
That was a mistake, thx.

event logic shouldn't be inside UnitIndex class.
It has to be inside the class to allow creating a unitwrapper by extending UnitIndex.
 
Level 10
Joined
Sep 19, 2011
Messages
527
what abot something like this:

JASS:
module UnitIndexEvent
    protected static unit triggerUnit = null
    use EventModule
    
    static function getTriggerUnit() returns unit
        return triggerUnit
    
    static function fire(UnitDex unitDex)
        unit prevUnit = triggerUnit
        triggerUnit = unitDex.getUnit()
        
        EventModule.fire()
        
        triggerUnit = prevUnit
        prevUnit = null
    
public abstract class IndexEvent
    use UnitIndexEvent
    
public abstract class DeIndexEvent
    use UnitIndexEvent
    
public class UnitDex
    protected unit u
    
    function getUnit() returns unit
        return this.u
        
    ondestroy
        DeIndexEvent.fire(this)
        this.u.setUserData(0)
    
    construct(unit whichUnit)
        this.u = whichUnit
        u.setUserData(this castTo int)
        IndexEvent.fire(this)
About UnitIndexEvent/UnitDeindexEvent - their unit property is supposed to be used outside of the class, so it has to be public.
yes, but shouldn't be changed when an event is fired for example.
 
Last edited:
Status
Not open for further replies.
Top