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

Soul Tear Aura V1.01

This my Entry to the Zephyr Contest #11 the description is taken from the entry post made there:

[TD]Spell: Soul Tear Aura[/TD]
Tooltip
Soul Tear Aura
Spirits strike nearby enemies damaging them, once struck attacks deal additional Magic damage to enemies. Enemies that die with this effect deal damage to other nearby enemies. Dying restores health to affected enemies

Level 1 - 35 strike damage, 7.5% additional damage, 30 damage done to nearby enemies, 150 Health restored on death
Level 2 - 45 strike damage, 10% additional damage, 50 damage done to nearby enemies, 200 Health restored on death
Level 3 - 55 strike damage, 12.5% additional damage, 70 damage done to nearby enemies, 250 Health restored on death
Code


JASS:
////////////////////////////////////////////////////////////////////
//                      SOUL TEAR AURA V1.00                      //
//                                                                //
//  Author: Tank-Commander                                        //
//  Requires: Dummy.mdl                                           //
//  Purpose: Tide-turner                                          //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the spell     //
//                                                                //
//  Credits:                                                      //
//    -  (Dummy.mdl) Vexorian                                     //
//                                                                //
//                                                                //
//  If you have used this spell in your map, you are required     //
//  to give credits to Tank-Commander for the creation of it      //
//  If you would like to use snippets of code from this for       //
//  whatever, getting permission and crediting the source/linking //
//  would be much appreciated.                                    //
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  README:                                                       //
//    Before modifying this spell a few things need to be         //
//    understood and read, this is one of those things, while     //
//    most modification can be considered intuitive, it still     //
//    helps to read through these intstructions, as they will     //
//    inform you about how to configure this spell to your        //
//    desire.                                                     //
//----------------------------------------------------------------//
//  Initial importing: The variable creator trigger can be        //
//  imported first and if you have the correct settings (file,    //
//  preferences, General, automatically create unknown variables  //
//  checked, then when you paste in the variable creator it       //
//  will automatically give you all the variables you need for    //
//  this spell                                                    //
//----------------------------------------------------------------//
//  Effect Sets: The simple trigger comment "Effect Sets" is for  //
//  temporarily storing effect strings so that they can be added  //
//  to the spell more easily, as well as for storing multiple     //
//  sets without having to re-find the effect strings, largely    //
//  for finding the set you wish to use and trying out various    //
//  combinations                                                  //
//----------------------------------------------------------------//
//  This configuration is ordered by category of use, and not     //
//  alphabetically, the ones you're most likely to want to        //
//  change, are earlier in the readme, the most essential being   //
//  first. These categories are:                                  //
//                                                                //
//  - Data Values: The most essential to change, otherwise the    //
//                 spell will simply not function                 //
//                                                                //
//  - ChronoKinetic: Fancy word for affecting the flow of time    //
//                 Contains things like the duration of the       //
//                 spell, the Timer speed and Fragment removal    //
//                                                                //
//  - Aesthetics: Controls the Appearences of the ability - the   //
//                 models used, the scales of those models and    //
//                 so on                                          //
//                                                                //
//  - Damage: Controls the AOE and damage (health and mana)       //
//                 values each section of the spell has/uses      //
//                 as well as projectile speeds and movement      //
//                 (since it affects how effective the aura is)   //
//                                                                //
//  - Damage Information: Contains things like weapontypes and    //
//                 damage types, etc.                             //
//                                                                //
//  - Other Attributes: Contains the collision size of the        //
//                 Fragments, causes them to detonate             //
//----------------------------------------------------------------//
//                        DATA VALUES                             //
//----------------------------------------------------------------//
//  Ability ID: This is the raw data of the Aura ability, to see  //
//  raw data in the object editor, press Ctrl + D, doing this     //
//  again will switch it back, if you want to change this Spell   //
//  follow as displayed (use the first 4 characters in the raw    //
//  data and put them in ' markers)                               //
constant function STA_AbilityID takes nothing returns integer
    return 'A000'
endfunction
//----------------------------------------------------------------//
//  Buff Ability ID: This is the ability used to apply the buff   //
//  icon/description to affected units, in order to make the      //
//  aura function near-exactly to normal Warcraft 3 auras, also   //
//  unlike most Auras, this enabled magic-immunes to be filtered  //
//  out from being effected, while not giving them the buff icon  //
//  it also removes dependance on the the dummy ability having    //
//  any range, so the dummy Ability used for the aura can be any  //
//  passive spell, or even an active one with it's own effect.    //
//  This Buff ability should be based off Slow Aura (Tornado) as  //
//  it does not display on the command panel of units which have  //
//  it as an ability, this is to improve visuals, it should have  //
//  all values which affect units set to 0, though if you want a  //
//  slowing effect added (attack &/or movespeed) to the aura      //
//  then these values can be modified to add it, set the buff     //
//  you want displayed on units set as it's buff, it should also  //
//  have AOE set to 0 just in case.                               //
constant function STA_BuffAbilityID takes nothing returns integer
    return 'A001'
endfunction
//----------------------------------------------------------------//
//  Dummy ID: This is done in the same manner as the Ability ID   //
//  except that this time, you're doing it with unit raw date     //
//  see the dummy unit if you do not know already how to view     //
//  raw data                                                      //
constant function STA_DummyID takes nothing returns integer
    return 'u000'
endfunction
//----------------------------------------------------------------//
//                        CHRONOKINETIC                           //
//----------------------------------------------------------------//
//  Timer Speed: The default for this is ~0.03, it determines how //
//  many times per second these triggers are ran, normally you    //
//  want to leave this at ~0.03, but 0.04 and prehaps 0.05        //
//  would be good options if you computer lags a bit.             //
constant function STA_TimerRate takes nothing returns real
    return 0.031250000
endfunction
//----------------------------------------------------------------//
//  Check Timer Rate: The default value for this is 0.25, this    //
//  timer is used for checking the map for all units which have   //
//  the aura, and primarily serves to initialise these units      //
//  which wouldn't be picked up by the standard "learn" method    //
//  this includes units which are created at runtime through      //
//  trigger actions, ones already placed on the map and those     //
//  which are trained, starting with the aura                     //
constant function STA_CheckTimerRate takes nothing returns real
    return 0.25
endfunction
//----------------------------------------------------------------//
//  Check Timer Periodic: Determines whether or not the Check     //
//  Timer runs constantly all the time without stopping, thus     //
//  allowing it to pick up more than pre-placed units, this       //
//  should always be set to false if no units will gain this      //
//  ability through any means other than learning it, or being    //
//  preplaced unit on the map, as it would just be wasteful       //
constant function STA_CheckTimerPeriodic takes nothing returns boolean
    return false
endfunction
//----------------------------------------------------------------//
//  Dummy Removal Delay: Determines how long after the death of a //
//  fragment, for it be removed from the game completely, this is //
//  so death effects, can finish playing before the unit is       //
//  removed, 1.8 is default, 2 is probably excessive and in some  //
//  cases it's possible that 1 could be too little                //
constant function STA_DummyRemovalDelay takes nothing returns real
    return 1.8
endfunction
//----------------------------------------------------------------//
//  Effect Reduction Delay: Determines how long between each      //
//  bonus damage effect, before another one can be created, this  //
//  is because without this the effect would spam causing         //
//  a significant FPS drop, 0.5 is the defaults value of this     //
constant function STA_EffectReductionDelay takes nothing returns real
    return 0.5
endfunction
//----------------------------------------------------------------//
//                        AESTHETICS                              //
//----------------------------------------------------------------//
//  Note: Effects must have valid pathnames and must not be set   //
//  to null values, otherwise graphical errors are likely to      //
//  occur, if an effect can be left null, it will be marked       //
//----------------------------------------------------------------//
//  Aura Effect: This determines the model used for your main     //
//  aura, you want to use the model path and paste it into the    //
//  double quotes or " markers, if the path has only single       //
//  slashes (\) you'll need to changeb it to double slash (\\)    //
//  before you save, the spell will not work if you do not do     //
//  this (if you acidentally save with the single slash (\) it    //
//  will still save but you may find yourself having a lingering  //
//  progress bar at full percentage, you can ignore it  for the   //
//  most part, but if you have it, you probably haven't entered   //
//  this field correctly. Get the model paths from the Object     //
//  editor - find the model you want (the model, not the unit,    //
//  in the models list, select it and hit enter twice, then       //
//  select the path (will look similar to this default value,     //
//  and paste in it here) I suggest doing this with a unit of no  //
//  value (changing the model to get the model path) and then     //
//  reset the field afterwards to get the unit back to normal,    //
//  this function must return a valid value, to make "remove"     //
//  effects from the spell (for those without "Use" functions     //
//  to disable them) you can enter the path of dummy.mdl (this    //
//  can differ from map to map depending on how you imported it)  //
//  and this will cause the system to create invisible effects    //
//  so that the spell can still run properly                      //
constant function STA_AuraEffect takes nothing returns string
    return "Abilities\\Spells\\Undead\\RegenerationAura\\ObsidianRegenAura.mdl"
endfunction
//----------------------------------------------------------------//
//  Soul Fragment Effect:This determines the effect displayed on  //
//  enemies who are within the AOE of the aura, it is the buff    //
//  effect of the spell, and therefore must have a valid value    //
constant function STA_SoulFragmentEffect takes nothing returns string
    return "Abilities\\Spells\\Undead\\Curse\\CurseTarget.mdl"
endfunction
//----------------------------------------------------------------//
//  Soul Fragment Projectile Effect: This is the effect used for  //
//  the projectiles or "spirits" that strike enemy targets which  //
//  activate the effect of the aura on impact, the same effect    //
//  is used for "spirits" that appear on unit death.              //
constant function STA_SoulFragmentProjectileEffect takes nothing returns string
    return "Abilities\\Weapons\\GreenDragonMissile\\GreenDragonMissile.mdl"
endfunction
//----------------------------------------------------------------//
//  Initial Damage Effect: This determines the effect used when   //
//  a unit is hit by the homing spirits and activate the initial  //
//  effect, this is one of the few effects that can be null but   //
//  only if "Use Initial Damage Effect" is set to false           //
constant function STA_InitialDamageEffect takes nothing returns string
    return "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl"
endfunction
//----------------------------------------------------------------//
//  Death Damage Effect: This determines the effect used when     //
//  a spirit created by a dying unit detonates dealing damage     //
//  typically this will be the same as "Initial Damage Effect"    //
//  and can also be null if "Use Death Damage Effect" is false    //
constant function STA_DeathDamageEffect takes nothing returns string
    return "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl"
endfunction
//----------------------------------------------------------------//
//  Bonus Damage Effect: This determines the effect created when  //
//  a unit takes bonus magic damage from the aura, it's limited   //
//  by the "Effect Reduction Delay" value, it cannot be set to    //
//  null, however                                                 //
constant function STA_BonusDamageEffect takes nothing returns string
    return "Abilities\\Spells\\Items\\AIil\\AIilTarget.mdl"
