Custom AOE targeting indicator

Level 25
Joined
Mar 29, 2020
Messages
1,466
Custom spell target indicators



in general, when creating a spell , there is one standard spell targeting indicator available for each race. And you can edit this globally from the game interface panel. However if, you do not want to change it globally, but instead you want a different spell targeting image per spell (limited to one spell per unit as all the instant build abilities use the same ID), you can do so following these simple steps:

first, some stuff in the object editor:

1.
Create a unit that matches what you want to use as a spell indicator.
The main fields that need to be changed are:

Art – model file: this is where you put what you want as the spell indicator.

Art – scaling value: this is how you adjust your affect to match the desired AOE. You can just place the unit in the editor to play around with the size until you get it right.

Pathing – Pathing Map: none

Pathing – placement requires: leave blank

You can mess around with these last two fields to make your spell only castable on specific terrain types (buildable,walkable, naval etc).


2. Create an ability based on the instant tower ability and edit the following fields:
Data-unit created per player race – just set this as the unit you created in stage 1 four times in a row.

Stats – cast range: this should match the cast range of the next custom ability we create for smoothness.

Stats – duration – I set this to 0.1 just to make sure that the building doesn’t complete before the other triggers take effect.

Stats – item ability = false.

This is the ability that will be the “Front end” ability you use. So this is where you should put all the stuff like mana cost, cooldown, custom icons, tooltips etc.


3. Create an ability based on channel. This will be our real ability. These are the important fields to edit:
Data – target type: point target
Data - Options – do not check visible!. We want this to stay invisible.
Data – base order Id : use a point targeting spell that your casting unit is not already using.
Stats – cast range: this should match the cast range of the previous ability we created (and the cast range you want for your ability...).

Besides these things, set whatever else you want for your ability, and trigger your ability however you want. Channel is great for this, and if you are not familiar with it yet, you should read up on it.


4. Give your unit both of these abilities. Only the first one should be visible.



Now we will set up the Triggering:

we will add a few simple triggers (which can all be found in the demo map attached to this tutorial):

1. Rewiring the build tower order to the channel order:
(my ability is using the ability ID from "Undead Destroyer - Devour Magic". you will need to select the ability that is the orderID your channel spell is using)
  • rewire build order
    • Events
      • Unit - A unit Begins casting an ability
    • Conditions
      • (Ability being cast) Equal to InstantBuildingBasedSpell
    • Actions
      • Set VariableSet tempPoint = (Target point of ability being cast)
      • Unit - Order (Triggering unit) to Undead Destroyer - Devour Magic tempPoint
      • Custom script: call RemoveLocation(udg_tempPoint)

2. dealing with the summoned unit:

