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

Spell for a unit group (trigger)

Level 10
Joined
Jun 9, 2020
Messages
86
Greetings, fellow map makers.

I'm trying to create a spell, that would make units invulnerable for ten seconds and take the half of their HP. (If a unit has less than 50% of his HP - he'll die). In order to make this work for many units simultaneously, I created a group. However, after the spell is applied, it makes a targeted unit invulnerable but it does not become vulnerable ten seconds again. What am I doing wrong here?

Screenshot (943).png


P.S: I've searched for a solution here, but many of treads are outdated.

Best wishes,
Akula
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,201
You cannot use GUI wait inside pick every group loops. This is because TriggerSleepAction is not supported inside pick every group loop callback functions, and causes a thread crash at point of execution.

What you want to do is store the group of affected units, make them all invulnerable with a pick every loop, wait 5 seconds outside the loop, and then make them all vulnerable and remove the appropriate life with another pick every group loop.

You might want to cause nearby enemy units to deal fatal damage to units below 50% current life. This is to prevent a kill credit exploit where a player could purposely use the ability to deny enemies kill credit (gold, exp, e.t.c.). Similar to minion kill blocking in DotA.
 
Level 10
Joined
Jun 9, 2020
Messages
86
You cannot use GUI wait inside pick every group loops. This is because TriggerSleepAction is not supported inside pick every group loop callback functions, and causes a thread crash at point of execution.

What you want to do is store the group of affected units, make them all invulnerable with a pick every loop, wait 5 seconds outside the loop, and then make them all vulnerable and remove the appropriate life with another pick every group loop.

You might want to cause nearby enemy units to deal fatal damage to units below 50% current life. This is to prevent a kill credit exploit where a player could purposely use the ability to deny enemies kill credit (gold, exp, e.t.c.). Similar to minion kill blocking in DotA.
It worked! Thank you.

But my next problem are special effects, that appear after casting the spell. I have to add them to a store. However, I need arrays this time. I checked the tutorials, but I'm still not sure, which of the variables (effect with/no array and an integer with/no array) and what order to use.

Screenshot (944).png


Best wishes,
Max
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,201
But my next problem are special effects, that appear after casting the spell. I have to add them to a store. However, I need arrays this time. I checked the tutorials, but I'm still not sure, which of the variables (effect with/no array and an integer with/no array) and what order to use.
Turn the array into an array list. Use another global integer variable to track the current end of the list. Every time you add an effect to it, increment this variable by 1. At the end of the ability you loop from the first index to the end of list variable, destroying the effects.
 
Level 10
Joined
Jun 9, 2020
Messages
86
I kinda get it. But how can I connect an effect and an integer variable? On the picture the red one is an array with size. The blue is a normal integer. It's still not working.

Problem.png


Or did I understand you wrong? Am I going to have to use the JASS from this tutorial and this tutorial instead? Oh, Godd*** it! I guess, I'll have to read the flipping manual. Last question: when the code is ready, may I post it here, so somebody would correct it if necessary?

Best wishes,
Max
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,201
I kinda get it. But how can I connect an effect and an integer variable? On the picture the red one is an array with size. The blue is a normal integer. It's still not working.
The array is an array of effects that you save the effect to. The integer keeps track of the index you are saving effects to.

It should look something like...
The following global variables exist...
Code:
Integer EffectListEnd with initial value 0
SpecialEffect Array EffectList with size 1

At the start of the trigger...
Code:
Set EffectListEnd = 1

In the loop just after creating an effect...
Code:
Set EffectList[EffectListEnd] = (Last created special effect)
Set EffectListEnd = (EffectListEnd + 1)

Then after the 15 seconds it should look something like...
Code:
For each Integer A from 1 to EffectListEnd, do (Actions)
    Special Effect - Destroy EffectList[(Integer A)]
 
Level 10
Joined
Jun 9, 2020
Messages
86
The array is an array of effects that you save the effect to. The integer keeps track of the index you are saving effects to.

It should look something like...
The following global variables exist...
Code:
Integer EffectListEnd with initial value 0
SpecialEffect Array EffectList with size 1

At the start of the trigger...
Code:
Set EffectListEnd = 1

In the loop just after creating an effect...
Code:
Set EffectList[EffectListEnd] = (Last created special effect)
Set EffectListEnd = (EffectListEnd + 1)

Then after the 15 seconds it should look something like...
Code:
For each Integer A from 1 to EffectListEnd, do (Actions)
    Special Effect - Destroy EffectList[(Integer A)]
If you want, I can send you some cash per Pay Pal. You've like done a whole job for me.
 
Level 10
Joined
Jun 9, 2020
Messages
86
The array is an array of effects that you save the effect to. The integer keeps track of the index you are saving effects to.