endfunction
//----------------------------------------------------------------//
//  Heal Effect: This is the effect used when the hero creating   //
//  the aura effect dies and nearby enemies are healed afterward  //
//  this should have a positive look unless the value you give    //
//  for the damage value of the heal effect is set to a negative  //
//  if "Use Heal Effect" is false, then this can be set to null   //
constant function STA_HealEffect takes nothing returns string
    return "Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl"
endfunction
//----------------------------------------------------------------//
//  Aura Attachment Point: Set this to the desired attachment     //
//  point of the aura, typically this will be "origin" as most    //
//  warcraft 3 auras use it for this effect valid attachment      //
//  points include: "origin", "overhead", "head", "chest", and    //
//  "left, [limb]", "right, [limb]" limbs: "foot", "hand"         //
constant function STA_AuraAttachmentPoint takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
//  Soul Fragment Attachment Point: This is the attachment point  //
//  of where the buff of being effected will appear, typically    //
//  this will be set to "overhead" as most Warcraft 3 auras use   //
constant function STA_SoulFragmentAttachmentPoint takes nothing returns string
    return "overhead"
endfunction
//----------------------------------------------------------------//
//  Soul Fragmnet Projectile Attachment Point: The attachment     //
//  point for the projectile on the dummy unit, what this is set  //
//  to doesn't particularly make much difference so long as it's  //
//  valid, try various settings to get one you like the look of   //
//  typically, it'll be set to chest, just to centre it           //
constant function STA_SoulFragmentProjectileAttachmentPoint takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
//  Initial Damage Attachment Point: This is the attachment       //
//  point of the effect created when the "Spirits" hit units to   //
//  activate the aura effect on them, this also doesn't normally  //
//  make any difference, typically it will be set to the same     //
//  value as "Soul Fragment Projectile Attachment Point"          //
constant function STA_InitialDamageAttachmentPoint takes nothing returns string
    return "chest"
endfunction
//----------------------------------------------------------------//
//  Death Damage Attachment Point: Determines the attachment      //
//  point for the effect created when "Spirits" made by a unit    //
//  dying impact, this is typically set to the same value as      //
//  "Soul Fragment Projectile Attachment Point" but it doesn't    //
//  really make any difference so long as it's valid              //
constant function STA_DeathDamageAttachmentPoint takes nothing returns string
    return "chest"
endfunction
//----------------------------------------------------------------//
//  BonusDamageAttachmentPoint: Determines the attachment point   //
//  of the effect generated when a unit takes bonus damage as a   //
//  result of the aura, "origin", "chest", and "overhead" all     //
//  suit this effect depending on the model used, for most it     //
//  looks/works best with "chest", however any can be used        //
constant function STA_BonusDamageAttachmentPoint takes nothing returns string
    return "chest"
endfunction
//----------------------------------------------------------------//
//  Heal Effect Attachment Point: Controls the attachment point   //
//  of the heal effect created when the unit/hero with the aura   //
//  has died, similar to the bonus damage "origin", "chest" and   //
//  "overhead" are all good matches for this attachment point     //
//  for the same reason                                           //
constant function STA_HealEffectAttachmentPoint takes nothing returns string
    return "chest"
endfunction
//----------------------------------------------------------------//
//  Soul Fragment Projectile Scale Base: This determines the      //
//  size of the projectiles created by the aura, the value is a   //
//  percentage, so 1 = 100%, and thus the default size of the     //
//  effect, depending on the effect used this will often need to  //
//  be a fractional value. Default is 0.6 or .6                   //
constant function STA_SoulFragmentProjectileScaleBase takes nothing returns real
    return .6
endfunction
//----------------------------------------------------------------//
//  Soul Fragment Projectile Scale Per Level: The extra size      //
//  given to projectiles for every level of the spell, this is    //
//  normally 0, but available if users wish to alter the size     //
//  to make the spell appear more menacing as it levels up        //
constant function STA_SoulFragmentProjectileScalePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  Soul Fragment Height Start Offset: This is the height added   //
//  on to the base height of the unit creating the effect, that   //
//  projectiles start at, 0 will be at their feet, 30 will often  //
//  be chets height, and 60 overhead, try out various heights to  //
//  see what you feel fits best                                   //
constant function STA_SoulFragmentHeightStartOffset takes nothing returns real
    return 100.
endfunction
//----------------------------------------------------------------//
//  Soul Fragment Height End Offset: The same as above but for    //
//  the height added onto the target's fly height that will be    //
//  reached when the "Spirit"/Projectile hits the target rather   //
//  than start at, again 0 is foot height, 30 is chest and 60 is  //
//  overhead, typically this value is lower than the start height //
constant function STA_SoulFragmentHeightEndOffset takes nothing returns real
    return 90.
endfunction
//----------------------------------------------------------------//
//  Use Initial Damage Effect: This boolean value determines if   //
//  the effect generated by the "spirits" or projectiles hitting  //
//  their target to start the effect of the ability is used,      //
//  setting to false will remove it, and thus the model used for  //
//  the effect can be null, as giving it a value will not make    //
//  any difference                                                //
constant function STA_UseInitialDamageEffect takes nothing returns boolean
    return true
endfunction
//----------------------------------------------------------------//
//  Use Death Damage Effect: Similar to the above boolean but     //
//  this one is for the effect created when projectiles made by   //
//  units dying impact and detonate, again setting it to false    //
//  removes the effect and thus enabled the value to be set to    //
//  null for the model string                                     //
constant function STA_UseDeathDamageEffect takes nothing returns boolean
    return true
endfunction
//----------------------------------------------------------------//
//  Use Heal Effect: Once again the same as above, however in     //
//  this case for the effect generated when units are healed from //
//  the Hero/Unit with the aura dying. unless the feature is      //
//  removed (setting the amount healed to 0) it makes sense for   //
//  this value to be set to true                                  //
constant function STA_UseHealEffect takes nothing returns boolean
    return true
endfunction
//----------------------------------------------------------------//
//                            DAMAGE                              //
//----------------------------------------------------------------//
//  AOE Base: This is your basic area of effect for your ability  //
//  it should be the highest AOE of the ability as such,          //
//  typically, this value should be at least 200, units in this   //
//  area will have Fragment launched at them and begin the bonus  //
//  damage effect                                                 //
constant function STA_AOEBase takes nothing returns real
    return 275.
endfunction
//----------------------------------------------------------------//
//  AOE Per Level: This is the amount of extra AOE given on top   //
//  of the basic AOE for each level of the spell that the hero    //
//  has,this will also be applied for level one                   //
constant function STA_AOEPerLevel takes nothing returns real
    return 75.
endfunction
//----------------------------------------------------------------//
//  Search AOE Base: Determines how far away from the location    //
//  of a dying unit effected by the aura, will be considered a    //
//  valid target, keep in mind, targets which are effected by     //
//  the aura, even if a different unit is the source, are valid   //
//  If this is set to 0, alone with "Search AOE Per Level", the   //
//  effect of damage being done by units dying will be removed    //
constant function STA_SearchAOEBase takes nothing returns real
    return 90.
endfunction
//----------------------------------------------------------------//
//  Search AOE Per Level: The Per Level value of the Search AOE   //
//  Base value, typically this will be a fraction of the base     //
//  value between 1/2 and 1/4 of the base value for balance       //
//  if this is set to 0, as well as "Search AOE Base", the effect //
//  of extra damage being done by units dying will be removed     //
constant function STA_SearchAOEPerLevel takes nothing returns real
    return 35.
endfunction
//----------------------------------------------------------------//
//  Impact AOE Base: Determines the AOE of the damaging effect    //
//  when a "Spirit" inflicts damage, this applies for both the    //
//  initial, homing projectiles, and the non-homing ones created  //
//  by unit death, this should be higher than the collision size  //
//  in order to avoid damageless impacts, and should be between   //
//  values of 50-100 typically, default is 75                     //
constant function STA_ImpactAOEBase takes nothing returns real
    return 75.
endfunction
//----------------------------------------------------------------//
//  Impact AOE Per Level: Determines how much extra AOE damage    //
//  effects have on top of the Impact AOE Base, and applies at    //
//  level one, as with all Per Level values, typically this is    //
//  set between 0-20 for balancing reasons and to reduce chain    //
//  killing becoming too common/likely to take out huge sections  //
//  of enemies                                                    //
constant function STA_ImpactAOEPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  Soul Fragment Projectile Speed Base: This is the true value   //
//  speed of projectiles, and how far the projectiles will move   //
//  per second, having a particularly high value (over 1000) will //
//  likely cause accuracy issues and overshooting with the spell  //
//  a value between 200-500 is typical, default value is 288      //
constant function STA_SoulFragmentProjectileSpeedBase takes nothing returns real
    return 288.
endfunction
//----------------------------------------------------------------//
//  Soul Fragment Projectile Speed Per Level: The per level       //
//  counterpart of "Soul Fragment Projectile Speed Base", when    //
//  combined with that value, the total should not exceed 1200    //
//  for the reasons described above, remember that this applies   //
//  even on the first level, a value between 0-200 is typical     //
//  default value is 192                                          //
constant function STA_SoulFragmentProjectileSpeedPerLevel takes nothing returns real
    return 192.
endfunction
//----------------------------------------------------------------//
//  Soul Fragment Projectile Hit Time Base: This is the optional  //
//  movement type of the projectiles, which alters the velocity   //
//  based on how long it takes for them to hit their target in    //
//  seconds, creating a rather different effect from using true   //
//  speed, this value in combination with the per level size      //
//  should not become lower than 1/6th of a second if inaccuracy  //
//  issues are to be avoided (units farther away with a short     //
//  hit time, generate much higher true speeds on the projectile) //
//  WARNING: if the sum of this value and its per level side end  //
//  up with values less than 0.5, depending on the impact AOE     //
//  used by the spell, not only may inaccuracy occur, but the     //
//  game may crash due to projectiles getting too fast. This      //
//  normally occurs because the real speed applied to the         //
//  projectile exceeds the AOE of the impact area; with the       //
//  default set up, this means that the projectile has to be      //
//  moving at over 2500m/s, while applying this value to the real //
//  speed option for the spell is unlikely to cause crashing;     //
//  it cannot be done here as if targets move away from the aura  //
//  projectiles speed up (in order to maintain the hit time)      //
//  meaning that if a projectile overshoots, it will only get     //
//  even faster, and typically leave the map (causing crash)      //
constant function STA_SoulFragmentProjectileHitTimeBase takes nothing returns real
    return 2.
endfunction
//----------------------------------------------------------------//
//  Soul Fragment Projectile Hit Time Per Level: One of the only  //
//  values which is typically negative, this is so that the time  //
//  taken to hit targets is reduced every level, so that they     //
//  move faster, making this a positive value will makes them     //
//  slower, remember; this applies to level 1, as well. default   //
//  value is -0.4. Keep in mind that if the total becomes very    //
//  small, inaccuracy issues may occur (and possibly game crash)  //
constant function STA_SoulFragmentProjectileHitTimePerLevel takes nothing returns real
    return -0.5