This requires moving the spawned unit to somewhere out of the playable map so we don't hear its building loop which seems to be hardcoded to the instant building ability (and not changeable by the building loops sound field). There are many ways you could do this, I made a point outside of the map by creating a small region outside of the playable map, and then saving it on game init:
  • OutOfBounds
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet tempPoint = (Center of Region 000 <gen>)
here we are moving the unit there when built, and then removing it from the game (just removing it doesn't deal with the sound):

  • remove built unit
    • Events
      • Unit - A unit Begins construction
    • Conditions
      • (Unit-type of (Constructing structure)) Equal to TargetingEffectDummy
    • Actions
      • -------- this is needed to not hear the building loop sound: --------
      • Unit - Move (Constructing structure) instantly to OutOfBounds
      • -------- this is to get rid of the building: --------
      • Unit - Remove (Constructing structure) from the game

dealing with the building placement sound:
So far so good, but you will notice that you still hear the building placement noise while you are casting your spell. And although this is probably the most satisfying noise in the whole game, we still don’t want it here. lets get rid of it:


1. In the world editor go to the Advanced tab -> game interface. On the top left hit the checkbox for “use custom game interface”. now scroll down to Sound-place building and set the value of that to an empty sound file. I used “FemaleCentaurPissed”.


Now that we have removed the building placement sound from the whole game, we have to add it back in where we do want it, AKA - everywhere else:

2. Go to the sound editor to Sounds/Internal/Sound/Buildings/Shared/BuildingPlacement.flac , right click it and select use as sound.

Now we will manually play this when placing any other building. In order to do this we basically catch all point orders in the game, and test if the order string is equal to that of placing a building. Don’t worry, I (mostly chatGPT to be honest) did the annoying part for you and here are 3 simple triggers you can stick in your map
:
a. the basic jass function that checks all point target orders for building order strings. just copy this into your map.
I don't really ever use jass, but this kind of thing is just too painful to do not in code. So I squeezed chatGPT untill the following jass came out. it works but I'm sure it's not pretty. I would be happy to replace it with a better version if anybody wants to step in...
:

JASS:
function CheckBuildingOrder takes nothing returns nothing
 
    local unit u = GetTriggerUnit()
    local location orderPoint = GetOrderPointLoc()
    local string issuedOrderString = OrderId2StringBJ(GetIssuedOrderIdBJ())
    local integer i = 0
    local string array buildingList

    //add to this number if you add more buildings to the list (should be one more than the last number):
    local integer buildingAmount = 45

    //uncomment this to find the accurate order string:
    //call DisplayTextToForce( GetPlayersAll(), issuedOrderString )

    // Initialize the building list
    set buildingList[0] = "townhall"
    set buildingList[1] = "farm"
    set buildingList[2] = "humanbarracks"
    set buildingList[3] = "humanlumbermill"
    set buildingList[4] = "blacksmith"
    set buildingList[5] = "arcanevault"
    set buildingList[6] = "scouttower"
    set buildingList[7] = "arcaneTower"
    set buildingList[8] = "guardTower"
    set buildingList[9] = "cannonTower"
    set buildingList[10] = "altarofkings"
    set buildingList[11] = "workshop"
    set buildingList[12] = "gryphonaviary"
    set buildingList[13] = "ancientofwar"
    set buildingList[14] = "ancientoflore"
    set buildingList[15] = "ancientofwind"
    set buildingList[16] = "moonwell"
    set buildingList[17] = "treeoflife"
    set buildingList[18] = "huntershall"
    set buildingList[19] = "chimaeraroost"
    set buildingList[20] = "altarofelders"
    set buildingList[21] = "boneyard"
    set buildingList[22] = "graveyard"
    set buildingList[23] = "ziggurat"
    set buildingList[24] = "crypt"
    set buildingList[25] = "templeofthedamned"
    set buildingList[26] = "slaughterhouse"
    set buildingList[27] = "altarofdarkness"
    set buildingList[28] = "necropolis"
    set buildingList[29] = "greathall"
    set buildingList[30] = "warmill"
    set buildingList[31] = "watchtower"
    set buildingList[32] = "spiritlodge"
    set buildingList[33] = "voodooulounge"
    set buildingList[34] = "beastiary"
    set buildingList[35] = "altarofstorms"
    set buildingList[36] = "orcburrow"
    set buildingList[37] = "taurentotem"
    set buildingList[38] = "arcanesanctum"
    set buildingList[39] = "tombofrelics"
    set buildingList[40] = "sacrificialpit"
    set buildingList[41] = "orcbarracks"
    set buildingList[42] = "trollburrow"
    set buildingList[43] = "voodoolounge"
    set buildingList[44] = "ancientofwonders"

 

 
    // Loop to check the issued order
    loop
        exitwhen i >= buildingAmount
        if (SubString(issuedOrderString, 0, StringLength(buildingList[i])) == buildingList[i]) then
            call PlaySoundAtPointBJ(gg_snd_BuildingPlacement, 100, orderPoint,0)
            return
        endif
        set i = i + 1
    endloop
    call RemoveLocation(orderPoint)  // Clean up location memory leak
endfunction

function InitBuildingCheckTrigger takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
    call TriggerAddAction(t, function CheckBuildingOrder)
 
endfunction

function InitTrig_BuildingCheck takes nothing returns nothing
    call InitBuildingCheckTrigger()
endfunction

b. initializing the above function :
  • added init
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Custom script: call InitTrig_BuildingCheck()
The above should currently cover all melee buildings, and by extension – any buildings created by copying them that still use their order strings, however – if you want to add a building that isn't based off of a melee building it’s easy -> just follow these steps:

Uncomment (remove the slashes from) this line of code:
  • //call DisplayTextToForce( GetPlayersAll(), issuedOrderString )
Now, when you build your building it will display the order string on the screen. Usually it is just the name of that building with no spaces, but there are exceptions. Once you know the order string go back to the code and make these 3 simple changes:

1.Restore the slashes to comment out that debug line.
2.Add your building’s build order string that you found out using the previous step to the bottom of the building list – like:
set buildingList[45] = "centaurhut"
3. Bump this number up by one -> local integer buildingAmount = 45 (you would just need to change the 45 to 46)

That’s it.

c.this one last trigger is just to sort out placing mines for undead, which are unique (I think) in being a building where you target a unit to place the building.

  • haunting goldmine
    • Events
      • Unit - A unit Is issued an order targeting an object
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to Acolyte
      • (Unit-type of (Target unit of issued order)) Equal to Gold Mine
      • (Issued order) Not equal to (Order(move))
      • (Issued order) Not equal to (Order(smart))
    • Actions
      • Sound - Play BuildingPlacement <gen> at 100.00% volume, attached to (Target unit of issued order)

And that's it, you're done. you can now have many different custom spell targeting images in your map.

As I mentioned in the intro – this method is only useable in this form for one spell per units since all the instant building abilities share an ability ID. So just tell your casters not to be greedy, and just give out the spells one per person.

Independent of creating custom spell indicators, this method can also be used to simplify the process of determining which type of terrain you are casting on.


That’s all folks.

(there weren't enough numbered lists right? I thought so too...)

thanks to @ThompZon for this custom indicator model I used in the testmap.
 

Attachments

  • customTargetingImage.w3m
    29 KB · Views: 12
Last edited:
Top