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

Psionic Destruction v1.4

The caster is overflowed with electric energy that shocks nearby units with lightning. Damage per shock - 40/50/60
Shock interval - 0.2s
Duration - 10/12/14 seconds
AoE - 500/600/700
Cooldown - 3/2/1 seconds

  • Psionic Destruction Configuration
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Note: This is a configuration trigger, where you can edit your spell values. The numbers indicate the levels of the spell. --------
      • -------- Your ability --------
      • Set PD_Ability = Psionic Destruction
      • -------- Ability duration --------
      • Set PD_CDuration[1] = 10.00
      • Set PD_CDuration[2] = 12.00
      • Set PD_CDuration[3] = 14.00
      • -------- Damage --------
      • Set PD_Damage[1] = 40.00
      • Set PD_Damage[2] = 50.00
      • Set PD_Damage[3] = 60.00
      • -------- Time between instances --------
      • Set PD_Period = 0.20
      • -------- AoE --------
      • Set PD_AoE[1] = 500.00
      • Set PD_AoE[2] = 600.00
      • Set PD_AoE[3] = 700.00
      • -------- Damage type --------
      • Set PD_DamageType[1] = Normal
      • Set PD_DamageType[2] = Normal
      • Set PD_DamageType[3] = Normal
      • -------- Attack type --------
      • Set PD_AttackType[1] = Spells
      • Set PD_AttackType[2] = Spells
      • Set PD_AttackType[3] = Spells
      • -------- Special effect on caster for the duration --------
      • Set PD_CBuffSFX = Abilities\Spells\Orc\LightningShield\LightningShieldTarget.mdl
      • -------- Special effect used above, location (Examples: origin, chest, overhead) --------
      • Set PD_BuffLocation = origin
      • -------- Special effect for damage --------
      • Set PD_SpecialEffect[1] = Abilities\Weapons\Bolt\BoltImpact.mdl
      • Set PD_SpecialEffect[2] = Abilities\Weapons\Bolt\BoltImpact.mdl
      • Set PD_SpecialEffect[3] = Abilities\Weapons\Bolt\BoltImpact.mdl
      • -------- Can't touch this! --------
      • Trigger - Add to Psionic Destruction Loop <gen> the event (Time - Every PD_Period seconds of game time)
  • Psionic Destruction Initialization
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to PD_Ability
    • Actions
      • -------- This checks if the spell is already in action from another source. If it isn't, turns on the loop. --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • PD_MaxIndex Equal to 0
        • Then - Actions
          • Trigger - Turn on Psionic Destruction Loop <gen>
        • Else - Actions
      • -------- This adds another instance for the spell to be used. --------
      • Set PD_MaxIndex = (PD_MaxIndex + 1)
      • -------- Setting the caster. We use "Triggering Unit" instead of "Casting Unit" because it's faster. --------
      • Set PD_Caster[PD_MaxIndex] = (Triggering unit)
      • -------- Getting the current level of ability. --------
      • Set PD_AbilityIndex[PD_MaxIndex] = (Level of PD_Ability for PD_Caster[PD_MaxIndex])
      • -------- Setting the duration from the configuration trigger. --------
      • Set PD_Duration[PD_MaxIndex] = PD_CDuration[PD_AbilityIndex[PD_MaxIndex]]
      • -------- Setting the player which uses the ability. --------
      • Set PD_Player[PD_MaxIndex] = (Triggering player)
      • -------- Aaaand creating a special effect on the caster to make it fancy. --------
      • Special Effect - Create a special effect attached to the PD_BuffLocation of PD_Caster[PD_MaxIndex] using PD_CBuffSFX
      • Set PD_BuffSFX[PD_MaxIndex] = (Last created special effect)
  • Psionic Destruction Loop
    • Events
    • Conditions
    • Actions
      • For each (Integer PD_TempIndex) from 1 to PD_MaxIndex, do (Actions)
        • Loop - Actions
          • Set PD_Duration[PD_TempIndex] = (PD_Duration[PD_TempIndex] - PD_Period)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (PD_Caster[PD_TempIndex] is alive) Equal to True
              • PD_Duration[PD_TempIndex] Greater than 0.00
            • Then - Actions
              • -------- This gets the current position of the caster. --------
              • Set PD_TempPoint = (Position of PD_Caster[PD_TempIndex])
              • Set PD_Group = (Units within PD_AoE[PD_AbilityIndex[PD_TempIndex]] of PD_TempPoint)
              • -------- This selects every unit in previously created group. --------
              • Unit Group - Pick every unit in PD_Group and do (Actions)
                • Loop - Actions
                  • Set PD_PickedUnits = (Picked unit)
                  • -------- This checks if the unit is alive and an enemy of the caster. --------
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (PD_PickedUnits is alive) Equal to True
                      • (PD_PickedUnits belongs to an enemy of PD_Player[PD_TempIndex]) Equal to True
                    • Then - Actions
                    • Else - Actions
                      • -------- Removing the units from the group that didn't pass the conditions. --------
                      • Unit Group - Remove PD_PickedUnits from PD_Group
              • Set PD_RandomUnit = (Random unit from PD_Group)
              • Unit - Cause PD_Caster[PD_TempIndex] to damage PD_RandomUnit, dealing PD_Damage[PD_AbilityIndex[PD_TempIndex]] damage of attack type PD_AttackType[PD_AbilityIndex[PD_TempIndex]] and damage type PD_DamageType[PD_AbilityIndex[PD_TempIndex]]
              • Set PD_TempPoint2 = (Position of PD_RandomUnit)
              • -------- Nulling the random unit, otherwise, if there are no units around, it will damage the last randomed unit for the duration, regardless if he's in the AoE of the spell. --------
              • Set PD_RandomUnit = No unit
              • Special Effect - Create a special effect at PD_TempPoint2 using PD_SpecialEffect[PD_AbilityIndex[PD_TempIndex]]
              • -------- This destroys the effect. If it's not permanent, the special effect will get destroyed only when it's completed. --------
              • Special Effect - Destroy (Last created special effect)
              • -------- These custom scripts destroy the two points and the unit group we used previously. They need to be cleaned, otherwise, if they pile up, the game will start to lag. --------
              • Custom script: call DestroyGroup(udg_PD_Group)
              • Custom script: call RemoveLocation(udg_PD_TempPoint)
              • Custom script: call RemoveLocation(udg_PD_TempPoint2)
            • Else - Actions
              • -------- All these actions listed below are to clean all the variables we used in the spell. Once the spell is done, they become useless. --------
              • Special Effect - Destroy PD_BuffSFX[PD_TempIndex]
              • Set PD_BuffSFX[PD_TempIndex] = PD_BuffSFX[PD_MaxIndex]
              • Set PD_Duration[PD_TempIndex] = PD_Duration[PD_MaxIndex]
              • Set PD_Caster[PD_TempIndex] = PD_Caster[PD_MaxIndex]
              • Set PD_Player[PD_TempIndex] = PD_Player[PD_MaxIndex]
              • Set PD_AbilityIndex[PD_TempIndex] = PD_AbilityIndex[PD_MaxIndex]
              • Set PD_TempIndex = (PD_TempIndex - 1)
              • Set PD_MaxIndex = (PD_MaxIndex - 1)
              • -------- This checks if all the spells that have been in action are ended. If they are, turns off this loop. --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • PD_MaxIndex Equal to 0
                • Then - Actions
                  • Trigger - Turn off Psionic Destruction Loop <gen>
                • Else - Actions