endfunction
//----------------------------------------------------------------//
//  Soul Fragment Movement Type: Determines the form of movement  //
//  used by the projectiles, true activated the Hit Time values   //
//  while false enables the Projectile Speed values or true       //
//  speed, the effect generated by the different movement types   //
//  are very different, so try out both if you're unsure          //
constant function STA_SoulFragmentMovementType takes nothing returns boolean
    return false
endfunction
//----------------------------------------------------------------//
//  Initial Effect Health Damage Base: This is the amount of hp   //
//  damage dealt when the "Spirits"/Projectiles first hit enemy   //
//  units and start the effect of the aura on them reducing it    //
//  to 0 removes all damage and makes the first stage simply an   //
//  activation effect. The true damage dealt by this effect is    //
//  affected by type of damage used by the aura                   //
constant function STA_InitialEffectHealthDamageBase takes nothing returns real
    return 25.
endfunction
//----------------------------------------------------------------//
//  Initial Effect Health Damage Per Level: The per level side    //
//  to the base health damage, added on to the base value even    //
//  at level 1 as with all per level values. to remove the        //
//  effect this should also be set to 0                           //
constant function STA_InitialEffectHealthDamagePerLevel takes nothing returns real
    return 10.
endfunction
//----------------------------------------------------------------//
//  Initial Effect Mana Damage Base: Determines how much mana     //
//  damage is done by the initial activation effect of the aura   //
//  typically this is 0, but is available if users wish to have   //
//  it, this is a true value and is thus unaffected by the type   //
//  of damage used by the aura if set to a negative value, it     //
//  will restore mana to targets rather than remove it            //
constant function STA_InitialEffectManaDamageBase takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  Initial Effect Mana Damage Per Level: Determines how much     //
//  extra mana damage is dealt by the spell per level, typically  //
//  also set to 0, but again available if users wish to have it   //
constant function STA_InitialEffectManaDamagePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  Death Health Damage Base: This is the amount of Hp damage     //
//  dealt by "Spirits"/Projectiles created by units dying with    //
//  the aura affecting them, actual damage dealt by this is       //
//  altered by the type of damage used by the spell rather than   //
//  a fixed value, typically this is the main damage dealing part //
//  of the ability and causes chain-kills even with low values    //
constant function STA_DeathHealthDamageBase takes nothing returns real
    return 10.
endfunction
//----------------------------------------------------------------//
//  Death Health Damage Per Level: The extra amount of Hp damage  //
//  dealt by the "Spirits"/Projectiles created by units dying     //
//  with the aura affecting them, very similar to the Base part   //
constant function STA_DeathHealthDamagePerLevel takes nothing returns real
    return 20.
endfunction
//----------------------------------------------------------------//
//  Death Mana Damage Base: The Base mana damage dealt by the     //
//  "Spirits"/Projectiles created by units dying with the aura    //
//  affecting them, this is true damage and is thus unaffected    //
//  by the type of damage used by the spell, setting this to a    //
//  negative value will restore mana rather than damage enemies   //
constant function STA_DeathManaDamageBase takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  Death Mana Damage Per Level: The per level counterpart to     //
//  "Death Mana Damage Base", also true damage and added on to    //
//  all levels (multiplied by the level of the ability)           //
constant function STA_DeathManaDamagePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  Bonus Health Damage Percent Base: Determines the amount of    //
//  bonus Hp damage dealt by all damage sources on the affected   //
//  units, only activated after they have been hit by the         //
//  initial "Spirit"/Projectile. This is a percentage value thus  //
//  setting this value to 1 = 100% damage increase, typically     //
//  this should be a low percentage, as damage adds up quickly    //
//  the resulting damage of this is affected by the damage type   //
//  used by the ability, default value is 0.1 or .1 (10%)         //
constant function STA_BonusHealthDamagePercentBase takes nothing returns real
    return .05
endfunction
//----------------------------------------------------------------//
//  Bonus Health Damage Percent Per Level: The Per Level side to  //
//  The "Bonus Health Damage Percent Base", typically a lower     //
//  fractional number, 0.05 or .5 is the default (5%)             //
constant function STA_BonusHealthDamagePercentPerLevel takes nothing returns real
    return .025
endfunction
//----------------------------------------------------------------//
//  Bonus Mana Damage Percent Base: Determines the amount of      //
//  bonus Mana damage dealt by all mana damage sources to the     //
//  affected units, only starts applying once they have been hit  //
//  by the initial "Spirit"/Projectile. Also a percentage value   //
//  but a True value, thus unaffected by the damage type of the   //
//  ability, setting it to a negative value will restore mana     //
//  instead of destroying it                                      //
constant function STA_BonusManaDamagePercentBase takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  Bonus Mana Damage Percent Per Level: A Per Level counterpart  //
//  to the Base value, typically lower than that Base value, and  //
//  even then often less than 10% or 0.1 (.1)                     //
constant function STA_BonusManaDamagePercentPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  Hero Health Heal Base: This is the amount of health restored  //
//  to enemy units when the hero generating the aura has been     //
//  slain, default value is 100, this is a true value and thus    //
//  ingores damage type used by the spell, unlike all other       //
//  health damage dealt by this aura. Setting this to a negative  //
//  value will cause it to deal damage rather than heal enemies   //
//  WARNING: This damage does not recognise the damage source,    //
//  Units killed by this effect will NOT give gold to the owner   //
//  of the unit with the aura, nor count as them having killed    //
//  those units                                                   //
constant function STA_HeroHealthHealBase takes nothing returns real
    return 100.
endfunction
//----------------------------------------------------------------//
//  Hero Health Heal Per Level: The extra health given to units   //
//  when the unit/hero generating the aura has been slain, this   //
//  is added on to the Base value (multiplied by the level of     //
//  the aura) and is a true value                                 //
constant function STA_HeroHealthHealPerLevel takes nothing returns real
    return 50.
endfunction
//----------------------------------------------------------------//
//  Hero Mana Heal Base: Determines the amount of mana given to   //
//  units when the unit/hero creating the aura is slain, it is a  //
//  true value thus unaffected by the damagetype used by the      //
//  aura, setting this to a negative value will remove mana       //
//  rather than add it                                            //
constant function STA_HeroManaHealBase takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  Hero Mana Heal Per Level: The extra amount of mana given to   //
//  units when the unit/hero creating the aura is slain, also a   //
//  true value and added on (multiplied by the level of the aura) //
//  to the base value, setting this to a negative value will      //
//  remove mana rather than add it                                //
constant function STA_HeroManaHealPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//                    DAMAGE INFORMATION                          //
//----------------------------------------------------------------//
//  Damage Type: These determine the damagetypes, changing this   //
//  will modify the damage multiplyers vs certain enemies         //
//  the standard is DAMAGE_TYPE_MAGIC, note that this spell       //
//  automatically discludes magic immunes, so changing this       //
//  damage type will not make them start taking damage            //
constant function STA_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_MAGIC
endfunction
//----------------------------------------------------------------//
//  Attack Type: This is very much so basically the same as       //
//  Damage Type, generally you'll want this to match with it      //
//  as such the default is ATTACK_TYPE_MAGIC, though Damagetype   //
//  is a key factor for determining bonuses rather than this      //
//  but unlike weapontype, you cannot have null as a setting      //
constant function STA_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_MAGIC
endfunction
//----------------------------------------------------------------//
//  Weapon Type: This alters what kind of weapon type is used by  //
//  the spell, those without knowledge of weapontypes don't       //
//  worry, you're not missing much, this spell doesn't really     //
//  use it, hence the default of null, but if you want to use     //
//  them, no reason to not.                                       //
constant function STA_WeaponType takes nothing returns weapontype
    return null
endfunction
//----------------------------------------------------------------//
//                     OTHER ATTRIBUTES                           //
//----------------------------------------------------------------//
//  Collision Size: Used for determining at what point has a      //
//  Fragment hit a target, you typically want this value above    //
//  30 but below 90, and it should always be larger than the      //
//  Damage AOE of the spell, otherwise the system will register   //
//  a collision, and then fail to deal any damage to the units    //
constant function STA_CollisionSize takes nothing returns real
    return 30.
endfunction
//  Dummy Owner: Used for what player owns all the dummy units    //
//  typically this is Player(14) but can really be used any       //
//  player number that is not in use in the map this spell is     //
//  used in                                                       //
constant function STA_DummyOwner takes nothing returns player
    return Player(14)
endfunction
//----------------------------------------------------------------//
//  You have now reached the end of the configuration, below are  //
//  the functions used to run the spell, beyond this point if     //
//  you find any constant values (indicated in Blue like the      //
//  other constants here if you have standard syntax highlighting //
//  they're there for a reason and don't fiddle with them, to     //
//  actually make any modifications I hope you're an experienced  //
//  programmer, though do not hassle for help because I don't     //
//  take responcibility for other people's programming skills     //
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  Function used to get the z height of locations needed by the  //
//  spell, since it can only be done with locations, this one     //
//  is reused throughout, instead of creating/destroying them     //
////////////////////////////////////////////////////////////////////
function STA_GetZ takes real x, real y returns real

    //Gets the location Z of the selected location
    call MoveLocation(udg_STA_ZLoc, x, y)
    return GetLocationZ(udg_STA_ZLoc)
   
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to calculate the speed of which any given       //
//  projectile must travel based on the form of movement that     //
//  has been selected in the configuration by the user            //
////////////////////////////////////////////////////////////////////
function STA_GetSpeed takes integer Node, real Distance returns real
   
    //Calculate Speed
    if (STA_SoulFragmentMovementType()) then
        return (Distance / udg_STA_SoulProjectileHitTime[Node]) * STA_TimerRate()
    else
        return udg_STA_SoulProjectileSpeed[Node] * STA_TimerRate()
    endif

endfunction

////////////////////////////////////////////////////////////////////
//  Function used to recycle instances, so that they can used     //
//  again later, keeping the total array sizes smaller            //
////////////////////////////////////////////////////////////////////
function STA_Recycle takes integer Node, timer T returns nothing
           
        if (udg_STA_LastNode == Node) then
            set udg_STA_LastNode = udg_STA_PrevNode[Node]
        endif
          
        //Recycles the node
        set udg_STA_RecycleNodes[udg_STA_RecyclableNodes] = Node
        set udg_STA_RecyclableNodes = udg_STA_RecyclableNodes + 1
        set udg_STA_NextNode[udg_STA_PrevNode[Node]] = udg_STA_NextNode[Node]
        set udg_STA_PrevNode[udg_STA_NextNode[Node]] = udg_STA_PrevNode[Node]
        set udg_STA_AbilityCounter = udg_STA_AbilityCounter - 1

        //Checks if there are no iterations of the aura left
        //this is ,mostly an "in-case" part of the script, as it
        //is unlikely that this ever returns true (only if a unit unlearns the ability)
        if (udg_STA_AbilityCounter == 0) then
            call DestroyTimer(T)
        endif

endfunction

/////////////////////////////////////////////////////////////////////
//  Target filter function - passed units and players and checks   //
//  if the unit is allowed to be targetted by this ability         //
/////////////////////////////////////////////////////////////////////
function STA_TargetFilter takes unit u, player pl returns boolean

    //Checks if the unit can be used as a target
    if (not IsUnitType(u, UNIT_TYPE_STRUCTURE)) and (not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)) and (IsUnitEnemy(u, pl)) and (GetUnitTypeId(u) != STA_DummyID()) and not(IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0) then
        return true
    endif
   
    return false

