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

[Trigger] Help on a special AOE charm spell

Status
Not open for further replies.
Level 4
Joined
Apr 22, 2020
Messages
53
Hi,

I tried to find a way with charm and other mindcontrol spell threads to manage my trigger but I think i drowned a bit.

Ok, so, my goal is to make a special charm AOE. "Special" because it just orders ennemies to follow the caster and their owners can't do anything against.

So, I tried to use Index for the first time... And I think it didn't go well...

Here's the baby :


As you see, I turned the group to neutral in order not to attack the nymph and just follow her.

It didn't work at all. I can't really figure out what I'm doing wrong... ":D
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
You cannot use a Wait like that inside of a Pick Every Unit function. And even if you could, Picked unit will have changed after the 5.00 seconds. It most likely wouldn't be referencing the same unit you originally picked.

Also, you don't need the AND in your conditions. Conditions need to ALL be true by default so it's redundant using AND.

Instead you can do this:
  • Cast Charm
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to CHARM
    • Actions
      • Set VariableSet Charm_Point = (Position of (Triggering unit))
      • Custom script: set bj_wantDestroyGroup = true
      • Unit Group - Pick every unit in (Units within 512.00 of Charm_Point.) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ((Picked unit) belongs to an enemy of (Triggering player).) Equal to True
              • ((Picked unit) is alive) Equal to True
              • ((Picked unit) is A structure) Equal to False
            • Then - Actions
              • Trigger - Run Charm Unit <gen> (ignoring conditions)
            • Else - Actions
      • Custom script: call RemoveLocation (udg_Charm_Point)
Note that "Custom script: set bj_wantDestroyGroup = true" is an alternate method to "call DestroyGroup". Both work fine, but I like this way better because you don't need to create a Unit Group variable. You simply put "set bj_wantDestroyGroup" before your Pick Every Unit function and it will automatically destroy it when it's finished using it.

This next trigger uses a trick that you can read about here local udg_
It basically turns a Global variable into a Local variable, which prevents the variable from changing. So in this case Charm_Owner won't change even if you run this trigger multiple times for different units owned by different players. Each unit will return to it's original owner.
  • Charm Unit
    • Events
    • Conditions
    • Actions
      • Custom script: local player udg_Charm_Owner = GetOwningPlayer(GetEnumUnit())
      • Unit - Change ownership of (Picked unit) to Neutral Passive and Change color
      • Unit - Order (Picked unit) to Follow (Triggering unit)
      • Special Effect - Create a special effect attached to the origin of (Picked unit) using Abilities\Spells\Other\Charm\CharmTarget.mdl
      • Special Effect - Destroy (Last created special effect)
      • Wait 5.00 seconds
      • Unit - Change ownership of (Picked unit) to Charm_Owner and Change color
      • Custom script: set udg_Charm_Owner = null
Edit: Forgot this line in the Charm Unit trigger: Custom script: set udg_Charm_Owner = null
This is needed to clean up a small memory leak.
 

Attachments

  • Charm.w3m
    18.8 KB · Views: 23
Last edited:
Level 4
Joined
Apr 22, 2020
Messages
53
Thanks a lot ! It seems perfect !

I wanted to try it but there are some points I didn't understand :

1. Set VariableSet Charm_Point : Is it different than a normal variable ? In mine I have "Set Charm_Point" how do you get "variableset" ?

2. In the second trigger, "Charm Unit", the first line refers to which variables ? I didn't set Charm_Owner or GetOwningPlayer, neither GetEnumUnit. How I set them to have it in the line : "Unit - Change ownership of (Picked Unit) to Charm_Owner..." ?

3. I can't open your map "Charm.w3m" It told me I can't open it cause I have missing data or invalid. (And maybe there is the answer to all my questions in it lol)

EDIT : Ok I know why : I'm on the old version of worldeditor lol. I opened it perfectly with reforged

Ok I will always use "set bj_wantDestroyGroup" I didn't know the difference between both functions before and now I agree with you :)
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
1) Reforged screwed up the wording. It's the same as "Set variable..."

2) This:
  • Custom script: local player udg_Charm_Owner = GetOwningPlayer(GetEnumUnit())
