• 🏆 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!

Unit Indexer Spell Template

For those who want to use an easy format for making your spells MUI, this might be a
good resource for you.

How to use
  1. Copy the following triggers into your map:
    • Unit Indexer
    • Cast Spell
    • Loop Spell
    • Preload Spells
  2. For all spells you make, add the spell to the "Preload Spells" trigger
  3. Modify the Cast Spell and Loop Spell triggers to what you need your spells to be like
  4. Follow the advice left in the spells as comments - this will help you develop and customize your spell.
  5. Anything you don't understand, please ask questions. This is after all a tutorial

  • Cast Spell
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to TesterSpell
    • Actions
      • -------- --------
      • -------- Create a dummy unit to represent this spell --------
      • -------- --------
      • Set TargetPoint = (Target point of ability being cast)
      • Unit - Create 1 Dummy (Vexorian, Anitarf, Infrane) for (Triggering player) at TargetPoint facing 0.00 degrees
      • -------- --------
      • -------- The variable ID represents this individual cast of the spell --------
      • -------- --------
      • Set TempUnit = (Last created unit)
      • Set ID = (Custom value of TempUnit)
      • -------- --------
      • -------- Use the ID variable with arrays to store exclusive data --------
      • -------- --------
      • -------- These next variables are just to show how to use it, you can delete them --------
      • -------- --------
      • Set TesterSpell_Caster[ID] = (Triggering unit)
      • Special Effect - Create a special effect at TargetPoint using Abilities\Spells\Other\Tornado\TornadoElemental.mdl
      • Set TesterSpell_Effect[ID] = (Last created special effect)
      • Set TempReal = (Real((Level of (Ability being cast) for TesterSpell_Caster[ID])))
      • Set TesterSpell_Damage[ID] = TempReal
      • Set TesterSpell_Radius[ID] = (200.00 + (50.00 x TempReal))
      • -------- --------
      • -------- You can change the value of 'duration' to whatever you want --------
      • -------- --------
      • Set TesterSpell_Duration[ID] = (5.00 x TempReal)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (TesterSpell_Group is empty) Equal to True
        • Then - Actions
          • -------- --------
          • -------- Turn on the loop trigger as it's currently off --------
          • -------- --------
          • Trigger - Turn on Loop Spell <gen>
        • Else - Actions
      • -------- --------
      • -------- Add the dummy to this spell's group so that it can be referenced over time --------
      • -------- --------
      • Unit Group - Add TempUnit to TesterSpell_Group
      • -------- --------
      • -------- Clean leaks of course --------
      • -------- --------
      • Custom script: call RemoveLocation(udg_TargetPoint)
  • Loop Spell
    • Events
      • Time - Every 0.03 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in TesterSpell_Group and do (Actions)
        • Loop - Actions
          • Set TempUnit = (Picked unit)
          • Set ID = (Custom value of TempUnit)
          • -------- --------
          • -------- This next chunk of code is just for the demo - change it to what you want your spell to be like --------
          • -------- --------
          • Set TempPlayer = (Owner of TempUnit)
          • Set CenterPoint = (Position of TempUnit)
          • Custom script: set bj_wantDestroyGroup = true
          • Unit Group - Pick every unit in (Units within TesterSpell_Radius[ID] of CenterPoint) and do (Actions)
            • Loop - Actions
              • Set TargetUnit = (Picked unit)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (TargetUnit belongs to an enemy of TempPlayer) Equal to True
                • Then - Actions
                  • -------- --------
                  • -------- I let the dummy unit deal the damage in case the caster is dead/removed --------
                  • -------- --------
                  • Unit - Cause TempUnit to damage TargetUnit, dealing TesterSpell_Damage[ID] damage of attack type Spells and damage type Unknown
                • Else - Actions
          • Custom script: call RemoveLocation(udg_CenterPoint)
          • -------- --------
          • -------- When the duration reaches 0 it means the spell is done --------
          • -------- --------
          • Set TesterSpell_Duration[ID] = (TesterSpell_Duration[ID] - 0.03)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • TesterSpell_Duration[ID] Less than or equal to 0.00
            • Then - Actions
              • -------- --------
              • -------- Clean up any data attached to this spell --------
              • -------- --------
              • Special Effect - Destroy TesterSpell_Effect[ID]
              • -------- --------
              • -------- Remove the dummy from the group and from the game --------
              • -------- --------
              • Unit Group - Remove TempUnit from TesterSpell_Group
              • Unit - Remove TempUnit from the game
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (TesterSpell_Group is empty) Equal to True
                • Then - Actions
                  • -------- --------
                  • -------- Turn off this trigger as it's currently doing nothing --------
                  • -------- --------
                  • Trigger - Turn off Loop Spell <gen>
                • Else - Actions
            • Else - Actions
  • Preload Spells
    • Events
      • Game - UnitIndexEvent becomes Equal to 3.00
    • Conditions
    • Actions
      • Set UnitIndexerEnabled = False
      • Set CenterPoint = (Center of (Playable map area))
      • Unit - Create 1 Dummy (Vexorian, Anitarf, Infrane) for Neutral Passive at CenterPoint facing 0.00 degrees
      • Set TempUnit = (Last created unit)
      • -------- --------
      • -------- To preload abilities and/or buffs, just add them to TempUnit --------
      • -------- --------
      • Unit - Add TesterSpell to TempUnit
      • -------- --------
      • -------- The dummy unit's task is now done, it can be removed from the game --------
      • -------- --------
      • Unit - Remove TempUnit from the game
      • Custom script: call RemoveLocation(udg_CenterPoint)
      • Set UnitIndexerEnabled = True
  • Unit Indexer
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Custom script: call ExecuteFunc("InitializeUnitIndexer")
      • Custom script: endfunction
      • -------- --------
      • -------- This is the most important function - it provides an index for units as they enter the map --------
      • -------- --------
      • Custom script: function IndexUnit takes nothing returns boolean
      • Custom script: local integer pdex = udg_UDex
      • -------- --------
      • -------- You can use the boolean UnitIndexerEnabled to protect some of your undesirable units from being indexed --------
      • -------- - Example: --------
      • -------- -- Set UnitIndexerEnabled = False --------
      • -------- -- Unit - Create 1 Dummy for (Triggering player) at TempLoc facing 0.00 degrees --------
      • -------- -- Set UnitIndexerEnabled = True --------
      • -------- --------
      • -------- You can also customize the following block - if conditions are false the (Matching unit) won't be indexed. --------
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • UnitIndexerEnabled Equal to True
        • Then - Actions
          • -------- --------
          • -------- Generate a unique integer index for this unit --------
          • -------- --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • UDexRecycle Equal to 0
            • Then - Actions
              • Set UDex = (UDexGen + 1)
              • Set UDexGen = UDex
            • Else - Actions
              • Set UDex = UDexRecycle
              • Set UDexRecycle = UDexNext[UDex]
          • -------- --------
          • -------- Link index to unit, unit to index --------
          • -------- --------
          • Set UDexUnits[UDex] = (Matching unit)
          • Unit - Set the custom value of UDexUnits[UDex] to UDex
          • -------- --------
          • -------- Use a doubly-linked list to store all active indexes --------
          • -------- --------
          • Set UDexPrev[UDexNext[0]] = UDex
          • Set UDexNext[UDex] = UDexNext[0]
          • Set UDexNext[0] = UDex
          • -------- --------
          • -------- Fire index event for UDex --------
          • -------- --------
          • Set UnitIndexEvent = 0.00
          • Set UnitIndexEvent = 1.00
          • Set UnitIndexEvent = 0.00
          • Custom script: set udg_UDex = pdex
        • Else - Actions
      • Custom script: return false
      • Custom script: endfunction
      • -------- --------
      • -------- The next function is called each time a unit enters the map --------
      • -------- --------
      • Custom script: function IndexNewUnit takes nothing returns boolean
      • Custom script: local integer pdex = udg_UDex
      • Custom script: local integer ndex
      • -------- --------
      • -------- Recycle indices of units no longer in-play every (15) units created --------
      • -------- --------
      • Set UDexWasted = (UDexWasted + 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • UDexWasted Equal to 15
        • Then - Actions
          • Set UDexWasted = 0
          • Set UDex = UDexNext[0]
          • Custom script: loop
          • Custom script: exitwhen udg_UDex == 0
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Custom value of UDexUnits[UDex]) Equal to 0
            • Then - Actions
              • -------- --------
              • -------- Remove index from linked list --------
              • -------- --------
              • Custom script: set ndex = udg_UDexNext[udg_UDex]
              • Custom script: set udg_UDexNext[udg_UDexPrev[udg_UDex]] = ndex
              • Custom script: set udg_UDexPrev[ndex] = udg_UDexPrev[udg_UDex]
              • Set UDexPrev[UDex] = 0
              • -------- --------
              • -------- Fire deindex event for UDex --------
              • -------- --------
              • Set UnitIndexEvent = 2.00
              • Set UnitIndexEvent = 0.00
              • -------- --------
              • -------- Recycle the index for later use --------
              • -------- --------
              • Set UDexUnits[UDex] = No unit
              • Set UDexNext[UDex] = UDexRecycle
              • Set UDexRecycle = UDex
              • Custom script: set udg_UDex = ndex
            • Else - Actions
              • Set UDex = UDexNext[UDex]
          • Custom script: endloop
          • Custom script: set udg_UDex = pdex
        • Else - Actions
      • -------- --------
      • -------- Handle the entering unit (Matching unit) --------
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Custom value of (Matching unit)) Equal to 0
        • Then - Actions
          • Custom script: call IndexUnit()
        • Else - Actions
      • Custom script: return false
      • Custom script: endfunction
      • -------- --------
      • -------- The next function initializes the core of the system --------
      • -------- --------
      • Custom script: function InitializeUnitIndexer takes nothing returns nothing
      • Custom script: local integer i = 0
      • Custom script: local region re = CreateRegion()
      • Custom script: local rect r = GetWorldBounds()
      • Set UnitIndexerEnabled = True
      • Custom script: call RegionAddRect(re, r)
      • Custom script: call TriggerRegisterEnterRegion(CreateTrigger(), re, Filter(function IndexNewUnit))
      • Custom script: call RemoveRect(r)
      • Custom script: set re = null
      • Custom script: set r = null
      • Custom script: loop
      • Custom script: call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, Player(i), Filter(function IndexUnit))
      • Custom script: set i = i + 1
      • Custom script: exitwhen i == 16
      • Custom script: endloop
      • -------- --------
      • -------- This is the "Unit Indexer Initialized" event, use it instead of "Map Initialization" for best results --------
      • -------- --------
      • Set UnitIndexEvent = 3.00
      • Set UnitIndexEvent = 0.00