endfunction

////////////////////////////////////////////////////////////////////
//  This function handles the damage dealt to units by the        //
//  projectiles, including health and mana in one function,       //
//  since both forms of projectile use the same damage method     //
////////////////////////////////////////////////////////////////////
function STA_SoulProjectileDamage takes real x, real y, integer Node returns nothing
     //Declare Locals
    local unit u
    local player pl = udg_STA_OwningPlayer[Node]

    //Finds units within the AOE of the impact point which may be damaged
    call GroupEnumUnitsInRange(udg_STA_TempGroup, x, y, udg_STA_ImpactAOE[udg_STA_CasterNode[Node]], null)
               
    loop
       //Scanning through
       set u = FirstOfGroup(udg_STA_TempGroup)
       exitwhen u == null
                          
       //Select all the units which are to be damaged
       if (STA_TargetFilter(u, pl)) then
            call UnitDamageTarget(udg_STA_Caster[Node], u, udg_STA_HealthDamage[Node], false, false, STA_AttackType(), STA_DamageType(), STA_WeaponType())
            call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) - udg_STA_ManaDamage[Node])
       endif

       //Remove the unit from the unit group
       call GroupRemoveUnit(udg_STA_TempGroup, u)
    endloop
   
    //Null variables
    set pl = null

endfunction

////////////////////////////////////////////////////////////////////
//  Function used to create each projectile and set up all the    //
//  information they need to run, since both the caster and the   //
//  affected units can produce these projectiles                  //
////////////////////////////////////////////////////////////////////
function STA_CreateProjectile takes real x, real y, real z, real zSpeed, real Speed, real Angle, real Distance, real HealthDamage, real ManaDamage, integer Node, boolean Type, integer Target returns nothing
    //Create Locals
    local integer TempNode
   
    //Adds a new instance to the linked list
    set udg_STA_AbilityCounter = udg_STA_AbilityCounter + 1
   
    //Checking for recycleable Nodes
    if (udg_STA_RecyclableNodes == 0) then
        set udg_STA_NodeNumber = udg_STA_NodeNumber + 1
        set TempNode = udg_STA_NodeNumber
    else
        set udg_STA_RecyclableNodes = udg_STA_RecyclableNodes - 1
        set TempNode = udg_STA_RecycleNodes[udg_STA_RecyclableNodes]
    endif

    //Adds a new instance
    set udg_STA_NextNode[TempNode] = 0
    set udg_STA_NextNode[udg_STA_LastNode] = TempNode
    set udg_STA_PrevNode[TempNode] = udg_STA_LastNode
    set udg_STA_LastNode = TempNode
    set udg_STA_Caster[TempNode] = udg_STA_Caster[Node]
    set udg_STA_CasterNode[TempNode] = udg_STA_CasterNode[Node]
    set udg_STA_OwningPlayer[TempNode] = udg_STA_OwningPlayer[Node]
   
    //Creates the unit and sets up aesthetics
    set udg_STA_Unit[TempNode] = CreateUnit(STA_DummyOwner(), STA_DummyID(), x, y, Angle)
    if UnitAddAbility(udg_STA_Unit[TempNode], 'Amrf') and UnitRemoveAbility(udg_STA_Unit[TempNode], 'Amrf') then
    endif
    set udg_STA_CurrentEffect[TempNode] = AddSpecialEffectTarget(STA_SoulFragmentProjectileEffect(), udg_STA_Unit[TempNode], STA_SoulFragmentProjectileAttachmentPoint())
    call SetUnitScale(udg_STA_Unit[TempNode], udg_STA_SoulFragmentScale[Node],0.,0.)
    call SetUnitFlyHeight(udg_STA_Unit[TempNode], z - STA_GetZ(x, y), 0)
   
    //Sets up the values it needs to run properly
    set udg_STA_StageID[TempNode] = 3
    set udg_STA_ZSpeed[TempNode] = zSpeed
    set udg_STA_Speed[TempNode] = Speed
    set udg_STA_HealthDamage[TempNode] = HealthDamage
    set udg_STA_ManaDamage[TempNode] = ManaDamage
    set udg_STA_FragmentType[TempNode] = Type
   
    set udg_STA_DistanceTravelled[TempNode] = 0.
 
    //Checks if it's an initial projectile
    //or one made when a unit dies 
    if (Type) then
        //Sets up the target of the projectile
        set udg_STA_Target[TempNode] = udg_STA_Unit[Target]
        set udg_STA_TargetID[TempNode] = Target
    else
        //Set the distance the projectile can travel
        set udg_STA_MaxDistance[TempNode] = Distance
        //Resets projectile data (prevents errors when recycling)
        set udg_STA_TargetID[TempNode] = 0
        set udg_STA_Target[TempNode] = null
    endif
   
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to find all units associated with a caster and  //
//  remove the effects, apply healing to units that have ceased   //
//  to be affected, and destroy projectiles that were created by  //
//  the aura (they do not deal damage when this occurs            //
////////////////////////////////////////////////////////////////////
function STA_AuraEnd takes integer Node returns nothing
    //Declare locals
    local integer TempInt = 0
    local integer TempNode = 0

    //Begin search for units
    loop
        //Moves to the next node
        set TempInt = TempInt + 1
        exitwhen TempInt > udg_STA_AbilityCounter
        set TempNode = udg_STA_NextNode[TempNode]
                   
        //Finds units which "belong" to this instance
        if (udg_STA_Caster[TempNode] == udg_STA_Caster[Node]) then

            //Checks the type of unit (2 = affected unit, 3 = Projectile)
            if (udg_STA_StageID[TempNode] == 2) then

                //Checks if the heal effect is in use
                if (STA_UseHealEffect()) then
                    call DestroyEffect(AddSpecialEffectTarget(STA_HealEffect(), udg_STA_Unit[TempNode], STA_HealEffectAttachmentPoint()))
                endif

                //Heals the units
                call SetWidgetLife(udg_STA_Unit[TempNode], udg_STA_CurrentHealth[TempNode] + udg_STA_HeroHealthHeal[Node])
                call SetUnitState(udg_STA_Unit[TempNode], UNIT_STATE_MANA, udg_STA_CurrentMana[TempNode] + udg_STA_HeroManaHeal[Node])
                //Recycles the instance and enables reselection as a target
                call DestroyEffect(udg_STA_CurrentEffect[TempNode])
                call UnitRemoveAbility(udg_STA_Unit[TempNode], STA_BuffAbilityID())
                call GroupRemoveUnit(udg_STA_AffectedUnits,udg_STA_Unit[TempNode])
                call STA_Recycle(TempNode, GetExpiredTimer())
                set TempInt = TempInt - 1
            elseif (udg_STA_StageID[TempNode] == 3) then

                //Checks if the death damage effect is enabled
                if (STA_UseDeathDamageEffect()) then
                    call DestroyEffect(AddSpecialEffectTarget(STA_DeathDamageEffect(), udg_STA_Unit[TempNode], STA_DeathDamageAttachmentPoint()))
                endif

                //Destroys the effect and starts the recycling process for projectiles
                call DestroyEffect(udg_STA_CurrentEffect[TempNode])
                set udg_STA_StageID[TempNode] = 4
            endif

        endif

    endloop

endfunction
////////////////////////////////////////////////////////////////////
//  Function used to update the stats for Heros generating the    //
//  aura, so that level ups are in effect instantly               //
////////////////////////////////////////////////////////////////////
function STA_UpdateAura takes integer Node returns nothing

    //Updates all the stat values of the hero when it levels up
    set udg_STA_SoulFragmentScale[Node] = STA_SoulFragmentProjectileScaleBase() + (STA_SoulFragmentProjectileScalePerLevel() * udg_STA_Level[Node])
    set udg_STA_AOE[Node] = STA_AOEBase() + (STA_AOEPerLevel() * udg_STA_Level[Node])
    set udg_STA_SearchAOE[Node] = STA_SearchAOEBase() + (STA_SearchAOEPerLevel() * udg_STA_Level[Node])
    set udg_STA_ImpactAOE[Node] = STA_ImpactAOEBase() + (STA_ImpactAOEPerLevel() * udg_STA_Level[Node])
    set udg_STA_SoulProjectileSpeed[Node] = STA_SoulFragmentProjectileSpeedBase() + (STA_SoulFragmentProjectileSpeedPerLevel() * udg_STA_Level[Node])
    set udg_STA_SoulProjectileHitTime[Node] = STA_SoulFragmentProjectileHitTimeBase() + (STA_SoulFragmentProjectileHitTimePerLevel() * udg_STA_Level[Node])
    set udg_STA_InitialEffectHealthDamage[Node] = STA_InitialEffectHealthDamageBase() + (STA_InitialEffectHealthDamagePerLevel() * udg_STA_Level[Node])
    set udg_STA_InitialEffectManaDamage[Node] = STA_InitialEffectManaDamageBase() + (STA_InitialEffectManaDamagePerLevel() * udg_STA_Level[Node])
    set udg_STA_DeathHealthDamage[Node] = STA_DeathHealthDamageBase() + (STA_DeathHealthDamagePerLevel() * udg_STA_Level[Node])
    set udg_STA_DeathManaDamage[Node] = STA_DeathManaDamageBase() + (STA_DeathManaDamagePerLevel() * udg_STA_Level[Node])
    set udg_STA_BonusHealthDamagePercent[Node] = STA_BonusHealthDamagePercentBase() + (STA_BonusHealthDamagePercentPerLevel() * udg_STA_Level[Node])
    set udg_STA_BonusManaDamagePercent[Node] = STA_BonusManaDamagePercentBase() + (STA_BonusManaDamagePercentPerLevel() * udg_STA_Level[Node])
    set udg_STA_HeroHealthHeal[Node] = STA_HeroHealthHealBase() + (STA_HeroHealthHealPerLevel() * udg_STA_Level[Node])
    set udg_STA_HeroManaHeal[Node] = STA_HeroManaHealBase() + (STA_HeroManaHealPerLevel() * udg_STA_Level[Node])
   
endfunction