The idea came in when a friend asked me to recreate Twilight's ORPG Psionic Destruction spell, thought I'd mention that.


v1.1 - Changed "Level of Psionic Destruction" to "Level of Cnfg_PD_Ability"
v1.2 - Removed obvious comments;
Merged two unit groups into one;
Fixed it lasting on the last randomed unit when there are no units around regardless if unit is in the AoE or not;
Fixed all instances' durations ending as soon as the first one ends;
Simplified some of the variables' names.
v1.3 - Replaced some functions with slightly faster ones, added a period configuration variable, reconstructed the duration reduction, again, thanks KILLCIDE.
v1.4 - Added a special effect on the caster for the duration of the ability.


Keywords:
Twilight, LITHUFFIN, MUI, GUI, Lightning
Contents

Psionic Destruction v1.4 (Map)

Reviews
8th Jan 2016 IcemanBo: With the effect on caster it looks better now. Can be useful. Psionic Destruction v1.1 | Reviewed by BPower | 01.08.2015 Concept[/COLOR]] "Death by electrocution" The concept itself is very simple, but I can see...

Moderator

M

Moderator

8th Jan 2016
IcemanBo: With the effect on caster it looks better now. Can be useful.


Psionic Destruction v1.1 | Reviewed by BPower | 01.08.2015