Keywords:
unit indexer, spell, mui, template, indexing
Contents

Bribe's Test Map (Map)

  • Unit - Create 1 Dummy (Vexorian, Anitarf, Infrane) for Neutral Passive at CenterPoint facing 0.00 degrees
lol (Although I have no idea who Infrane is xD)

This looks pretty good Bribe :)
Maybe now people would use UnitIndexer more :p

Btw, After converting UnitIndexer to Jass and implementing it, my systems started running faster :D (Deprecated AIDS)
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Preloading the spell prevents lag on the first cast if the spell wasn't already on an
existing here when the map was first loaded. Quite important with the spell is to also
also preload dummy units and the special effects. Now if you do this right the dummy
unit in the map is the last dummy unit you ever need, for anything. The demo map does
not preload the special effect because I wanted to keep things as simple as possible for
users, but any real spell I highly recommend it because it keeps things seamless all
throughout the game.
 
Preloading the spell prevents lag on the first cast if the spell wasn't already on an
existing here when the map was first loaded. Quite important with the spell is to also
also preload dummy units and the special effects. Now if you do this right the dummy
unit in the map is the last dummy unit you ever need, for anything. The demo map does
not preload the special effect because I wanted to keep things as simple as possible for
users, but any real spell I highly recommend it because it keeps things seamless all
throughout the game.