////////////////////////////////////////////////////////////////////
//  The main loop function of the spell, it handles the main      //
//  operations of the ability such as assigning new units,        //
//  moving projectiles and dealing bonus damage to units          //
////////////////////////////////////////////////////////////////////
function STA_Loop takes nothing returns nothing
    //Declare Locals
    local integer Node = 0
    local integer TempNode = 0
    local integer TempInt = 0
    local integer TempInt2 = 0
    local real TempReal
    local real TempReal2
    local real TempReal3
    local real x
    local real y
    local real x2
    local real y2
    local real z
    local real z2
    local real Angle
    local unit u
    local player pl
   
    //Main loop
    loop
        //Moves to the next node
        set TempInt = TempInt + 1
        exitwhen TempInt > udg_STA_AbilityCounter
        set Node = udg_STA_NextNode[Node]

        //Checks which node the unit belongs to
        //Ordered to be more efficient and labelled
        //In order of occurence:
        //StageID = 3, Projectiles
        if (udg_STA_StageID[Node] == 3) then
            set x = GetUnitX(udg_STA_Unit[Node])
            set y = GetUnitY(udg_STA_Unit[Node])
                       
            //Calculate the next position
            set x2 = x + udg_STA_Speed[Node] * Cos(GetUnitFacing(udg_STA_Unit[Node]) * bj_DEGTORAD)
            set y2 = y + udg_STA_Speed[Node] * Sin(GetUnitFacing(udg_STA_Unit[Node]) * bj_DEGTORAD)
            //Check the difference in height between it's current point and next point
            set TempReal = STA_GetZ(x, y)
            set TempReal2 = STA_GetZ(x2, y2)
           
            //Account for height difference
            if not(TempReal == TempReal2) then
                set TempReal = TempReal - TempReal2
            else
                set TempReal = 0               
            endif

            //Move the projectile
            call SetUnitX(udg_STA_Unit[Node], x2)
            call SetUnitY(udg_STA_Unit[Node], y2)
            call SetUnitFlyHeight(udg_STA_Unit[Node], GetUnitFlyHeight(udg_STA_Unit[Node]) + udg_STA_ZSpeed[Node] + TempReal, 0.)
            set udg_STA_DistanceTravelled[Node] = udg_STA_DistanceTravelled[Node] + udg_STA_Speed[Node]

            //Checks the kind of projectile that it is
            if (udg_STA_FragmentType[Node] == true) then
           
                //Checks the distance from the target
                set x2 = GetUnitX(udg_STA_Target[Node])
                set y2 = GetUnitY(udg_STA_Target[Node])
                set TempReal = SquareRoot(Pow(x - x2, 2) + Pow(y - y2, 2))

                //Checks for a collision
                if (TempReal > STA_CollisionSize()) then
                    //No collision, recalculate movement
                    set Angle = Atan2(y2 - y, x2 - x)
                    call SetUnitFacing(udg_STA_Unit[Node], bj_RADTODEG * Angle)

                    //Calculate Z heights
                    set z = STA_GetZ(x,y) + GetUnitFlyHeight(udg_STA_Unit[Node])
                    set z2 = STA_GetZ(x2, y2) + GetUnitFlyHeight(udg_STA_Target[Node]) + STA_SoulFragmentHeightEndOffset()
                   
                    //Calculate Speed
                    if (STA_SoulFragmentMovementType() == true) then
                        set udg_STA_Speed[Node] = (TempReal + udg_STA_DistanceTravelled[Node] / udg_STA_SoulProjectileHitTime[udg_STA_CasterNode[Node]]) * STA_TimerRate()
                    endif
                   
                    //Calculate change is Z speed needed
                    set udg_STA_ZSpeed[Node] = (z2 - z) / (TempReal / udg_STA_Speed[Node])
                else
                    //Collision occured, enable aura effect
                    set udg_STA_Disabled[udg_STA_TargetID[Node]] = false
                    call STA_SoulProjectileDamage(x,y,Node)
                    set udg_STA_StageID[Node] = 4
                    call DestroyEffect(udg_STA_CurrentEffect[Node])
                   
                    //Check if the death damage effect is in use
                    if (STA_UseInitialDamageEffect()) then
                        call DestroyEffect(AddSpecialEffectTarget(STA_InitialDamageEffect(), udg_STA_Unit[Node], STA_InitialDamageAttachmentPoint()))
                    endif

                endif
            //If not homing, check if it's reached it's destination
            elseif (udg_STA_DistanceTravelled[Node] >= udg_STA_MaxDistance[Node]) then
                //Cause Damage
                call STA_SoulProjectileDamage(x,y,Node)
                set udg_STA_StageID[Node] = 4
                call DestroyEffect(udg_STA_CurrentEffect[Node])
 
                //Check if the death damage effect is in use
                if (STA_UseDeathDamageEffect()) then
                    call DestroyEffect(AddSpecialEffectTarget(STA_DeathDamageEffect(), udg_STA_Unit[Node], STA_DeathDamageAttachmentPoint()))
                endif

            endif
              
        //StageID = 2, Affected units
        elseif (udg_STA_StageID[Node] == 2) and (not udg_STA_Disabled[Node]) then
            //Find the units current stats
            set TempReal = GetWidgetLife(udg_STA_Unit[Node])
            set TempReal2 = GetUnitState(udg_STA_Unit[Node], UNIT_STATE_MANA)
            //Increase the effect delay timer
            set udg_STA_EffectDelayTimer[Node] = udg_STA_EffectDelayTimer[Node] + STA_TimerRate()

            //Check if either of the values have been reduced since the last iteration
            if (TempReal < udg_STA_CurrentHealth[Node]) or (TempReal2 < udg_STA_CurrentMana[Node]) then
           
                //Check if the effect is able to be used
                if (udg_STA_EffectDelayTimer[Node] >= STA_EffectReductionDelay()) then
                    set udg_STA_EffectDelayTimer[Node] = 0.
                    call DestroyEffect(AddSpecialEffectTarget(STA_BonusDamageEffect(), udg_STA_Unit[Node], STA_BonusDamageAttachmentPoint()))
                endif
               
                //Check if a health reduction has occured
                if (TempReal < udg_STA_CurrentHealth[Node]) then
                    //Apply bonus damage
                    set TempReal = (udg_STA_CurrentHealth[Node] - TempReal) * udg_STA_BonusHealthDamagePercent[udg_STA_CasterNode[Node]]
                    call UnitDamageTarget(udg_STA_Caster[Node], udg_STA_Unit[Node], TempReal, false, false, STA_AttackType(), STA_DamageType(), STA_WeaponType())
                    set udg_STA_CurrentHealth[Node] = GetWidgetLife(udg_STA_Unit[Node])
                endif

                //Check if a mana reduction has occured
                if (TempReal2 < udg_STA_CurrentMana[Node]) then
                    //Apply bonus damage
                    set TempReal2 = (udg_STA_CurrentMana[Node] - TempReal2) * udg_STA_BonusManaDamagePercent[udg_STA_CasterNode[Node]]
                    call SetUnitState(udg_STA_Unit[Node], UNIT_STATE_MANA, TempReal2)
                endif
               
            endif
           
            //Set up positions
            set x = GetUnitX(udg_STA_Unit[Node])
            set y = GetUnitY(udg_STA_Unit[Node])
            set x2 = GetUnitX(udg_STA_Caster[Node])
            set y2 = GetUnitY(udg_STA_Caster[Node])
            set TempReal = SquareRoot(Pow(x - x2, 2) + Pow(y - y2, 2))
           
            //Check if the unit is still close enough to be affected by the aura
            if (TempReal > udg_STA_AOE[udg_STA_CasterNode[Node]]) then
                //Outside range, deallocate unit
                call DestroyEffect(udg_STA_CurrentEffect[Node])
                call UnitRemoveAbility(udg_STA_Unit[Node], STA_BuffAbilityID())
                call GroupRemoveUnit(udg_STA_AffectedUnits,udg_STA_Unit[Node])
                call STA_Recycle(Node, GetExpiredTimer())
                set TempInt = TempInt - 1
           
            else
           
                //Check if the unit is still alive
                if (IsUnitType(udg_STA_Unit[Node], UNIT_TYPE_DEAD) or GetUnitTypeId(udg_STA_Unit[Node]) == 0)then
               
                    //Remove units related to this aura and end the effect
                    set TempInt2 = 0
                    set TempNode = 0

                    loop
                        //Moves to the next node
                        set TempInt2 = TempInt2 + 1
                        exitwhen TempInt2 > udg_STA_AbilityCounter
                        set TempNode = udg_STA_NextNode[TempNode]
                       
                        //Finds projectiles that are homing in on this unit
                        if (udg_STA_Target[TempNode] == udg_STA_Unit[Node]) and (udg_STA_StageID[TempNode] == 3) then
                            //Destroy them (without damage) and recycle
                            call DestroyEffect(udg_STA_CurrentEffect[TempNode])
                            call DestroyEffect(AddSpecialEffectTarget(STA_DeathDamageEffect(), udg_STA_Unit[TempNode], STA_DeathDamageAttachmentPoint()))
                            set udg_STA_StageID[TempNode] = 4
                        endif

                    endloop

                    //Find units nearby which can be targets
                    call GroupEnumUnitsInRange(udg_STA_TempGroup, x, y, udg_STA_SearchAOE[udg_STA_CasterNode[Node]], null)
                    set pl = udg_STA_OwningPlayer[Node]
                   
                    loop
                        //Scanning through
                        set u = FirstOfGroup(udg_STA_TempGroup)
                        exitwhen u == null
                              
                        //Select all the units to create projectiles to attack
                        if (STA_TargetFilter(u, pl)) and (IsUnitInGroup(u, udg_STA_AffectedUnits)) then
                            //Set up data for the Soul Fragment Projectile
                            set x2 = GetUnitX(u)
                            set y2 = GetUnitY(u)
                            set z = STA_GetZ(x,y) + GetUnitFlyHeight(udg_STA_Unit[Node]) + STA_SoulFragmentHeightStartOffset()
                            set z2 = STA_GetZ(x2, y2) + GetUnitFlyHeight(u) + STA_SoulFragmentHeightEndOffset()
                            set TempReal = SquareRoot(Pow(x - x2, 2) + Pow(y - y2, 2))
                           
                            //Calculate Speed
                            set TempReal2 = STA_GetSpeed(udg_STA_CasterNode[Node], TempReal)
                           
                            //Calculate Z Speed and angle
                            set TempReal3 = (z2 - z) / (TempReal / TempReal2)
                            set Angle = bj_RADTODEG * Atan2(y2 - y, x2 - x)

                            //Create Projectile
                            call STA_CreateProjectile(x, y, z, TempReal3, TempReal2, Angle, TempReal, udg_STA_DeathHealthDamage[udg_STA_CasterNode[Node]], udg_STA_DeathManaDamage[udg_STA_CasterNode[Node]], udg_STA_CasterNode[Node], false, 0)
                        endif

                        //Remove the unit from the unit group
                        call GroupRemoveUnit(udg_STA_TempGroup, u)
                    endloop

                    //Remove effect
                    call DestroyEffect(udg_STA_CurrentEffect[Node])
                    //Recycle instance and enable re-selection as a target
                    call GroupRemoveUnit(udg_STA_AffectedUnits,udg_STA_Unit[Node])
                    call STA_Recycle(Node, GetExpiredTimer())
                    set TempInt = TempInt - 1
                   
                    //Null variables
                    set pl = null
                endif

            endif

        //StageID = 1, Aura source units/heros
        elseif (udg_STA_StageID[Node] == 1) then
       
            //Check if the unit has died
            if (IsUnitType(udg_STA_Caster[Node], UNIT_TYPE_DEAD) or GetUnitTypeId(udg_STA_Caster[Node]) == 0) then
                //The Hero is Dead, end the aura
                call STA_AuraEnd(Node)

                //Checks if the unit was removed from the game
                if (GetUnitTypeId(udg_STA_Caster[Node]) == 0) then
                    //Unit removed, clear up instance
                    call DestroyEffect(udg_STA_CurrentEffect[Node])
                    call STA_AuraEnd(Node)
                    //Recycle the index
                    call STA_Recycle(Node, GetExpiredTimer())
                    set TempInt = TempInt - 1
                endif
        
            else             
                //Find the position of the unit
                set x = GetUnitX(udg_STA_Caster[Node])
                set y = GetUnitY(udg_STA_Caster[Node])
                //Select potential targets of the aura
                set pl = udg_STA_OwningPlayer[Node]
                call GroupEnumUnitsInRange(udg_STA_TempGroup, x, y, udg_STA_AOE[Node], null)
               
                loop
                    //Scanning through
                    set u = FirstOfGroup(udg_STA_TempGroup)
                    exitwhen u == null
                          
                    //Select all the units which are to be affected and create projectiles
                    if (STA_TargetFilter(u, pl)) and (not(IsUnitInGroup(u, udg_STA_AffectedUnits))) then
                       
                        //Prevent this unit from being selected again
                        call GroupAddUnit(udg_STA_AffectedUnits,u)
                           
                        //Adds instance to the linked list
                        set udg_STA_AbilityCounter = udg_STA_AbilityCounter + 1
                           
                        //Checking for recycleable Nodes
                        if (udg_STA_RecyclableNodes == 0) then
                            set udg_STA_NodeNumber = udg_STA_NodeNumber + 1
                            set TempNode = udg_STA_NodeNumber
                        else
                            set udg_STA_RecyclableNodes = udg_STA_RecyclableNodes - 1
                            set TempNode = udg_STA_RecycleNodes[udg_STA_RecyclableNodes]
                        endif

                        //Sets up this Node
                        set udg_STA_NextNode[TempNode] = 0
                        set udg_STA_NextNode[udg_STA_LastNode] = TempNode
                        set udg_STA_PrevNode[TempNode] = udg_STA_LastNode
                        set udg_STA_LastNode = TempNode
                        //Sets up data for this unit
                        set udg_STA_Caster[TempNode] = udg_STA_Caster[Node]
                        set udg_STA_CasterNode[TempNode] = Node
                        set udg_STA_Unit[TempNode] = u
                        set udg_STA_OwningPlayer[TempNode] = udg_STA_OwningPlayer[Node]
                        set udg_STA_CurrentEffect[TempNode] = AddSpecialEffectTarget(STA_SoulFragmentEffect(), u, STA_SoulFragmentAttachmentPoint())
                        set udg_STA_Disabled[TempNode] = true
                        set udg_STA_StageID[TempNode] = 2
                        set udg_STA_CasterNode[TempNode] = Node
                        set udg_STA_CurrentHealth[TempNode] = GetWidgetLife(u)
                        set udg_STA_CurrentMana[TempNode] = GetUnitState(u, UNIT_STATE_MANA)
   
                        //Add buff ability
                        call UnitAddAbility(u, STA_BuffAbilityID())
                        call SetUnitAbilityLevel(u, STA_BuffAbilityID(), R2I(udg_STA_Level[Node]))
                       
                        //Set up data for the Soul Fragment Projectile
                        set x2 = GetUnitX(u)
                        set y2 = GetUnitY(u)
                        set z = STA_GetZ(x,y) + GetUnitFlyHeight(udg_STA_Caster[Node]) + STA_SoulFragmentHeightStartOffset()
                        set z2 = STA_GetZ(x2, y2) + GetUnitFlyHeight(u) + STA_SoulFragmentHeightEndOffset()
                        set TempReal = SquareRoot(Pow(x - x2, 2) + Pow(y - y2, 2))
                           
                        //Calculate Speed
                        set TempReal2 = STA_GetSpeed(Node, TempReal)
                           
                        //Calculate the needed z change
                        set TempReal = (z2 - z) / (TempReal / TempReal2)
                        //Find the facing angle needed
                        set Angle = bj_RADTODEG * Atan2(y2 - y, x2 - x)

                        //Create Projectile
                        call STA_CreateProjectile(x, y, z, TempReal, TempReal2, Angle, 0, udg_STA_InitialEffectHealthDamage[Node], udg_STA_InitialEffectManaDamage[Node], Node, true, TempNode)
                       
                    endif
                    //Remove the unit from the unit group
                    call GroupRemoveUnit(udg_STA_TempGroup, u)
                          
                endloop
               
                //Find the level of the aura ability
                set TempReal = I2R(GetUnitAbilityLevel(udg_STA_Caster[Node], STA_AbilityID()))
               
                //Check if the level has changed
                if (TempReal != udg_STA_Level[Node]) then

                    if (TempReal == 0) then
                        //The unit has forgotten the ability, end it's instance
                        call DestroyEffect(udg_STA_CurrentEffect[Node])
                        call GroupRemoveUnit(udg_STA_CasterGroup,udg_STA_Caster[Node])
                        call STA_AuraEnd(Node)
                        //Recycle the index
                        call STA_Recycle(Node, GetExpiredTimer())
                        set TempInt = TempInt - 1
                    else
                        //Update the aura
                        set udg_STA_Level[Node] = TempReal
                        call STA_UpdateAura(Node)
                    endif

                endif

                //Null variables
                set pl = null
            endif
         
        //StageID = 4, Projectiles to clear up (so death animations can play) 
        elseif (udg_STA_StageID[Node] == 4) and (udg_STA_CurrentDeathTimer[Node] >= STA_DummyRemovalDelay()) then
            set udg_STA_CurrentDeathTimer[Node] = 0.00
           
            //Removes the projectile
            call RemoveUnit(udg_STA_Unit[Node])
            //Recycles the instance
            call STA_Recycle(Node, GetExpiredTimer())
            set TempInt = TempInt - 1
       
        else
            //Increases the death delay timer
            set udg_STA_CurrentDeathTimer[Node] = udg_STA_CurrentDeathTimer[Node] + STA_TimerRate()
        endif
       
    endloop
   