^Is basically the same exact thing as this:
  • Set VariableSet Charm_Owner = (Owner of (Picked unit))
GetEnumUnit() = Picked unit
GetOwningPlayer() = The owner of the unit that you put in the parentheses (Picked unit in this case)

Custom Script allows you to use the code that the game is written in. This code is called vJass (formerly known as Jass).


To understand this better, you should create a trigger like this:
  • Untitled Trigger 001
    • Events
    • Conditions
    • Actions
      • Set VariableSet Charm_Owner = (Owner of (Picked unit))
Then with the Trigger selected, go to Edit -> Convert to Custom Text. This will turn the Trigger into the code that the game is written in. Which is the same thing that Custom Script uses.

So that's the reason why you see Custom Script being used all of the time in triggers. GUI (The Trigger Editor Interface that uses Events/Conditions/Actions) is actually missing features that are only accessible if you write in code. Custom Script is useful because you don't actually have to convert your triggers to code to take advantage of these missing features. It allows you to basically make hybrid triggers that use mostly GUI but have some code (Custom Script) here and there.

In my triggers case, i'm taking advantage of local variables, a feature that is restricted to code. But with Custom Script and some other tricks i'm able to use it in my GUI trigger as well.
 
Last edited:
Level 4
Joined
Apr 22, 2020
Messages
53
Effect detail :

I wanted to add a "buff effect" during the spell, but I didn't make it right. It only has been removed on only one unit.

I tried this :


I saw on forum a lot using index but I still don't get how to use it lol
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
So you're trying to use a Global variable to keep track of the Special Effects, but a global variable can only be assigned to 1 thing at a time (if it doesn't use an Array).

So with your current trigger if you cast Charm on 4 Footman for example, Charm_Buff will be set 4 separate times (once for each Footman). So only the very LAST special effect you create will be assigned as Charm_Buff.

Footman #1: Create a special effect <- Set Charm_Buff as last created special effect
Footman #2: Create a special effect <- Set Charm_Buff as last created special effect
Footman #3: Create a special effect <- Set Charm_Buff as last created special effect
Footman #4: Create a special effect <- Set Charm_Buff as last created special effect

Remember, Charm_Buff can only be Set to ONE thing at a time. So only the very last created special effect will be set as Charm_Buff, which in this example is Footman #4. That's why it only worked for 1 of your units when you tested it.

This is why I was using that "local" Custom Script method in my trigger. It turns Global Variables into Local variables so that the problem above doesn't happen. Using that same concept, here's an easy solution:
  • Charm Unit
    • Events
    • Conditions
    • Actions
      • Custom script: local player udg_Charm_Owner
      • Custom script: local effect udg_Charm_Buff
      • -------- --------
      • Set VariableSet Charm_Owner = (Owner of (Picked unit))
      • Unit - Change ownership of (Picked unit) to Neutral Passive and Change color
      • Unit - Order (Picked unit) to Follow (Triggering unit)
      • -------- --------
      • Special Effect - Create a special effect attached to the origin of (Picked unit) using Abilities\Spells\Other\Charm\CharmTarget.mdl
      • Special Effect - Destroy (Last created special effect)
      • -------- --------
      • Special Effect - Create a special effect attached to the overhead of (Picked unit) using Abilities\Spells\NightElf\FaerieFire\FaerieFireTarget.mdl
      • Set VariableSet Charm_Buff = (Last created special effect)
      • -------- --------
      • Wait 5.00 seconds
      • -------- --------
      • Special Effect - Destroy Charm_Buff
      • Unit - Change ownership of (Picked unit) to Charm_Owner and Change color
      • -------- --------
      • Custom script: set udg_Charm_Owner = null
      • Custom script: set udg_Charm_Buff = null
Note that I made some additional changes to the Charm Unit trigger. Instead of setting "local player udg_Charm_Owner = GetOwningPlayer(GetEnumUnit())", I left the end part blank. THEN I set Charm_Owner in the line below it to Owner of Picked unit. This works exactly the same as before but now you don't need to write "GetOwningPlayer(GetEnumUnit())" and can set the variables the normal GUI way.