[COLOR="gray"

[COLOR="gray"

[COLOR="gray"

[COLOR="gray"

[COLOR="gray"

Concept[/COLOR]]
126248-albums6177-picture66521.png
"Death by electrocution"

The concept itself is very simple, but I can see this spell
beeing useful for some users. Good Job.
Code[/COLOR]]
126248-albums6177-picture66521.png
  • The spell is MUI, leakless and working. Very simple code.
126248-albums6177-picture66523.png
  • Place comments with reason. Not every line requires explanation.
  • Damagetype and Attacktype don't have to be arrays in my opinion.
  • Instead of using two groups, you could also extend your filter function and go only with one group. ( understood? )
Objects[/COLOR]]
126248-albums6177-picture66523.png
  • Object data seems to be ok.
Effects[/COLOR]]
126248-albums6177-picture66521.png
  • A simple lightning effect. Nothing special, but fits to the descriptions.
Rating[/COLOR]]
CONCEPTCODEOBJECTSEFFECTSRATINGSTATUS
2/5
3/5
4/5
2/5
2.5/5
APPROVED
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Easy concept of coding, but looks properly done. Good Job.

In future I would recommend to leave out comments which are obvious to get by the commented operation.
Only comment blocks which are harder to get at a glance or really important for you/user.

i.e. "This sets all picked units" is not a useful comment. ( the function unit == PickUnit is obvious )

Before approving I have to check the demo map. Overall approximatly a rating of ~3.

Cheers.
 
You leak unit group. The "set bj_wantDestroyGroup" does not help you here.
You can read it up here how to remove unit group properly. Feel free to ask in case.

Why do we need this:
  • Unit Group - Pick every unit in PD_Group2 and do (Actions)
    • Loop - Actions
      • -------- This picks a random unit from this group. --------
      • Set PD_RandomUnit = (Random unit from Group)
1. Why you randomize more than once?
2. You don't need the second unit group. You can remove all units from first unit group that don't pass the filter, and then take a random unit just from group1.

Fix the leak.
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
If you're setting PD_Group to a variable, you should just use this to remove the leak.

  • Custom script: call DestroyGroup (udg_PD_Group)