endfunction

////////////////////////////////////////////////////////////////////
//  Caster assignment function, when a new unit learns an ability //
//  or if the unit is found to have the ability through the       //
//  STA_EnableUnits function, this function is then called and    //
//  the unit is passed to potentially add it to the system        //
//  this  then checks if the unit is registered as a caster in    //
//  the linked list, if it is, then it doesn't need to be added   //
//  however, if it doesn't exist as a caster, then the unit will  //
//  be assigned to a node and added to the spell                  //
////////////////////////////////////////////////////////////////////
function STA_NewAura takes unit u returns nothing
    //Set up locals
    local integer Node = 0

    //If the unit wasn't found as a caster, add it as one
    if not(IsUnitInGroup(u, udg_STA_CasterGroup)) then
        set udg_STA_AbilityCounter = udg_STA_AbilityCounter + 1
           
        //Checking for recycleable Nodes
        if (udg_STA_RecyclableNodes == 0) then
            set udg_STA_NodeNumber = udg_STA_NodeNumber + 1
            set Node = udg_STA_NodeNumber
        else
            set udg_STA_RecyclableNodes = udg_STA_RecyclableNodes - 1
            set Node = udg_STA_RecycleNodes[udg_STA_RecyclableNodes]
        endif

        //Sets up this Node
        set udg_STA_NextNode[Node] = 0
        set udg_STA_NextNode[udg_STA_LastNode] = Node
        set udg_STA_PrevNode[Node] = udg_STA_LastNode
        set udg_STA_LastNode = Node
           
        //Set up the data needed for the Aura
        set udg_STA_Level[Node] = I2R(GetUnitAbilityLevel(u, STA_AbilityID()))
        set udg_STA_StageID[Node] = 1
        set udg_STA_Caster[Node] = u
        set udg_STA_CasterNode[Node] = Node
        set udg_STA_OwningPlayer[Node] = GetOwningPlayer(u)
        set udg_STA_CurrentEffect[Node] = AddSpecialEffectTarget(STA_AuraEffect(), u, STA_AuraAttachmentPoint())

        call GroupAddUnit(udg_STA_CasterGroup,u)
        call STA_UpdateAura(Node)
           
        //Applies timer if this is the only instance of the aura
        if (udg_STA_AbilityCounter == 1) then
            call TimerStart(CreateTimer(), STA_TimerRate(), true, function STA_Loop)
        endif
       
    endif

endfunction

////////////////////////////////////////////////////////////////////
//  This function runs whenever a unit learns an ability, it      //
//  checks if the learnt ability is the one used for the aura,    //
//  if it turns out that it is, then it passed the unit to the    //
//  STA_NewAura function to potentially add it to the system      //
////////////////////////////////////////////////////////////////////
function STA_LearnAbility takes nothing returns boolean
    //Create Locals
    local unit u
   
    //Checks if the learnt ability by the triggering unit is the aura ability
    if (GetLearnedSkill() == STA_AbilityID()) then
        //Attempts to add the unit
        set u = GetTriggerUnit()
        call STA_NewAura(u)
        //Nulls variables
        set u = null
    endif
   
    //return a false value as there is no point returning anything else
    return false

endfunction

////////////////////////////////////////////////////////////////////
//  This function's primary purpose is to add all preplaced units //
//  with the aura on the map to the system, however it can also   //
//  be utilised for units which are added at runtime with the     //
//  aura so that they can function correctly despite not running  //
//  the other trigger (the same goes with trained units)          //
////////////////////////////////////////////////////////////////////
function STA_EnableUnits takes nothing returns boolean
    //Sets up locals
    local unit u
   
    //Adds all units on the map to a unit group
    call GroupEnumUnitsInRect(udg_STA_TempGroup, bj_mapInitialPlayableArea, null)
   
    //Scans through the unit group looking for units with the Aura ability
    loop
        set u = FirstOfGroup(udg_STA_TempGroup)
        exitwhen u == null

        //Checks that the selected unit has the aura as an ability
        if (GetUnitAbilityLevel(u, STA_AbilityID()) > 0) then
            //Attempts to add the unit
            call STA_NewAura(u)
        endif
 
        call GroupRemoveUnit(udg_STA_TempGroup, u)
    endloop
   
    //Checks if this function will run constantly, if not then destroy the trigger
    if (not(STA_CheckTimerPeriodic())) then
        call DestroyTrigger(GetTriggeringTrigger())
    endif

    //return a false value as there is no point returning anything else
    return false

endfunction
////////////////////////////////////////////////////////////////////
//  Initialisation trigger, applies the conditions to triggers    //
//  and sets up the global location used to get location Z's      //
////////////////////////////////////////////////////////////////////
function InitTrig_Soul_Tear_Aura takes nothing returns nothing
    //Set up locals
    local trigger STA = CreateTrigger()
    local integer index = 0

    //Set up hero learning trigger
    loop
        //Applies the even to each player in turn
        call TriggerRegisterPlayerUnitEvent(STA, Player(index), EVENT_PLAYER_HERO_SKILL, null)
        set index = index + 1
        exitwhen index == bj_MAX_PLAYER_SLOTS
    endloop

    //Applies the function to add new units to the linked list when they learn abilities
    call TriggerAddCondition(STA, Condition(function STA_LearnAbility))
   
    //Set up initial hero enabling trigger
    set STA = CreateTrigger()
    //Sets up the event listener, if the function is periodic, then it will run constantly instead of just at the start
    call TriggerRegisterTimerEvent(STA, STA_CheckTimerRate(), STA_CheckTimerPeriodic())
    call TriggerAddCondition(STA, Condition(function STA_EnableUnits))

    //Set up the location used to find Z heights
    set udg_STA_ZLoc = Location(0,0)