The solution I provided to you doesn't use Indexing but that doesn't mean that Indexing isn't useful. it's definitely useful and in a lot of cases it's required for more complex triggers.
Indexing takes advantage of Arrays to create multiple versions of a single variable.

So if you're ever creating multiple versions of a variable like this: Player1Hero, Player2Hero, Player3Hero, etc... you should know that using an Array would make this a MUCH easier process as you can achieve this with a single variable. It also allows you to make many shortcuts for your triggers that reference these variables.

Now here's two ways of going about creating a Unit variable that keeps track of each player's Hero (you'll see this in Arena games or games that you only control 1 Hero):

BAD METHOD:
This takes 3+ variables:
Player1Hero = player 1's hero, Player2Hero = player 2's hero, Player3Hero = player 3's hero, etc...

GOOD METHOD:
This takes 1 variable with an Array []:
PlayerHero[1] = player 1's hero, PlayerHero[2] = player 2's hero, PlayerHero[3] = player 3's hero, etc...

So the [Index] acts as it's own unique variable, meaning, if you kill PlayerHero[2] it will kill player 2's hero but not mess with the other player's heroes.

Now here's why Indexing is so useful.
Compare these two triggers, the first one uses the BAD method and the other one uses the GOOD method. This trigger will kill the Player's hero when they type -kill:
  • Bad Example
    • Events
      • Player - Player 1 (Red) types a chat message containing -kill as An exact match
      • Player - Player 2 (Blue) types a chat message containing -kill as An exact match
      • Player - Player 3 (Teal) types a chat message containing -kill as An exact match
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Triggering player) Equal to Player 1 (Red)
        • Then - Actions
          • Unit - Kill PlayerHero1
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Triggering player) Equal to Player 2 (Blue)
            • Then - Actions
              • Unit - Kill PlayerHero2
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Triggering player) Equal to Player 3 (Teal)
                • Then - Actions
                  • Unit - Kill PlayerHero3
                • Else - Actions
Now look at how much easier this one is.
Each player has a Player Number that you can reference at any given time. Player 1's number = 1, Player 2's number = 2, Player 3's number = 3, etc...
  • Good Example
    • Events
      • Player - Player 1 (Red) types a chat message containing -kill as An exact match
      • Player - Player 2 (Blue) types a chat message containing -kill as An exact match
      • Player - Player 3 (Teal) types a chat message containing -kill as An exact match
    • Conditions
    • Actions
      • Unit - Kill PlayerHero[(Player number of (Triggering player))]
So the trigger will put the player number of whoever typed -kill (1, 2, 3, etc...) inside of the Index.
Since we assigned PlayerHero[1] to player 1's hero, and we know that Player 1's Number = 1, this will kill player 1's hero. It will also kill the correct hero for Player 2, and Player 3.
This is just one way that you can take advantage of Arrays to make your triggers more efficient.

But not every situation is that simple that you can just plug the Player's Number into the Index and have it work.

Here's that concept applied to the Charm_Buff variable:
  • Actions
    • Special Effect - Create a special effect attached to the overhead of (Picked unit) using Abilities\Spells\NightElf\FaerieFire\FaerieFireTarget.mdl
    • Set VariableSet Charm_Buff[1] = (Last created special effect)
    • Wait 8.00 seconds
    • Special Effect - Destroy Charm_Buff[1]
But how is this useful? Well, in it's current form it isn't useful but with some special techniques we can make this work.

So now all that's left to do is make the [Index] unique to the Charmed unit. This way each Charmed unit will have it's own Index [] for the Charm_Buff special effect.

This is where systems like these come in handy:
GUI Unit Indexer 1.4.0.0
Visualize: Dynamic Indexing

Here's our trigger from above but this time taking advantage of a Unit Indexer:
  • Unit Indexer Method
    • Events
    • Conditions
    • Actions
      • Special Effect - Create a special effect attached to the overhead of (Picked unit) using Abilities\Spells\NightElf\FaerieFire\FaerieFireTarget.mdl
      • Set VariableSet Charm_Buff[(Custom value of (Picked unit))] = (Last created special effect)
      • Wait 8.00 seconds
      • Special Effect - Destroy Charm_Buff[(Custom value of (Picked unit))]