Ohh right; see that makes more sense now,
Cheers for explaining that; when i get chance shall add preload to my Cloud Spiral then,
so its easier for people who are you using your System, to get a better idea of spells.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
It's usually not the hero spell itself that needs to be preloaded but dummy spells that the triggered spell uses as they might be "added" to the hero. This will 'cause a heavy "first cast lagg" when a dummy ability is added to a unit for the first time and thus added to a dummy unit at first will make it preloaded and safe to use for the next add which might be your hero unit.

EDIT:

I think it's worth to mention that Playable map area in this line will not leak as it's a predefined region. Else way I think this is good enough.

Set CenterPoint = (Center of (Playable map area))

PS to Mag -> Ever tried fixing AIDS? I think I've done it once or twice for my maps although I cannot find it now when I speak about it :) (And use Nest's UnitIndexer!)

EDIT2:

Are we allowed to upload resources that uses your UnitIndexer Bribe? Maybe I can code GUI again then hehe.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
The point "center of map area" leaks, because it creates a new location every time.

Yeah spells can use unit indexer, that's why I made it.

I'm trying to figure out why my current test map is crashing during the load process, can
anyone help me with this?

Current map is now attached.
 

Attachments

  • Bribe Test Map.w3x
    50.2 KB · Views: 332
Level 22
Joined
Nov 14, 2008
Messages
3,256
The point "center of map area" leaks, because it creates a new location every time.