endfunction
////////////////////////////////////////////////////////////////////
// End of the spell                                               //
////////////////////////////////////////////////////////////////////

Aura Information


- Pure JASS: Over 1400 lines of code (including commenting/readme/etc.)
- Runs on standard world editor without compile errors when saving
- ~60 configurable aspects giving high level user control which reach every aspect of the aura, including removing parts of the spell
- Extensive guide on how to configure the aura to your liking
- Enough Effects to last you a lifetime
- Damage Bonus applies to mana as well as health damage
- Damage Bonus applies to all sources of damage, including the damage dealt by a nearby unit dying
- Configuration enables users to add mana damage to any of the stages of the aura
- Can work on Flying and Ground units as both casters and targets without graphical errors/inconsistencies
- Smooth across different terrain heights including cliffs
- Fully MUI, Recycles correctly
- Fully commented code
- Unique functions, including a drawback effect if your unit dies quickly
- Because the Buff ability is based off the Tornado slow ability, applying object editor values to it, can add the effect of changing the movement speed of affected units as well, however this is not done in the test map to prevent the ability from feeling too "crowded" with effects
[Rainbow]- The ability can be stacked onto any other ability without changing the aura's effect

- Taunt
- Divine Shield
- True Sight
- War Stomp
- Roar
- Critical Strike
- Another Aura!
- Another custom ability!

- Somebody Else's contest entry!

[/Rainbow]

Aura interaction:
- When two units which generate the aura come into contact, the one which started its effect first on the units takes precedence
- Chain effects can be carried across by units affected by another instance of the aura - this is the aura's form of "stacking", making chains able to stretch across the AOE of multiple auras rather than limited to just one, so long as they are in "contact" with eachother
Helpful Files

- Variable Creator: Copy and paste into your map, you now have all the variables needed for the spell to run
- Effect Sets: Temporary storage space for different effects, so that they can be easily accessed/swapped out at will when looking for good combinations
- Manual Addition: Explains how to manually add new Auras to the system if you don't want to rely on the inbuilt systems to do it, particularly for use when creating units with the aura later via triggers - while they can be handled by the system, it's more efficient to utilise the manual addition method.


  • Variable Creator
    • Events
    • Conditions
    • Actions
      • Set STA_AOE[0] = 0.00
      • Set STA_AbilityCounter = 0
      • Set STA_AffectedUnits = STA_TempGroup
      • Set STA_BonusHealthDamagePercent[0] = 0.00
      • Set STA_BonusManaDamagePercent[0] = 0.00
      • Set STA_Caster[0] = STA_Target[0]
      • Set STA_CasterGroup = STA_CasterGroup
      • Set STA_CasterNode[0] = STA_LastNode
      • Set STA_CurrentDeathTimer[0] = 0.00
      • Set STA_CurrentEffect[0] = STA_CurrentEffect[0]
      • Set STA_CurrentHealth[0] = 0.00
      • Set STA_CurrentMana[0] = 0.00
      • Set STA_DeathHealthDamage[0] = 0.00
      • Set STA_DeathManaDamage[0] = 0.00
      • Set STA_Disabled[0] = False
      • Set STA_DistanceTravelled[0] = 0.00
      • Set STA_EffectDelayTimer[0] = 0.00
      • Set STA_FragmentType[0] = False
      • Set STA_HealthDamage[0] = 0.00
      • Set STA_HeroHealthHeal[0] = 0.00
      • Set STA_HeroManaHeal[0] = 0.00
      • Set STA_ImpactAOE[0] = 0.00
      • Set STA_InitialEffectHealthDamage[0] = 0.00
      • Set STA_InitialEffectManaDamage[0] = 0.00
      • Set STA_Level[0] = 0.00
      • Set STA_ManaDamage[0] = 0.00
      • Set STA_MaxDistance[0] = 0.00
      • Set STA_NextNode[0] = 0
      • Set STA_NodeNumber = 0
      • Set STA_OwningPlayer[0] = Neutral Passive
      • Set STA_PrevNode[0] = 0
      • Set STA_RecyclableNodes = 0
      • Set STA_RecycleNodes[0] = 0
      • Set STA_SearchAOE[0] = 0.00
      • Set STA_SoulFragmentScale[0] = 0.00
      • Set STA_SoulProjectileHitTime[0] = 0.00
      • Set STA_SoulProjectileSpeed[0] = 0.00
      • Set STA_Speed[0] = 0.00
      • Set STA_StageID[0] = 0
      • Set STA_Target[0] = STA_Unit[0]
      • Set STA_TargetID[0] = 0
      • Set STA_ZLoc = STA_ZLoc
      • Set STA_ZSpeed[0] = 0.00


JASS:
Default Set:
Aura Effect: "Abilities\\Spells\\Undead\\RegenerationAura\\ObsidianRegenAura.mdl"
Soul Fragment Effect: "Abilities\\Spells\\Undead\\Curse\\CurseTarget.mdl"
Soul Fragment Projectile Effect: "Abilities\\Weapons\\GreenDragonMissile\\GreenDragonMissile.mdl"
Initial Damage Effect: "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl"
Death Damage Effect: "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl"
Bonus Damage Effect: "Abilities\\Spells\\Items\\AIil\\AIilTarget.mdl"
Heal Effect: "Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl"

Fire Set:
Aura Effect: "Abilities\\Spells\\Items\\VampiricPotion\\VampPotionCaster.mdl"
Soul Fragment Effect: "Abilities\\Spells\\Other\\SoulBurn\\SoulBurnbuff.mdl"
Soul Fragment Projectile Effect: "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile.mdl"
Initial Damage Effect: "Abilities\\Spells\\Human\\Feedback\\SpellBreakerAttack.mdl"
Death Damage Effect: "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
Bonus Damage Effect: "Abilities\\Spells\Human\\Feedback\\SpellBreakerAttack.mdl"
Heal Effect: "Abilities\\Spells\\Human\\Feedback\\ArcaneTowerAttack.mdl"

//Blank set for users to temporarily store strings they wish for their sets
Store Set:
Aura Effect:
Soul Fragment Effect:
Soul Fragment Projectile Effect:
Initial Damage Effect:
Death Damage Effect:
Bonus Damage Effect:
Heal Effect:


JASS:
// This Line can be used to manually enter new units into the system
// instead of relying on the inbuilt system
// "unit" should reference whatever unit you want to add, or enter the
// variable name of a variable set to your desired unit
call STA_NewAura(unit)

// Note: The unit you add through this way should already have the ability before doing this
// This means if the unit is a Hero, that it has already learnt at least level 1

// doing this requires this to be added to the map header if you're using the Vanilla WE:
function EnableScriptUsage takes nothing returns nothing
endfunction

Images

Gif

Screenshot

Tooltip

Alternate Theme

Heal on Death

Uneven Terrain Demo

136650d1404414053-zephyr-contest-11-my-aura-your-aura-soul-tear-aura.gif
136642d1404408267-zephyr-contest-11-my-aura-your-aura-soul-tear-aura-chain-kill.png
136660d1404418539-zephyr-contest-11-my-aura-your-aura-soul-tear-aura-tooltip.png
136711d1404507375-zephyr-contest-11-my-aura-your-aura-soul-tear-aura-fire-theme.png
137492d1406219515-zephyr-contest-11-my-aura-your-aura-soul-tear-aura-heal-death.png
137400d1406036290-zephyr-contest-11-my-aura-your-aura-smooth-across-multiple-terrain-heights.png

Changelog


-=V1.00=-
- Initial Upload
-=Sub=-
- Accounted for units being removed entirely (only affects timer)
- Minor efficiency improvements to caster verification
-=V1.01=-
- Changed conditions for checking death
- Made Player(14) configurable for owning dummies
- Changed GetOwningPlayer() into a stored variable
- Removed an extra line of code
- Minor efficiency improvements

[tr]

Keywords:
Aura, Soul, Tear, Burn, Effects, Damage Bonus, Mana Damage, Dark, Curse, Evil, Offensive ability, Chain, Reaction.[/tr]
Contents

Soul Tear Aura (Map)

Reviews
Changed were made. It is a well coded and decent spell with good visuals. Approved. 13:08, 5th Jan 2015 IcemanBo: To get it approved make these few changes...

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
I'm curious why you all are submitting your resources before contest actually ends ^)^
Anyway, I'll get to your spell review tomorrow. However, this I'll state immidiately: you overexpressed the description i.e too much text (aka bloat) is as bad as no description at all. If you want to be as informative as you shown here, please separate triggers and actual documentation.

Good night!
 
I'm curious why you all are submitting your resources before contest actually ends ^)^

People's individual reasons are probably all very different, public advertising, putting your entry more directly in view, gain attraction for the contest in general, etc. all seem like fair enough reasons. Eitherway it's pretty commonplace regardless of the form of contest

If you want to be as informative as you shown here, please separate triggers and actual documentation.

I would have done, and have done so in the past, if it weren't for the overwhelming number of people who post/pm issues with my spells, which were actually explained on how to solve (particularly when it came to importing/adjusting the spell) So I integrate it now in order to force people to actually read the readme's for once XD, though Indeed I'm aware it's quite bloaty. I've had both positive and negative responces on my method of documentation so I choose to stick with it on my own personal preference.
 

Deleted member 219079

D

Deleted member 219079

Why not use vJass? Globals could help you

So many comments lol
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Meaby he has his own reasons to stick with vanilla jass. This does not require JNPG I imagen, but frankly everything that is written as script, most GUIers recognize as chaos and pain made flesh. For most of them vJass or not - would not make a difference.

Yeah, if you implemented whole script in jass you could as well provide portable version with help of vJass.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
JASS:
        //Checks if there are no iterations of the aura left
        //this is ,mostly an "in-case" part of the script, as it
        //is unlikely that this ever returns true (only if a unit unlearns the ability)
        if (udg_STA_AbilityCounter == 0) then
            call DestroyTimer(T)
        endif
it can also occurs if the unit has been removed (in case caster is not a hero), so you should also check whether the unit is still existing in the map or not. Which as fas as I have scanned through your code, you are not checking it.

JASS:
if GetUnitTypeId(u) != 0 then



JASS:
    loop
        //Moves to the next node
        set TempInt = TempInt + 1
        exitwhen TempInt > udg_STA_AbilityCounter
        set Node = udg_STA_NextNode[Node]
       
        //If the unit exists as a caster in the linked list, don't add it
        if (u == udg_STA_Caster[Node]) then
            set scan = true
        endif

    endloop