The Unit Indexer assigns each unit a unique Custom Value, this is an Integer stored to the unit. No two units will ever have the same Custom Value so you'll always be putting a unique # inside the [Index] for Charm_Buff.

So with Indexing [] we can do all sorts of tricks to make this work.
 

Attachments

  • Charm 2.w3m
    19 KB · Views: 23
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
I'd actually recommend steering away from local variables in GUI. It's fine when used correctly but it's not a perfect solution and has limitations.

Indexing is an overall better method to use because you won't run into any issues when done properly. The main reason I used this local variable setup is because of the simple nature of your ability. When it's simple like this, the local variable trick works wonders, but it shouldn't be something you rely on all of the time.

That being said, here's a list of the names of the local variable types. Most have the same name as their Global Variable counterparts.

The important ones that you'll use most of the time: player, unit, item, ability, effect, location
vJASS:
Ignore everything besides the NAME column.
     NAME
type event             extends    handle  // a reference to an event registration
type player            extends    handle  // a single player reference
type widget            extends    handle  // an interactive game object with life
type unit              extends    widget  // a single unit reference
type destructable      extends    widget
type item              extends    widget
type ability           extends    handle
type buff              extends    ability
type force             extends    handle
type group             extends    handle
type trigger           extends    handle
type triggercondition   extends    handle
type triggeraction     extends    handle
type timer             extends    handle
type location          extends    handle
type region            extends    handle
type rect              extends    handle
type boolexpr          extends    handle
type sound             extends    handle
type conditionfunc     extends    boolexpr
type filterfunc        extends    boolexpr
type unitpool          extends    handle
type itempool          extends    handle
type race              extends    handle
type alliancetype      extends    handle
type racepreference    extends    handle
type gamestate         extends    handle
type igamestate        extends    gamestate
type fgamestate        extends    gamestate
type playerstate       extends    handle
type playerscore       extends    handle
type playergameresult   extends    handle
type unitstate         extends    handle
type aidifficulty      extends    handle
type eventid           extends    handle
type gameevent         extends    eventid
type playerevent       extends    eventid
type playerunitevent   extends    eventid
type unitevent         extends    eventid
type limitop           extends    eventid
type widgetevent       extends    eventid
type dialogevent       extends    eventid
type unittype          extends    handle
type gamespeed         extends    handle
type gamedifficulty    extends    handle
type gametype          extends    handle
type mapflag           extends    handle
type mapvisibility     extends    handle
type mapsetting        extends    handle
type mapdensity        extends    handle
type mapcontrol        extends    handle
type playerslotstate   extends    handle
type volumegroup       extends    handle
type camerafield       extends    handle
type camerasetup       extends    handle
type playercolor       extends    handle
type placement         extends    handle
type startlocprio      extends    handle
type raritycontrol     extends    handle
type blendmode         extends    handle
type texmapflags       extends    handle
type effect            extends    handle
type effecttype        extends    handle
type weathereffect     extends    handle
type terraindeformation extends    handle
type fogstate          extends    handle
type fogmodifier       extends    handle
type dialog            extends    handle
type button            extends    handle
type quest             extends    handle
type questitem         extends    handle
type defeatcondition   extends    handle
type timerdialog       extends    handle
type leaderboard       extends    handle
type multiboard        extends    handle
type multiboarditem    extends    handle
type trackable         extends    handle
type gamecache         extends    handle
type version           extends    handle
type itemtype          extends    handle
type texttag           extends    handle
type attacktype        extends    handle
type damagetype        extends    handle
type weapontype        extends    handle
type soundtype         extends    handle
type lightning         extends    handle
type pathingtype       extends    handle
type image             extends    handle
type ubersplat         extends    handle
group is Unit Group in GUI
force is Player Group in GUI
destructable is Destructible in GUI
effect is Special Effect in GUI
location is Point in GUI
rect is Region in GUI

There's probably a couple of variables missing from the list as I found this from a post from 2007, but all of the important variable types are there.
 
Status
Not open for further replies.
Top