Yeah spells can use unit indexer, that's why I made it.

I'm trying to figure out why my current test map is crashing during the load process, can
anyone help me with this?

Current map will be attached in just a moment.

You seem to be tired my friend.

I think it's worth to mention that Playable map area in this line will not leak as it's a predefined region

Crashes whaaaat?
 
I see something;
  • Unit - Cause TempUnit to damage TargetUnit, dealing TesterSpell_Damage[ID] damage of attack type Spells and damage type Unknown
Sometimes it is not a good idea to use the tempunit, Lets say the unit is not in sight of the unit; So in the Fog Of War area; sometimes you wont get the exp;
Also; you want the unit to charge to the caster; not away from the dummy unit;
so i would recommend changing it to;
  • Unit - Cause TesterSpell_Caster[ID] to damage TargetUnit, dealing TesterSpell_Damage[ID] damage of attack type Spells and damage type Unknown
Also use a different variable in the loop trigger;
Maybe
  • Set TempID = (Custom value of (Picked Unit))
 
PS to Mag -> Ever tried fixing AIDS? I think I've done it once or twice for my maps although I cannot find it now when I speak about it :) (And use Nest's UnitIndexer!)

Actually, I'm using Bribe's UnitIndexer converted to vJASS xD
Nestharus' has LUA scripts (I'm never ever going to use them because once, they made a map of mine unplayable :S)
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Keep in mind that GUI Unit Indexer does not deindex units when they are still in the game
so things like "Is Unit Hero", "Level of Unit", "Position of Unit" will not work. Nestharus'
Unit Indexer uses LUA scripts to generate a copy of the "Defend" ability to detect the
moment the unit leaves the game. I use this trick in "Unit Event" you just set an event
where DeathEvent becomes equal to 3.00.

Anyway not to get too much into it, you can avoid Nestharus' mess of LUA scripts by
copy and pasting the defend ability and creating a global constant integer called
ABILITIES_UNIT_INDEXER set to the rawcode of that ability. That's what I had to do
originally with it. Same went for AutoIndex and AIDS, their object mergers were not
working for me.

I am doing a lot more work in GUI lately because of two main reasons: 1) NewGen does
not support the full GUI, so vJass resources are not going to be compatible with GUI
resources, and 2) NewGen does not work with a lot of antivirus software and doesn't
work at all on Macs.
 
Anyway not to get too much into it, you can avoid Nestharus' mess of LUA scripts by
copy and pasting the defend ability and creating a global constant integer called
ABILITIES_UNIT_INDEXER set to the rawcode of that ability. That's what I had to do
originally with it. Same went for AutoIndex and AIDS, their object mergers were not
working for me.