Can we just use a global unit group variable instead of that lousy slow loop checking?
=>
JASS:
    //If the unit wasn't found as a caster, add it as one
    if not(IsUnitInGroup(u, udg_STA_CasterGroup)) then
        set udg_STA_AbilityCounter = udg_STA_AbilityCounter + 1
            
        //Checking for recycleable Nodes
        if (udg_STA_RecyclableNodes == 0) then
            set udg_STA_NodeNumber = udg_STA_NodeNumber + 1
            set Node = udg_STA_NodeNumber
        else
            set udg_STA_RecyclableNodes = udg_STA_RecyclableNodes - 1
            set Node = udg_STA_RecycleNodes[udg_STA_RecyclableNodes]
        endif
        .....
        call GroupAddUnit(udg_STA_CasterGroup, u)
Much faster


How about new created units with the aura ability? You didn't register them.

That's all I can say, I'm just pointing major flaws. Other than that, it looks like spagghetti with mayonnaise. I mean awesome. :grin:

EDIT: I believe the contest is over and you are not allowed to update the entry so here is my review. Also there are several improvements above.
 
it can also occurs if the unit has been removed (in case caster is not a hero), so you should also check whether the unit is still existing in the map or not. Which as fas as I have scanned through your code, you are not checking it.

The spell works properly in the way it is when units are removed (even if it's a unit ability on a unit and not a hero) thus this change is not needed, but the timer as you correctly point out will keep going if you do this - not a major flaw since removing units is very rare (beyond dummy units) but I'll update the spell to account for it

Can we just use a global unit group variable instead of that lousy slow loop checking?

Certainly - it would function in pretty much the same way, checking a unit is in a unit group requires enumeration through that unit group, the code would be shorter, but I doubt the processor difference is major (if any) and I'd have one more variable for the purpose of a function which isn't ran very often. I opted for the method I used for personal preference, if it's that big of a deal I'd comfortably change it for the sake of minor efficiency improvements

How about new created units with the aura ability? You didn't register them.

Somebody didn't read the readme, I did account for this

JASS:
//----------------------------------------------------------------//
//  Check Timer Periodic: Determines whether or not the Check     //
//  Timer runs constantly all the time without stopping, thus     //
//  allowing it to pick up more than pre-placed units, this       //
//  should always be set to false if no units will gain this      //
//  ability through any means other than learning it, or being    //
//  preplaced unit on the map, as it would just be wasteful       //
constant function STA_CheckTimerPeriodic takes nothing returns boolean
    return false
endfunction


Setting this to true will make the system pick up units placed in the map via either training or create unit functions

Edit: Also yes, it's in the polling stage at this point - I can't update my entered version, I can however update this one without infringing any rules (if the need arises)

Edit2: Changes made (too small to warrant a different version number)
 
Last edited:

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449

The spell works properly in the way it is when units are removed (even if it's a unit ability on a unit and not a hero) thus this change is not needed, but the timer as you correctly point out will keep going if you do this - not a major flaw since removing units is very rare (beyond dummy units) but I'll update the spell to account for it
Does removed unit stay in the list?

Certainly - it would function in pretty much the same way, checking a unit is in a unit group requires enumeration through that unit group, the code would be shorter, but I doubt the processor difference is major (if any) and I'd have one more variable for the purpose of a function which isn't ran very often. I opted for the method I used for personal preference, if it's that big of a deal I'd comfortably change it for the sake of minor efficiency improvements
Are you sure unit group checking works that way? (I dunno)

Somebody didn't read the readme, I did account for this
Yup, I didn't read all of your narrative texts :)
Oh, you do check them periodically, then I suggest not to, it does uneeded processes. Just check units when they entered the map area. Creating one another trigger wont hurt.
JASS:
native TriggerRegisterEnterRegion takes trigger whichTrigger, region whichRegion, boolexpr filter returns event
and this one too maybe?
JASS:
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_TRAIN_FINISH)
One or two additional lines but it does better job.

And one day ago, someone told me that:
JASS:
if (GetWidgetLife(udg_STA_Caster[Node]) <= 0.405) then
is not a proper way to check whether a unit is alive or not. Just use:
JASS:
if not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) != 0 then
I dunno, just sharing my experience :)
 
Does removed unit stay in the list?

No

Are you sure unit group checking works that way? (I dunno)

Fairly sure, eitherway I've already made the change

Yup, I didn't read all of your narrative texts :)
Oh, you do check them periodically, then I suggest not to, it does uneeded processes. Just check units when they entered the map area. Creating one another trigger wont hurt.
One or two additional lines but it does better job.
I think that would be uneeded, note that a unit can have the ability constantly removed/added, a simple "when entering the map" condition would not account for this, the periodic check was chosen as a universal method, and to be efficient is why I made it being periodic optional, this was a low on memory, higher in processing solution and kept the code shorter. If somebody wanted to use an alternative method, that's what the "manual adding" documentation was for and that functionality can be added in a minute (I didn't do this in the test map, because the case was not applicable to the scenario in it)

And one day ago, someone told me that:
JASS:
if (GetWidgetLife(udg_STA_Caster[Node]) <= 0.405) then
is not a proper way to check whether a unit is alive or not. Just use:
JASS:
if not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) != 0 then
I dunno, just sharing my experience :)

I was told the opposite in my own experience, regardless if you see it fail feel free to tell me so, otherwise it would be a "proper way" and less processing
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
JASS:
//----------------------------------------------------------------//
//                        AESTHETICS                              //
//----------------------------------------------------------------//
//  Note: Effects must have valid pathnames and must not be set   //
//  to null values, otherwise graphical errors are likely to      //
//  occur, if an effect can be left null, it will be marked       //
//----------------------------------------------------------------//
//  Aura Effect: This determines the model used for your main     //
//  aura, you want to use the model path and paste it into the    //
//  double quotes or " markers, if the path has only single       //
//  slashes (\) you'll need to changeb it to double slash (\\)    //
//  before you save, the spell will not work if you do not do     //
//  this (if you acidentally save with the single slash (\) it    //
//  will still save but you may find yourself having a lingering  //
//  progress bar at full percentage, you can ignore it  for the   //
//  most part, but if you have it, you probably haven't entered   //
//  this field correctly. Get the model paths from the Object     //
//  editor - find the model you want (the model, not the unit,    //
//  in the models list, select it and hit enter twice, then       //
//  select the path (will look similar to this default value,     //
//  and paste in it here) I suggest doing this with a unit of no  //
//  value (changing the model to get the model path) and then     //
//  reset the field afterwards to get the unit back to normal,    //
//  this function must return a valid value, to make "remove"     //
//  effects from the spell (for those without "Use" functions     //
//  to disable them) you can enter the path of dummy.mdl (this    //
//  can differ from map to map depending on how you imported it)  //
//  and this will cause the system to create invisible effects    //
//  so that the spell can still run properly                      //
Man, this is not just a spell. This is TRAINING COURSE! Even better than that xD
(The main code itself seemingly to only takes 33% of the whole code)

Sorry, for the off-topic... Anyway, I think this should be approved soon.. ^)^
 
  • One global timer can be used, instead of recreating/destroying timers.
  • Use IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0 or UnitAlive native to check if a unit is dead.
    Using (GetWidgetLife(u) > 0.405) is not recommended because the life of a dead unit can still be changed.
  • In function STA_AuraEnd there is no loop needed to find the correct node. It's already given as parameter.

Some minor notes:
  • GetOwningPlayer(udg_STA_Caster[Node]) can be stored into a variable.
  • In function STA_SoulProjectileDamage you set your player variable twice.
  • You can directly return the expression in your filter function.
  • No need to declare a local variable for only one function call. function LearnAbility
  • For unit creation Player(14) could be a constant.
  • No need to null players.
  • No need to null a trigger that is never destroyed.

Well made visuals and everything is alright in OE. Nicely documentated, even a bit too much for me. :D
It's a great spell and I can see the work you've put it! :csmile:
 
  • One global timer can be used, instead of recreating/destroying timers.
  • Use IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0 or UnitAlive native to check if a unit is dead.
    Using (GetWidgetLife(u) > 0.405) is not recommended because the life of a dead unit can still be changed.
Will get on that, granted the global timer isn't much of a major thing - the odds of the timer being destroyed is pretty low in any given play
IcemanBo said:
  • In function STA_AuraEnd there is no loop needed to find the correct node. It's already given as parameter.
The loop is needed - it's not looking for the node which is passed (the unit which is losing the aura) but rather all the projectiles and effects associated with that aura instance so they can be removed
IcemanBo said:
Some minor notes:
  • GetOwningPlayer(udg_STA_Caster[Node]) can be stored into a variable.
  • In function STA_SoulProjectileDamage you set your player variable twice.
  • You can directly return the expression in your filter function.
  • No need to declare a local variable for only one function call. function LearnAbility
  • For unit creation Player(14) could be a constant.
  • No need to null players.
  • No need to null a trigger that is never destroyed.
1 - I'll get on straight away
2 - Ah, my mistake: must've missed it when skimming over it
3 - I kept it seperate so that it can be modified by users if they want to make it not affect/start affecting other certain types of units (I was recommended to do that with my abilities some time ago by a different moderator)
4 - I thought unit variables leaked even if only called once? (hence the need to null them? if not then sure I'll change that)
5 - I'll change that
6 - I was told otherwise (so I suppose it's a preference thing)
7 - Was never told that before, but sure I'll change that
 
I am sorry, you were correct with using the loop in function function STA_AuraEnd. Thanks for mentioning it. :)

Needed changes were done. Approved.

I thought unit variables leaked even if only called once? (hence the need to null them? if not then sure I'll change that)
Once you initialize a local variable, you are right, it has to be nulled. But you only use the variable once, so actually there is no need to declare it:
JASS:
set u = GetTriggerUnit()
call STA_NewAura(u)
set u = null
can be shortened to ->

call STA_NewAura(GetTriggerUnit())

I was told otherwise (so I suppose it's a preference thing)
It is because players can be seen as constants that are never destroyed in game. So it's ID can never recycle. -> No need to null it. (same for triggers that are never destroyed)
 
Level 8
Joined
Jul 14, 2010
Messages
235
Anywhere I can set the duration of the buff to last forever? Right now it's applied by the projectiles(?) but the buff lasts forever as long as the enemy is within AoE of my hero. As fast as they leave the AoE of the hero, they lose the buff. So you can stand on the edge of the AoE and projectiles will shoot out in an OP speed.

I thought first the affected units made their own aura and it had an X duration. But for me at least it seems the hero is giving out the aura
Such a good description, but I can't find anything on the duration of the debuff.
 
You're correct in that the debuff automatically happens whenever a unit leaves the AOE of the hero with the aura - I'm aware this can lead to projectile spamming. Though unless vs. Ais it's unlikely somebody is going to stand around and let you do it, making the projectiles deal 0 damage is a way around any sort of encouragement for doing it as well
 
Yes and No - it would require some code structure changes (the buff is simply removed at this point, to add a duration I'd have to add a timer for removal when they leave the AOE which would then also have to check if they have come back within the AOE since but before the timer runs out (with the timeout being the configurable) anybody is more than welcome to do that themselves if it's what they need but it'll add a lot of extra processing if I did it to the general resource
 
Top