It should look something like...
The following global variables exist...
Code:
Integer EffectListEnd with initial value 0
SpecialEffect Array EffectList with size 1

At the start of the trigger...
Code:
Set EffectListEnd = 1

In the loop just after creating an effect...
Code:
Set EffectList[EffectListEnd] = (Last created special effect)
Set EffectListEnd = (EffectListEnd + 1)

Then after the 15 seconds it should look something like...
Code:
For each Integer A from 1 to EffectListEnd, do (Actions)
    Special Effect - Destroy EffectList[(Integer A)]
Hello. It's me again. I've come this far with the spell code. I used an existing spell and made it harm its target unit or kill it (depends, if it has less or more than 50% of its HP). And then I created global variables and the loop, as you've advised. However, it shows me this syntax error: unexpected "EffectList"?

If it's easy to make this right, you can write it here;
BUT
I it's not, than I ask you to write the while code of the spell. I could pay for it a little, if you want.

Best wishes, Max
 

Attachments

  • MySpell.png
    MySpell.png
    21.6 KB · Views: 10

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,201
However, it shows me this syntax error: unexpected "EffectList"?
"specialEffect" is not a valid JASS type. You probably meant to use effect.

"with initial value 0" is not valid JASS code. You need to write the initial value to the array indices before you use them, but in your case this is not needed since they have an uninitialized value of 0 already.

"with size 1" is not valid JASS code. JASS arrays are dynamic arrays that will automatically allocate space up the the array maximum size constant.

"Set" is not valid JASS. Like a lot of programming languages, JASS is case sensitive. To set a variable you start the line with set.

"(Last created special effect)" is not valid JASS. You either assign the variable to the effect when you create it, or you need to call the function that GUI wraps GetLastCreatedEffectBJ or reference the variable GUI uses internally, bj_lastCreatedEffect, directly.

I do not think calling TriggerSleepAction in the initialization thread is allowed. It is likely to cause a thread crash or unexpected behaviour such as delaying the initialization of unrelated triggers.

"For each Integer A from 1 to EffectListEnd, do (Actions)" is not valid JASS. JASS does not support for loops, you need to emulate them using a conventional loop. This is done by starting the loop with loop, applying the end condition check at the top of the loop body with an exitwhen boolean statement where boolean is replaced with your test logic, and then perform the increment logic to your variable at the bottom of the loop body.

"Special Effect - Destroy" is not valid JASS. You probably meant to call DestroyEffect.

"(Integer A)" is not valid JASS. Reference the variable used internally from the extract below.
Code:
    // Utility function vars
    integer            bj_forLoopAIndex            = 0
    integer            bj_forLoopBIndex            = 0
    integer            bj_forLoopAIndexEnd         = 0
    integer            bj_forLoopBIndexEnd         = 0
Hello. It's me again. I've come this far with the spell code. I used an existing spell and made it harm its target unit or kill it (depends, if it has less or more than 50% of its HP). And then I created global variables and the loop, as you've advised.
If you want to use GUI, then stick to using GUI. Otherwise you will need to write everything in JASS. JASS does not understand English text form of GUI, that is not valid JASS code.

Your spell logic does not really make sense. You are not creating any special effects and instead are trying setup and destroy a list once only at map initialisation after stalling the entire map initialisation thread for approximately 10 real time seconds. You probably want to process the special effects in the action function body.
 
Last edited:
Level 10
Joined
Jun 9, 2020
Messages
86
"specialEffect" is not a valid JASS type. You probably meant to use effect.

"with initial value 0" is not valid JASS code. You need to write the initial value to the array indices before you use them, but in your case this is not needed since they have an uninitialized value of 0 already.

"with size 1" is not valid JASS code. JASS arrays are dynamic arrays that will automatically allocate space up the the array maximum size constant.

"Set" is not valid JASS. Like a lot of programming languages, JASS is case sensitive. To set a variable you start the line with set.

"(Last created special effect)" is not valid JASS. You either assign the variable the the effect when you create it, or you need to call the function that GUI wraps GetLastCreatedEffectBJ or reference the variable GUI uses internally, bj_lastCreatedEffect, directly.

I do not think calling TriggerSleepAction in the initialization thread is allowed. It is likely to cause a thread crash or unexpected behaviour such as delaying the initialization of unrelated triggers.

"For each Integer A from 1 to EffectListEnd, do (Actions)" is not valid JASS. JASS does not support for arrays, you need to emulate them using a conventional loop. This is done by starting the loop with loop, applying the end condition check at the top of the loop body with an exitwhen boolean statement where boolean is replaced with your test logic, and then perform the increment logic to your variable at the bottom of the loop body.

"Special Effect - Destroy" is not valid JASS. You probably meant to call DestroyEffect.