Works for me ^^
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Come on, 18 comments already and not even one 5/5 (= Gogo GUI giant.
GUI is easy to read, especialy for guys who are used to GUI. If you are high in War3 programing (vJass) but have problems with foundaments (GUI) - it's shame for you. The graphic user interface was here, and always will be since it's what you see after opening the WE for the first time.

EDIT: Sorry for that 'game' mistake - polish shortcuts :S
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Makes sense, baassee, I guess that's one argument for using Unit Indexer. But I still think
it's best for people to use whatever indexing system is preferred for them, Hanky's is the
fastest that I know of, and some people really like using hashtables. My personal opinion
is that using a unit indexer is the most readable way to do it.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
Yeah of course the user should use whatever they want (except they cannot use standard paladon indexing anymore). Hanky's indexing is really good, so are D4RK_G4ND4LF's it's like struct indexing in vJASS but in GUI :) (reffering to Dynasti's tutorial, this was before T32 was invented?)
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
The indexing I did with Unit Indexer is as good as it gets, using perfect struct indexing and
a doubly linked list. That's why I just recommend people to create a dummy unit for each
spell instance instead of doing their own indexing, and also using a unit group instead of
using a stack or linked list of their own. This at least saves you from having to create
the same indexing thing over and over per spell.
 
Level 13
Joined
May 11, 2008
Messages
1,198
GUI is easy to read if you know how, same with JASS. Weren't you the guy who
didn't like to indent your code?

Anyway, it's GUI because that means you can automatically create unknown variables
when pasting trigger data - when pasting custom script you don't get that benefit.

i really don't understand your reasoning. you can make the variables easily in vjass. is this meant to be regular war3 editor compatible.

and anyway it's not that i can't read gui....it's just so timeconsuming to read...i don't have that kind of time. i'd rather read vjass or even cjass, much faster to read that stuff.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
>> is this meant to be regular war3 editor compatible.

That's exactly it. You just can't expect everyone to have NewGen. A lot of antivirus
stuff has problems with it, and it doesn't even work on Macs.

I also got very tired of the broken pJass parser which doesn't compile things that do
get compiled in normal World Editor.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
It makes the spells MUI because you create a unique unit for each cast of the spell and adds the units to a group.

Each unit has a unique ID that can be used as array indices.

So when you loop through the group, you will be able to access data specific to each cast of the spell.

It is really strange to explain with words, it would be better with an illustration so I may end up creating one some time. I am just really behind on everything because work, life, many existing projects of mine that need to be updated/completed, etc.
 
Level 6
Joined
Jun 19, 2010
Messages
143
MUI=Multi-Units Interface, correct? Kinda spell that conjures a lot of dummies casting a skill/some skills multiple times. The spell can be area effect, single effect but return something special in front of players' eyes?. If I understand it correctly.
 
Level 6
Joined
Jun 19, 2010
Messages
143
Thank you for MUI explanation. So the idea is to get more than one units cast the spell simultaneously.
Conventionally, I made 2-3 orders for 2-3 units casting the spell, let's say
issue unit1 an order1
issue unit2 an order2
They are not casting the same time I guess though there is no TriggerSleepAction. Since there is a sequence in those orders I believe. Is it right your system is used to get rid of this?
One way I know is to get units in region/range (they are under spell effect), issue dummies orders to every picked unit. I issue 2 dummies orders to 4 enermy units in that range, then each dummy will do 4 orders, 8 altogether. Any comment on this?

I don't read through your system at the moment, but I appreciate you could explain the core idea of your system.
Thanks.
 
Level 5
Joined
May 20, 2013
Messages
162
OMFW!!! I just ruined a months work of work by importing these triggers into my map! :(

I guess that's what i get for messing with stuff i don't know a damn thing about!
 
Level 20
Joined
Aug 13, 2013
Messages
1,696
So as you said that this is like a hashtable.. saving/loading datas. It only changes the GetHandleId to Custom Value of Unit right???. So this system will make all the GUI MUI Hashtable User like me to be easier and faster in the code??

Is this Unit Indexer is efficient enough??
 
Top