I would have also suggested to remove your second unit group, but IcemanBo has already covered that. I also think doing it this way will prevent the damage from stacking on one unit :D which is a good thing!
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
A rather simple spell, but it can be useful! As BPower said, try not to have so many comments in the triggers. Most people reviewing the spell will already know what you are trying to do. For example, you don't need to have a comment explaining what your For Loop does, or why you have call RemoveLocation scripts.
  • Change PD_Player[] to (Triggering Player) from Owner of (unit). Triggering Player is slightly faster (;
  • Make your loop look like this so that spell deindexes when it has to and not at the next iteration. A wise man told me it's not nice to have actions run before the spell needs to deindex. It should only deindex when it is time to deindex.
    • Psionic Destruction Loop
      • Events
        • Time - Every 0.20 seconds of game time
      • Conditions
      • Actions
        • For each (Integer PD_TempIndex) from 1 to PD_MaxIndex, do (Actions)
          • Loop - Actions
            • Set PD_Duration[PD_TempIndex] = (PD_Duration[PD_TempIndex] - 0.20)
            • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
              • If - Conditions
                • (PD_Caster[PD_TempIndex] is alive) Equal to True
                • PD_Duration[PD_TempIndex] Greater than 0.00
              • Then - Actions
                • -------- loop actions --------
              • Else - Actions
                • -------- deindex --------
  • You don't need to null PD_RandomUnit
  • Why do you create the special effect on a location instead of having it attached to the unit? It looks really awkward if they move too fast and the effect appears behind them
  • I think you should add a config option to set how often the spell searches and damages a random enemy unit instead of having it every 0.2 seconds. This will also allow you to have a loop of 0.03 seconds and give users the flexibility of how they want the spell to act
  • Change Turn off (This trigger) to Turn off Psionid Descrtion Loop <gen>. Again, slightly faster (;
 
Level 11
Joined
Jul 25, 2014
Messages
490
1. Hmmmm, I always used owner of <unit> and nobody batted an eye on previous spells, will change, thanks.
2. I do deindex only when it's time, it's just as you said, didn't understand your point.
3. Yes, in fact, I do, since if I don't and while the spell is in action and I go out of range, the last randomed unit will get damaged for the duration.
4. You meant point, yeah, but I want to keep it this way.
5. Okay, thanks.
6. Same dilemma as in the first point of the review, but will do, thanks.
 
Last edited:
Level 37
Joined
Jul 22, 2015
Messages
3,485
Hmmmm, I always used owner of <unit> and nobody batted an eye on previous spells, will change, thanks.
There is nothing wrong with Owner of (unit), I'm just saying it's slightly faster :)

I do deindex only when it's time, it's just as you said, didn't understand your point.
Let me visualize it for you: Imagine a spell duration of 3 seconds with a loop timer of 1 second
  • With your loop:
    1 second has passed -> check if spell duration is over -> no, add 1 second (total: 1 second) to spell duration and continue to run loop -> 2 seconds has passed -> check if spell duration is over -> no, add 2 seconds (total: 3 seconds) and continue to run loop -> 4 seconds has passed -> check if spell duration is over -> yes, deindex​
  • With my proposed loop:
    1 second has passed -> add 1 second to spell duration (total: 1 second) and check if spell duration is over -> no, continue to run loop -> 2 seconds has passed -> add 2 seconds to spell duration (total: 3 seconds) and check if spell duration is over -> yes, deindex.​

Do you see how you go through 4 seconds of a spell duration meant for 3 seconds with your setup? My proposal makes the spell end at 3 seconds, not at the next loop iteration. It's not really a big deal, but there is a possibility of getting unwanted results with loops that act like this. I'm also just trying to improve your triggering.

Yes, in fact, I do, since if I don't and while the spell is in action and I go out of range, the last randomed unit will get damaged for the duration.
Ahh my mistake. I guess you can just use Unit Group - Remove unit from PD_Group instead of nulling, but I'm not sure which is better.

You meant point, yeah, but I want to keep it this way.
Location and point are the same thing, hence why in the variable editor they are called points, yet we use "call RemoveLocation" and not "call RemovePoint". Anyway, it was just a suggestion, so no worries.
 
Level 11
Joined
Jul 25, 2014
Messages
490
Couldn't the whole trigger just be replaced with immolation?

No, since it attacks random selected units in AoE, not all units in AoE.

Let me visualize it for you: Imagine a spell duration of 3 seconds with a loop timer of 1 second
With your loop:
1 second has passed -> check if spell duration is over -> no, add 1 second (total: 1 second) to spell duration and continue to run loop -> 2 seconds has passed -> check if spell duration is over -> no, add 2 seconds (total: 3 seconds) and continue to run loop -> 4 seconds has passed -> check if spell duration is over -> yes, deindex
With my proposed loop:
1 second has passed -> add 1 second to spell duration (total: 1 second) and check if spell duration is over -> no, continue to run loop -> 2 seconds has passed -> add 2 seconds to spell duration (total: 3 seconds) and check if spell duration is over -> yes, deindex.

Do you see how you go through 4 seconds of a spell duration meant for 3 seconds with your setup? My proposal makes the spell end at 3 seconds, not at the next loop iteration. It's not really a big deal, but there is a possibility of getting unwanted results with loops that act like this. I'm also just trying to improve your triggering.

So basically I would just change the duration before the ITE block in the loop? I guess it makes sense.

Thanks for the review, by the way, appreciated.
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
I think you misunderstood me when I suggested the new configurable option xD

Right now, your spell is hardcoded to find and damage a random enemy unit in range every 0.2 seconds. Personally, I think a loop timer of 0.2 seconds is too long. On top of that, a map maker might want to change it and have it damage a random enemy unit every 1 second, or even every 0.05 seconds. They wouldn't be able to do that unless they understood triggers and change what you hardcoded. To avoid this, you can just add 2 new variables to your spell:

  • DamageFrequency (real) - You can have a multi-level or just single level variable that lets a user decide when it is time to deal damage. For your case, you want 0.2 seconds
  • CounterDamage[] (real) - CounterDamage[] works like PD_Duration[]; it will keep track of the time passed. You will need to index + deindex it when it is time

  • Psionic Destruction Loop
    • Events
      • Time - Every 0.03 seconds of game time
    • Conditions
    • Actions
      • For each (Integer PD_TempIndex) from 1 to PD_MaxIndex, do (Actions)
        • Loop - Actions
          • Set PD_Duration[PD_TempIndex] = (PD_Duration[PD_TempIndex] - 0.03)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (PD_Caster[PD_TempIndex] is alive) Equal to True
              • PD_Duration[PD_TempIndex] Greater than 0.00
            • Then - Actions
              • Set CounterDamage[] = CounterDamage[] + 0.03
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • CounterDamage[] Greater than or equal to DamageFrequency
                • Then - Actions
                  • Set CounterDamage[] = 0.00
                  • -------- find random enemy unit and damage target --------
                • Else - Actions
                  • -------- it is not time to deal damage; do nothing --------
            • Else - Actions
              • -------- deindex --------

You can also go as far as letting the user decide how many random units get damage.
 
Level 11
Joined
Jul 25, 2014
Messages
490
Right now, your spell is hardcoded to find and damage a random enemy unit in range every 0.2 seconds. Personally, I think a loop timer of 0.2 seconds is too long. On top of that, a map maker might want to change it and have it damage a random enemy unit every 1 second, or even every 0.05 seconds. They wouldn't be able to do that unless they understood triggers and change what you hardcoded. To avoid this, you can just add 2 new variables to your spell:

Doesn't PD_Period allow you to change the frequency?
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
Doesn't PD_Period allow you to change the frequency?

Well, sort of... but it is currently the same value as your loop timer, and like I said, a loop timer of 0.2 seconds can be ugly. You should always try to use really small loop timers like 0.03 seconds to get "smooth" movements and effects. PD_Period should be changed to 0.03, and you will need to give a variable like DamageFrequency to 0.2 so that your original spell integrity is kept. The biggest pro to this is that you now give users extreme flexibility with your spell.
 
Level 11
Joined
Jul 25, 2014
Messages
490
Well, the point of this spell was supposed to be a recreation of a cool ability in a certain ORPG, it had the loop of 0.2 seconds, so I left it that way. The spell itself is not too complicated, so the loop time doesn't matter that much, it doesn't require any smoothness at all. If a map maker decides to implement this in their map, they can just change the frequency with PD_Period variable in the configuration trigger and no additional code/variables is needed. Like I said, the point of this spell was to keep it simple.

--

I do think that your methods are efficient, but I don't think they're this ability's material. Thanks for the suggestion tho =P
 
Top