"(Integer A)" is not valid JASS. Reference the variable used internally from the extract below.
Code:
    // Utility function vars
    integer            bj_forLoopAIndex            = 0
    integer            bj_forLoopBIndex            = 0
    integer            bj_forLoopAIndexEnd         = 0
    integer            bj_forLoopBIndexEnd         = 0

If you want to use GUI, then stick to using GUI. Otherwise you will need to write everything in JASS. JASS does not understand English text form of GUI, that is not valid JASS code.

Your spell logic does not really make sense. You are not creating any special effects and instead are trying setup and destroy a list once only at map initialisation after stalling the entire map initialisation thread for approximately 10 real time seconds. You probably want to process the special effects in the action function body.
Thank you.
 
Level 39
Joined
Feb 27, 2007
Messages
5,016
You seem to be struggling. There is no reason to try to create a bunch of timed effects that you have to keep track of when you can simply use a buff to do it for you. Create some generic buff ability that doesn't do anything (other than give the buff for 10 seconds), have a dummy caster apply it to the targeted unit, and then check all units with that buff periodically to remove invulnerability from units whose buff has expired. This will work with not only multiple simultaneous casts but also multiple on the same target (if this spell can target invulnerable units).
  • Events
    • Unit - A unit starts the effect of an ability
  • Conditions
    • (Ability being cast) equal to Unholy Armor
  • Actions
    • Set UA_Target = (Target unit of ability being cast)
    • Set TempPoint = (Position of UA_Target)
    • Unit - Create 1 DUMMY CASTER UNIT for (Owner of UA_Target) at TempPoint facing (Default building facing) degrees
    • Set UA_Dummy = (Last created unit)
    • Unit - Add YOUR BUFF ABILITY to UA_Dummy
    • Unit - Set level of YOUR BUFF ABILITY for UA_Dummy to (Level of (Ability being cast) for (Triggering Unit))
    • Unit - Add a 1.00 second generic expiration timer to UA_Dummy
    • Set UA_Pct = ((Percentage life of UA_Target) - 50.00)
    • If (All conditions are true) then do (Then actions) else do (Else actions)
      • If - Conditions
        • UA_Pct less than or equal to 0.00
      • Then - Actions
        • Custom script: set bj_wantDestroyGroup = true
        • Set UA_Dummy = (Random unit from (Units within 1000.00 of TempPoint matching (((Matching Unit) belongs to an enemy of (Owner of UA_Target)) equal to True)))
        • If (All conditions are true then do (Then actions) else do (Else actions)
          • If - Conditions
            • UA_Dummy equal to (No unit)
          • Then - Actions
            • Set UA_Dummy = (Triggering Unit)
          • Else - Actions
        • Unit - Set life of UA_Target to 1.00
        • Unit - Cause UA_Dummy to damage UA_Target, dealing 100.00 damage of attack type Chaos and damage type Universal
      • Else - Actions
        • Unit - Remove Invulnerable (Neutral) from UA_Target
        • Unit - Order UA_Dummy to THE CORRECT ORDER FOR YOUR BUFF ABILITY UA_Target
        • Unit - Set life of UA_Target to UA_Pct%
        • Unit - Add Invulnerable (Neutral) to UA_Target
        • Unit Group - Add UA_Target to UA_Group
    • Custom script: call RemoveLocation(udg_TempPoint)
  • Events
    • Time - Every 0.25 seconds of game-time
  • Conditions
  • Actions
    • Unit Group - Pick every unit in UA_Group and do (Actions)
      • Loop - Actions
        • Set UA_Target = (Picked unit)
        • If (All conditions are true then do (Then actions) else do (Else actions)
          • If - Conditions
            • (UA_Target has buff YOUR BUFF ABILITY'S BUFF) equal to False
          • Then - Actions
            • Unit - Remove Invulnerable (Neutral) from UA_Target
            • Unit Group - Remove UA_Target from UA_Group
          • Else - Actions
For this to work it is very important that your dummy unit is properly configured so that it can cast instantly without turning:
  • Movement speed base: 0
  • Movement type: NONE
  • Cast backswing: 0.00
  • Cast point: 0.00
  • Unit ability: Locust ('aloc')
  • Remove everything that might allow a player to know that the unit exists, like model/shadow/attacks/minimap/icons/classifications
 
Level 10
Joined
Jun 9, 2020
Messages
86
You seem to be struggling. There is no reason to try to create a bunch of timed effects that you have to keep track of when you can simply use a buff to do it for you. Create some generic buff ability that doesn't do anything (other than give the buff for 10 seconds), have a dummy caster apply it to the targeted unit, and then check all units with that buff periodically to remove invulnerability from units whose buff has expired. This will work with not only multiple simultaneous casts but also multiple on the same target (if this spell can target invulnerable units).
  • Events
    • Unit - A unit starts the effect of an ability
  • Conditions
    • (Ability being cast) equal to Unholy Armor
  • Actions
    • Set UA_Target = (Target unit of ability being cast)
    • Set TempPoint = (Position of UA_Target)
    • Unit - Create 1 DUMMY CASTER UNIT for (Owner of UA_Target) at TempPoint facing (Default building facing) degrees
    • Set UA_Dummy = (Last created unit)
    • Unit - Add YOUR BUFF ABILITY to UA_Dummy
    • Unit - Set level of YOUR BUFF ABILITY for UA_Dummy to (Level of (Ability being cast) for (Triggering Unit))
    • Unit - Add a 1.00 second generic expiration timer to UA_Dummy
    • Set UA_Pct = ((Percentage life of UA_Target) - 50.00)
    • If (All conditions are true) then do (Then actions) else do (Else actions)
      • If - Conditions
        • UA_Pct less than or equal to 0.00
      • Then - Actions
        • Custom script: set bj_wantDestroyGroup = true
        • Set UA_Dummy = (Random unit from (Units within 1000.00 of TempPoint matching (((Matching Unit) belongs to an enemy of (Owner of UA_Target)) equal to True)))
        • If (All conditions are true then do (Then actions) else do (Else actions)
          • If - Conditions
            • UA_Dummy equal to (No unit)
          • Then - Actions
            • Set UA_Dummy = (Triggering Unit)
          • Else - Actions
        • Unit - Set life of UA_Target to 1.00
        • Unit - Cause UA_Dummy to damage UA_Target, dealing 100.00 damage of attack type Chaos and damage type Universal
      • Else - Actions
        • Unit - Remove Invulnerable (Neutral) from UA_Target
        • Unit - Order UA_Dummy to THE CORRECT ORDER FOR YOUR BUFF ABILITY UA_Target
        • Unit - Set life of UA_Target to UA_Pct%
        • Unit - Add Invulnerable (Neutral) to UA_Target
        • Unit Group - Add UA_Target to UA_Group
    • Custom script: call RemoveLocation(udg_TempPoint)
  • Events
    • Time - Every 0.25 seconds of game-time
  • Conditions
  • Actions
    • Unit Group - Pick every unit in UA_Group and do (Actions)
      • Loop - Actions
        • Set UA_Target = (Picked unit)
        • If (All conditions are true then do (Then actions) else do (Else actions)
          • If - Conditions
            • (UA_Target has buff YOUR BUFF ABILITY'S BUFF) equal to False
          • Then - Actions
            • Unit - Remove Invulnerable (Neutral) from UA_Target
            • Unit Group - Remove UA_Target from UA_Group
          • Else - Actions
For this to work it is very important that your dummy unit is properly configured so that it can cast instantly without turning:
  • Movement speed base: 0
  • Movement type: NONE
  • Cast backswing: 0.00
  • Cast point: 0.00
  • Unit ability: Locust ('aloc')
  • Remove everything that might allow a player to know that the unit exists, like model/shadow/attacks/minimap/icons/classifications
Yes, I am. Thank you very much! :peasant-cheers-back: And now I'm so happy I don't need to write those custom scripts - "Custom script: set bj_wantDestroyGroup = true" and "Custom script: call RemoveLocation(udg_TempPoint)" - by myself.

I'd also need another spell - Raise Ded - but it must work like in Warcraft I: the player will have to cast it by himself and also chose a corpse by himself to raise a skeleton from.
 
Level 39
Joined
Feb 27, 2007
Messages
5,016
And now I'm so happy I don't need to write those custom scripts by myself.
Things That Leak
You can learn yourself; it's not difficult or complicated.

Why is manually targeting a corpse necessary for you? WC3 Raise Dead automatically targets a corpse within range when cast for the same functionality.
 
Level 10
Joined
Jun 9, 2020
Messages
86
Things That Leak
You can learn yourself; it's not difficult or complicated.

Why is manually targeting a corpse necessary for you? WC3 Raise Dead automatically targets a corpse within range when cast for the same functionality.
Thank you!.. I want it to be more oldschoolish, like in Warcraft I.
 

Remixer

Map Reviewer
Level 31
Joined
Feb 19, 2011
Messages
1,957
I'd also need another spell - Raise Ded - but it must work like in Warcraft I: the player will have to cast it by himself and also chose a corpse by himself to raise a skeleton from.
Then you'd essentially want to create a dummy ability that lets you select a target area in which a dummy unit casts the Raise Dead ability. Unless you specifically want it to be point-and-click, which makes the issue a lot harder.
 
Top