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

Punishing Particles V1.01

  • Like
Reactions: Warseeker
Code

JASS:
////////////////////////////////////////////////////////////////////
//                   PUNISHING PARTICLES V1.00                    //
//  Author: Tank-Commander                                        //
//  Requires: Dummy.mdl                                           //
//  Purpose: Deceptive/Skill-Based/Disable                        //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the spell     //
//                                                                //
//  Credits:                                                      //
//    -  (Dummy.mdl) Vexorian                                     //
//    -  (AquaSpike.mdl) JetFangInferno                           //
//    -  (WispMissile.blp) ~Nightmare                             //
//                                                                //
//  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 instructions, 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                                                    //
//                                                                //
//  While the remaining object editor based data is not required  //
//  to function (provided they're replaced with equivelents)      //
//  it's recommended that they are also imported, if their data   //
//  values are not the same as listed in the configuration, those //
//  configurables will need to be changed to work correctly       //
//----------------------------------------------------------------//
//  This configuration is divided up in order to make it easier   //
//  to modify specific aspects of the spell via locating the      //
//  appropriate section, they are divided up into these sections: //
//      - General                                                 //
//      - Setup                                                   //
//      - Damage                                                  //
//      - Aesthetics                                              //
//      - Physics                                                 //
//      - Loop control (Can be configured but mostly read-only)   //
//----------------------------------------------------------------//
//  WHAT PART IS WHAT?!                                           //
//  This section tells you about what prefix denotes what part    //
//  of the spell                                                  //
//      - Pickup: This is the healing or damaging part left on    //
//                the floor by the first part (target ground)     //
//                of the abilities for enemies to step on         //
//      - PickupProjectile: This is the Pickup while it is still  //
//                in transit (flying through the air)             //
//      - ParticleProjectile: This is the particles that are      //
//                created when the pickup is stepped on           //
//      - DamagingPickup: This is specific to Pickups that deal   //
//                damage                                          //
//      - HealingPickup: This is specific to Pickups that heal    //
//                enemies                                         //
//      - Particle: This is the second part (target enemy) of     //
//                the ability while it is in transit before it    //
//                has activated energy stacks                     //
//      - ParticleRing: These are the energy stacks which float   //
//                above and around an affected enemy              //
//      - ReleasedParticle: These are the particles created by    //
//                an active ParticleRing or energy stack          //
//      - ParticleFinale: This is the explosion when all          //
//                ParticleRings or energy stacks have released    //
//                their energy                                    //
//                                                                //
//  Following this section should make most of the configuration  //
//  self-explanatory, so comments are kept to the less clear      //
//  configurables                                                 //
//----------------------------------------------------------------//
//  CONFIGURABLE INFORMATION/HELP:                                //
//                                                                //
//  - Viewing data values: To see data values in the editor you   //
//  need to press Ctrl + D, to shift back to normal viewing       //
//  press it again                                                //
//                                                                //
//  - Effects: Pathnames for effects used in the spells should    //
//  have two "\"s throughout or the effect will not work (the     //
//  WE progress bar will not go away when saving, however if      //
//  fixed afterwards the save will still work, but the progress   //
//  bar will still remain until the WE is closed)                 //
//  e.g. "units\\human\\Footman\\Footman"                         //
//                                                                //
//  - Effect Scaling: Some effects have scale values below them   //
//  the scale determines the size of the effect and is expressed  //
//  as a real percentage (1.00 = 100%)                            //
//                                                                //
//  - Removing Effects: to remove an effect you don't want from   //
//  the ability, set the model path to that of Dummy.mdl          //
//                                                                //
//  - Base and Per Values: Most configurables have a base and per //
//  value, Base values are what a value is set to regardless of   //
//  other factors. Per values are what a value is set to based on //
//  what the per value is the formula for calculating the result  //
//  is as follows:                                                //
//    - BaseValue + (Factor * PerValue)                           //
//                                                                //
//  - Factors: Per values all have factors, what the factor is,   //
//  is described in the configuration. All factors in this spell  //
//  are per level (level of the ability)                          //
//                                                                //
//  - AttackTypes: This should match the Damage Type of the       //
//  ability, though it can be something else if you wish          //
//                                                                //
//  - DamageTypes: This changes the damage multiplyer vs. enemy   //
//  armour types, note that by default the damage filters         //
//  exclude magic immune units so changing this from MAGIC        //
//  damage will not make them take damage                         //
//                                                                //
//  - WeaponTypes: Generally don't need to be used, should only   //
//  not be null if you particularly wish or need to use them      //
//                                                                //
//  - Physics: This has it's own section at its relevent point    //
//  in the configuration given the complexity of how it works     //
//  and the amount of configurables, please consult with that     //
//  when setting it up                                            //
//                                                                //
//  - StageIDs: Modifying StageIDs in the loop control can cause  //
//   the spellto stop functioning corretly, understanding of the  //
//  code is strongly recommended should you attempt to change     //
//  these                                                         //
//                                                                //
//----------------------------------------------------------------//
//                           GENERAL                              //
//----------------------------------------------------------------//
//  TimerSpeed: This is the rate in which the loop function runs  //
//  default value is 0.031250000, this value cannot be 0, for any //
//  other value the system automatically adjusts so that things   //
//  remain as normal, the exception to this is the decay rate and //
//  energy stack spin speeds which will need to be adjusted to    //
//  match the new TimerSpeed                                      //
//  vals between 0.03 <-> 0.04 are recommended                    //
constant function PP_TimerSpeed takes nothing returns real
    return 0.031250000
endfunction
//----------------------------------------------------------------//
//  DummyID: This is the data value of the unit that serves as    //
//  the dummy, it should have Dummy.mdl set to its model have     //
//  locust as its ability, movement type float (or fly) and 0     //
//  pitch and roll angle for optimal use                          //
constant function PP_DummyID takes nothing returns integer
    return 'u000'
endfunction
//----------------------------------------------------------------//
//  AbilityID: This is the data value of the ability that serves  //
//  as the base ability for this spell, it should be based on     //
//  channel and have the target type set to "Unit or Point        //
//  Target" so that both parts may be used, if you'd like to use  //
//  two different abilities for each part then you will also      //
//  have to modify the appropriate code near the bottom of this   //
//  trigger                                                       //
constant function PP_AbilityID takes nothing returns integer
    return 'A000'
endfunction
//----------------------------------------------------------------//
//  BuffID: This is the data value of the ability used to slow    //
//  units that have stepped on a particle, it should have at      //
//  least two levels, how much each level slows is optional       //
constant function PP_BuffID takes nothing returns integer
    return 'A001'
endfunction
//----------------------------------------------------------------//
//  StunID: This is the data value of the ability used to stun    //
//  enemies during the second part of the ability, it should stun //
//  for at least 0.5 seconds in order to make it constant. If     //
//  you want to remove the stun, you can set this to any other    //
//  ability and it will not run                                   //
constant function PP_StunID takes nothing returns integer
    return 'A002'
endfunction
//----------------------------------------------------------------//
//  StunOrderID: This is the OrderID for the stun ability, this   //
//  should match your StunID ability type, if you do not know the //
//  order ID it can be located via search engine by typing in     //
//  "WC3 Order IDs" though there are other methods such as using  //
//  a trigger to tell you what the order ID for a given ability   //
//  is                                                            //
constant function PP_StunOrderID takes nothing returns integer
    return 852095
endfunction
//----------------------------------------------------------------//
//  DummyPlayer: This is the player who will own all dummy units  //
//  created by this ability, by default this is Player(14)        //
constant function PP_DummyPlayer takes nothing returns player
    return Player(14)
endfunction
//----------------------------------------------------------------//
//                            SETUP                               //
//----------------------------------------------------------------//
//  AttackType: This is the attack type used by the spell, for    //
//  information pertaining to this and DamageType and WeaponType  //
//  consult the CONFIGURABLE INFORMATION/HELP section             //
constant function PP_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_MAGIC
endfunction
//----------------------------------------------------------------//
//  Damageype: This is the damage type used by the spell, for    //
//  information pertaining to this and AttackType and WeaponType  //
//  consult the CONFIGURABLE INFORMATION/HELP section             //
constant function PP_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_MAGIC
endfunction
//----------------------------------------------------------------//
//  WeaponType: This is the weapon type used by the spell, for    //
//  information pertaining to this and DamageType and AttackType  //
//  consult the CONFIGURABLE INFORMATION/HELP section             //
constant function PP_WeaponType takes nothing returns weapontype
    return null
endfunction
//----------------------------------------------------------------//
//  DThenH: This determines which way around Pickups are ordered  //
//  if true then pickups closer to enemies will deal damage if    //
//  false then pickups closer to enemies will heal them instead   //
constant function PP_DThenH takes nothing returns boolean
    return true
endfunction
//----------------------------------------------------------------//
//  PickupTypeDist: This determines how close a pickup must be    //
//  to an enemy for the "closer version" to be applied            //
//  this should closely match the AOE of the dummy ability        //
//  with a bit of an oversight (about 10.00) to make it easier    //
//  to use the ability effectively                                //
constant function PP_PickupTypeDistBase takes nothing returns real
    return 160.00
endfunction
//                                                                //
constant function PP_PickupTypeDistPerLevel takes nothing returns real
    return .0
endfunction
//----------------------------------------------------------------//
//  FilterMaxZ: This is the maximum fly height of a unit that     //
//  can be targetted by this ability if the default target filter //
//  is being used                                                 //
constant function PP_FilterMaxZ takes nothing returns real
    return 50.
endfunction
//----------------------------------------------------------------//
//  PickupAOE: This is the area around the pickup that if any     //
//  valid enemy unit comes within range of, will automatically    //
//  pick up and set off the particle                              //
constant function PP_PickupAOEBase takes nothing returns real
    return 100.
endfunction
//                                                                //
constant function PP_PickupAOEPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  PickupDuration: This is the timer in seconds for which any    //
//  given pickup remains on the map before it is automatically    //
//  removed (they can have negligible damage but still apply the  //
//  normal amount of energy stacks)                               //
constant function PP_PickupDurationBase takes nothing returns real
    return 4.
endfunction
//                                                                //
constant function PP_PickupDurationPerLevel takes nothing returns real
    return 1.
endfunction
//----------------------------------------------------------------//
//  SlowTimeout: This is how long it takes for each level of the  //
//  slow to be reduced to 1 level lower, consider how many levels //
//  units will have before choosing what to set this as           //
constant function PP_SlowTimeoutBase takes nothing returns real
    return 4.00
endfunction
//                                                                //
constant function PP_SlowTimeoutPerLevel takes nothing returns real
    return 0.00
endfunction
//----------------------------------------------------------------//
//  PickupStartLevel: This is the level of the slow that will     //
//  be applied to a unit that stepped on a pickup if they do not  //
//  already have an applied slow                                  //
constant function PP_DamagingPickupStartLevelBase takes nothing returns integer
    return 2
endfunction
//                                                                //
constant function PP_DamagingPickupStartLevelPerLevel takes nothing returns integer
    return 0
endfunction
//                                                                //
constant function PP_HealingPickupStartLevelBase takes nothing returns integer
    return 1
endfunction
//                                                                //
constant function PP_HealingPickupStartLevelPerLevel takes nothing returns integer
    return 0
endfunction
//----------------------------------------------------------------//
//  PickupLevelModifier: This is the way in which stepping on a   //
//  pickup affects the slow level of a unit which already has an  //
//  applied slow                                                  //
constant function PP_DamagingPickupLevelModifierBase takes nothing returns integer
    return 1
endfunction
//                                                                //
constant function PP_DamagingPickupLevelModifierPerLevel takes nothing returns integer
    return 0
endfunction
//                                                                //
constant function PP_HealingPickupLevelModifierBase takes nothing returns integer
    return -1
endfunction
//                                                                //
constant function PP_HealingPickupLevelModifierPerLevel takes nothing returns integer
    return 0
endfunction
//----------------------------------------------------------------//
//                           DAMAGE                               //
//----------------------------------------------------------------//
constant function PP_DamagingPickupDamageHealthBase takes nothing returns real
    return 100.
endfunction
//                                                                //
constant function PP_DamagingPickupDamageHealthPerLevel takes nothing returns real
    return 75.
endfunction
//----------------------------------------------------------------//
constant function PP_DamagingPickupDamageManaBase takes nothing returns real
    return 0.
endfunction
//                                                                //
constant function PP_DamagingPickupDamageManaPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_HealingPickupHealHealthBase takes nothing returns real
    return 400.
endfunction
//                                                                //
constant function PP_HealingPickupHealHealthPerLevel takes nothing returns real
    return 100.
endfunction
//----------------------------------------------------------------//
constant function PP_HealingPickupHealManaBase takes nothing returns real
    return 0.
endfunction
//                                                                //
constant function PP_HealingPickupHealManaPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_PickupPercentageMainBase takes nothing returns real
    return .6
endfunction
//                                                                //
constant function PP_PickupPercentageMainPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  PickupDecayRate: This is the speed at which any given pickup  //
//  loses its potency per timer iteration, to find the number you //
//  want for a given interval/amount lost use the formula:        //
//  Pow(Amount to decay by, 1 / Interval to decay over)           //
//  Keep in mind that the interval must be a factor of the        //
//  TimerSpeed value                                              //
//  e.g. to decay 0.5 of your potency over 1.5 seconds            //
//  Pow(0.5, 1 / (1.5 / 0.03125)) given default TimerSpeed        //
//  Pow(0.5, 1/48)                                                //
//  0.98566319(...)                                               //
//  Which is the default value for this configurable              //
constant function PP_PickupDecayRateBase takes nothing returns real
    return 0.98566
endfunction
//                                                                //
constant function PP_PickupDecayRatePerLevel takes nothing returns real
    return 0.00200
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleHealthDamageBase takes nothing returns real
    return 0.
endfunction
//                                                                //
constant function PP_ParticleHealthDamagePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleManaDamageBase takes nothing returns real
    return 0.
endfunction
//                                                                //
constant function PP_ParticleManaDamagePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  ParticleRingCount: This is the amount of energy stacks that   //
//  are applied to a unit by the prefixed source                  //
constant function PP_HealingPickupParticleRingCountBase takes nothing returns integer
    return 1
endfunction
//                                                                //
constant function PP_HealingPickupParticleRingCountPerLevel takes nothing returns integer
    return 0
endfunction
//                                                                //
constant function PP_DamagingPickupParticleRingCountBase takes nothing returns integer
    return 2
endfunction
//                                                                //
constant function PP_DamagingPickupParticleRingCountPerLevel takes nothing returns integer
    return 0
endfunction
//                                                                //
constant function PP_ParticleParticleRingCountBase takes nothing returns integer
    return 0
endfunction
//                                                                //
constant function PP_ParticleParticleRingCountPerLevel takes nothing returns integer
    return 0
endfunction
//----------------------------------------------------------------//
//  ParticleRingStored: This is the amount of the suffixed        //
//  thing that is kept within each energy stack before it is      //
//  activated                                                     //
constant function PP_ParticleRingStoredHealthDamageBase takes nothing returns real
    return 50.
endfunction
//                                                                //
constant function PP_ParticleRingStoredHealthDamagePerLevel takes nothing returns real
    return 25.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleRingStoredManaDamageBase takes nothing returns real
    return 0.
endfunction
//                                                                //
constant function PP_ParticleRingStoredManaDamagePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  FireRate: This is how fast energy stacks release their energy //
//  and thus directly relates to how long a unit will be stunned  //
//  for (lower number -> faster fire rate)                        //
//  (faster fire rate -> Stunned for a shorter period of time)    //
constant function PP_ParticleRingFireRate takes nothing returns real
    return 0.30
endfunction
//----------------------------------------------------------------//
//  StoredSalvos: This is how many more sets of particles that    //
//  will be launched at an affected unit by a active energy       //
//  energy stacks, per energy stack                               //
constant function PP_ParticleRingStoredSalvosBase takes nothing returns integer
    return 3
endfunction
//                                                                //
constant function PP_ParticleRingStoredSalvosPerLevel takes nothing returns integer
    return 0
endfunction
//----------------------------------------------------------------//
//  FinalePercentage: This is the ratio to the amount of total    //
//  stored energy that is dealt as bonus damage once all energy   //
//  stacks have released their energy (0.5 is 50%)                //
constant function PP_ParticleFinalePercentageHealth takes nothing returns real
    return .5
endfunction
//                                                                //
constant function PP_ParticleFinalePercentageMana takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleFinaleAOE takes nothing returns real
    return 150.
endfunction
//----------------------------------------------------------------//
//                         AESTHETICS                             //
//----------------------------------------------------------------//
//  AttachmentPoint: This is the point on the dummy model that    //
//  all effects are attached to - by default it's set to origin   //
//  and performs most optimally when left that way                //
constant function PP_AttachmentPoint takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
constant function PP_PickupEffect takes nothing returns string
    return "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl"
endfunction
//----------------------------------------------------------------//
constant function PP_PickupDetonateEffect takes nothing returns string
    return "Abilities\\Weapons\\SteamTank\\SteamTankImpact.mdl"
endfunction
//----------------------------------------------------------------//
constant function PP_SlowEffect takes nothing returns string
    return "Abilities\\Spells\\Human\\slow\\slowtarget.mdl"
endfunction
//----------------------------------------------------------------//
constant function PP_PickupScaleBase takes nothing returns real
    return 2.
endfunction
//                                                                //
constant function PP_PickupScalePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_HealingPickupProjectileEffect takes nothing returns string
    return "Abilities\\Weapons\\SeaElementalMissile\\SeaElementalMissile.mdl"
endfunction
//----------------------------------------------------------------//
constant function PP_DamagingPickupProjectileEffect takes nothing returns string
    return "Abilities\\Weapons\\SludgeMissile\\SludgeMissile.mdl"
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleProjectileScaleBase takes nothing returns real
    return 0.25
endfunction
//                                                                //
constant function PP_ParticleProjectileScalePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleEffect takes nothing returns string
    return "Abilities\\Weapons\\FaerieDragonMissile\\FaerieDragonMissile.mdl"
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleScaleBase takes nothing returns real
    return 1.5
endfunction
//                                                                //
constant function PP_ParticleScalePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleRingEffect takes nothing returns string
    return "Objects\\InventoryItems\\CrystalShard\\CrystalShard.mdl"
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleRingSpawnEffect takes nothing returns string
    return "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleRingDestroyEffect takes nothing returns string
    return "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleRingScaleBase takes nothing returns real
    return 1.0
endfunction
//                                                                //
constant function PP_ParticleRingScalePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleReleaseEffect takes nothing returns string
    return "Abilities\\Weapons\\WaterElementalMissile\\WaterElementalMissile.mdl"
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleReleaseScale takes nothing returns real
    return 0.1
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleFinaleinalEffect takes nothing returns string
    return "war3mapImported\\AquaSpike.mdx"
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleFinaleinalScale takes nothing returns real
    return 1.
endfunction
//----------------------------------------------------------------//
//  HeightOffset: This is how high off the ground (0) the effect  //
//  is placed at                                                  //
constant function PP_ParticleFinaleHeightOffset takes nothing returns real
    return 0.
endfunction
//                                                                //
constant function PP_ParticleRingHeightOffset takes nothing returns real
    return 150.
endfunction
//----------------------------------------------------------------//
//  Offset: This is how far away from the middle of the host unit //
//  the effect is placed at                                       //
constant function PP_ParticleRingOffset takes nothing returns real
    return 100.
endfunction
//----------------------------------------------------------------//
//  SpinSpeed: This is how fast the energy stacks spin in radians //
//  per TimerSpeed cycle
constant function PP_ParticleRingSpinSpeed takes nothing returns real
    return 0.02
endfunction
//----------------------------------------------------------------//
//  ProjectileCount: This is the amount of particle projectiles   //
//  that are created by a pickup when they are stepped on         //
//  how many there are (so long as it's 1 or above) does not      //
//  impact damage dealt)                                          //
constant function PP_ParticleProjectileCountBase takes nothing returns integer
    return 7
endfunction
//                                                                //
constant function PP_ParticleProjectileCountPerLevel takes nothing returns integer
    return 0
endfunction
//----------------------------------------------------------------//
//                           PHYSICS                              //
//----------------------------------------------------------------//
//  Physics in this ability are rather complex, so the guide to   //
//  configure them is located here rather than at the top to make //
//  consultation easier                                           //
//                                                                //
//  Each prefixed section is arranged into the same order to      //
//  further ease the difficulty of the section                    //
//      - AOE: This is how close to a target the projectile must  //
//          be to be considered in contact with that target       //
//          not all projectiles have this field                   //
//      - LaunchOffset: Has a height offset variant as well,      //
//          this is how far away from the source that the unit    //
//          will be created, height is how far above              //
//      - LaunchAngle: This is the angle (in degrees) that the    //
//          projectiles will be launched (90 being upwards, 0     //
//          being straight across and -90 being downwards)        //
//      - Speed: This is how fast the projectile initially        //
//          travels, the strength of the launch                   //
//      - TurnRate: This is the strength at which the projectile  //
//          is pulled towards its target - this does affect the   //
//          speed of the projectile and should almost never be    //
//          a negative value as that may cause crashes            //
//      - TurnEfficiency: This is the rate at which existing      //
///         momentum is converted to be toward the target unit    //
//          (this will cause the projectile to slow when turning) //
//          1 converts 50% each cycle, 0.5 converts 25% and so on //
//      - Acell: This is the percentage increase in speed the     //
//          projectile gains or loses each cycle (1.01 being an   //
//          increase of 1% speed)                                 //
//                                                                //
//  Experimenting with this section can create interesting        //
//  results - feel free to play around with the settings but make //
//  sure to change it from any game-crashing setups               //
//----------------------------------------------------------------//
//  HeightLet: This is the maximum fly height any given           //
//  projectile can have and be treated as being on the ground     //
constant function PP_HeightLet takes nothing returns real
    return 20.
endfunction
//----------------------------------------------------------------//
constant function PP_PickupProjectileLaunchOffset takes nothing returns real
    return 90.
endfunction
//----------------------------------------------------------------//
constant function PP_PickupProjectileLaunchAngleBase takes nothing returns real
    return 135.
endfunction
//                                                                //
constant function PP_PickupProjectileLaunchAnglePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_PickupProjectileSpeedBase takes nothing returns real
    return 200.
endfunction
//                                                                //
constant function PP_PickupProjectileSpeedPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_PickupProjectileTurnRateBase takes nothing returns real
    return 5.
endfunction
//                                                                //
constant function PP_PickupProjectileTurnRatePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_PickupProjectileTurnEfficiencyBase takes nothing returns real
    return 0.30
endfunction
//                                                                //
constant function PP_PickupProjectileTurnEfficiencyPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_PickupProjectileAccelBase takes nothing returns real
    return 1.01
endfunction
//                                                                //
constant function PP_PickupProjectileAccelPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleProjectileAOEBase takes nothing returns real
    return 90.
endfunction
//                                                                //
constant function PP_ParticleProjectileAOEPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleProjectileLaunchHeightOffset takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleProjectileLaunchDistOffset takes nothing returns real
    return 20.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleProjectileLaunchAngleBase takes nothing returns real
    return 55.
endfunction
//                                                                //
constant function PP_ParticleProjectileLaunchAnglePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleProjectileSpeedBase takes nothing returns real
    return 175.
endfunction
//                                                                //
constant function PP_ParticleProjectileSpeedPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleProjectileTurnRateBase takes nothing returns real
    return 1.
endfunction
//                                                                //
constant function PP_ParticleProjectileTurnRatePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleProjectileTurnEfficiencyBase takes nothing returns real
    return .25
endfunction
//                                                                //
constant function PP_ParticleProjectileTurnEfficiencyPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleProjectileAccelBase takes nothing returns real
    return 1.01
endfunction
//                                                                //
constant function PP_ParticleProjectileAccelPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleAOEBase takes nothing returns real
    return 55.
endfunction
//                                                                //
constant function PP_ParticleAOEPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleLaunchOffset takes nothing returns real
    return 75.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleLaunchAngleBase takes nothing returns real
    return 0.
endfunction
//                                                                //
constant function PP_ParticleLaunchAnglePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleSpeedBase takes nothing returns real
    return 75.
endfunction
//                                                                //
constant function PP_ParticleSpeedPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleTurnRateBase takes nothing returns real
    return 1.
endfunction
//                                                                //
constant function PP_ParticleTurnRatePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleTurnEfficiencyBase takes nothing returns real
    return .5
endfunction
//                                                                //
constant function PP_ParticleTurnEfficiencyPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleAccelBase takes nothing returns real
    return 1.00
endfunction
//                                                                //
constant function PP_ParticleAccelPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
constant function PP_ReleasedParticleProjectileAOE takes nothing returns real
    return 60.
endfunction
//----------------------------------------------------------------//
constant function PP_ReleasedParticleLaunchOffset takes nothing returns real
    return 50.
endfunction
//----------------------------------------------------------------//
constant function PP_ReleasedParticleProjectileLaunchAngle takes nothing returns real
    return 30.
endfunction
//----------------------------------------------------------------//
constant function PP_ReleasedParticleProjectileSpeed takes nothing returns real
    return 70.
endfunction
//----------------------------------------------------------------//
constant function PP_ReleasedParticleProjectileTurnRate takes nothing returns real
    return 1.
endfunction
//----------------------------------------------------------------//
constant function PP_ReleasedParticleProjectileTurnEfficiency takes nothing returns real
    return .25
endfunction
//----------------------------------------------------------------//
constant function PP_ReleasedParticleProjectileAccel takes nothing returns real
    return 1.01
endfunction
//----------------------------------------------------------------//
//  Aimoffset: This is how far above the fly height of the unit   //
//  that all projectiles will attempt to aim at                   //
constant function PP_TargetAimOffset takes nothing returns real
    return 40.
endfunction
//----------------------------------------------------------------//
//  Gravity: This is how fast all projectiles are pulled toward   //
//  the ground
constant function PP_Gravity takes nothing returns real
    return 2.
endfunction
//----------------------------------------------------------------//
//                        LOOP CONTROL                            //
//----------------------------------------------------------------//
constant function PP_PickupStageID takes nothing returns integer
    return 1
endfunction
//----------------------------------------------------------------//
constant function PP_ReleasedParticleojectileStageID takes nothing returns integer
    return 2
endfunction
//----------------------------------------------------------------//
constant function PP_SlowedUnitStageID takes nothing returns integer
    return 3
endfunction
//----------------------------------------------------------------//
constant function PP_ParticleRingStageID takes nothing returns integer
    return 4
endfunction
//----------------------------------------------------------------//
//                      END OF CONFIGURATION                      //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  Target filter Code (can be configured but requires some       //
//  understanding of programming to do so)                        //
////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
//  Enemy target filter function - Passed units and players, using //
//  them to check if the unit can be treated as an enemy by the    //
//  spellset                                                       //
/////////////////////////////////////////////////////////////////////
function PP_TargetFilter takes unit u, player pl returns boolean

    //Checks if the unit can be used as a target
    return (not IsUnitType(u, UNIT_TYPE_STRUCTURE)) and (GetUnitFlyHeight(u) <= PP_FilterMaxZ()) and (not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)) and (IsUnitEnemy(u, pl)) and (GetUnitTypeId(u) != PP_DummyID()) and not(IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0)
  
endfunction

////////////////////////////////////////////////////////////////////
//  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 PP_GetZ takes real x, real y returns real

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

////////////////////////////////////////////////////////////////////
//  Function used to make sure that the location is within the    //
//  map bounds so that units cannot be moved outside of it and    //
//  get permanently stuck                                         //
////////////////////////////////////////////////////////////////////
function PP_ValidateLocation takes real x, real y returns boolean
  
    //Check if the point is within the map bounds
    return (x < udg_PP_MapMaxX) and (x > udg_PP_MapMinX) and (y < udg_PP_MapMaxY) and (y > udg_PP_MapMinY)

endfunction

////////////////////////////////////////////////////////////////////
//  Function used to recycle instances, so that they can used     //
//  again later, keeping the total array sizes smaller            //
////////////////////////////////////////////////////////////////////
function PP_Recycle takes integer Node returns nothing
      
        if (udg_PP_LastNode == Node) then
            set udg_PP_LastNode = udg_PP_PrevNode[Node]
        endif
          
        //Recycles the node
        set udg_PP_RecycleNodes[udg_PP_RecyclableNodes] = Node
        set udg_PP_RecyclableNodes = udg_PP_RecyclableNodes + 1
        set udg_PP_NextNode[udg_PP_PrevNode[Node]] = udg_PP_NextNode[Node]
        set udg_PP_PrevNode[udg_PP_NextNode[Node]] = udg_PP_PrevNode[Node]
        set udg_PP_AbilityCounter = udg_PP_AbilityCounter - 1

        //Stops the timer if this is the only remaining Node
        if (udg_PP_AbilityCounter == 0) then
            call PauseTimer(udg_PP_Timer)
        endif

endfunction

////////////////////////////////////////////////////////////////////
//  Function used to create new Nodes for the system whenever a   //
//  unit or effect is added to run in the loop function           //
////////////////////////////////////////////////////////////////////
function PP_CreateNode takes nothing returns integer
    //set up local
    local integer Node = 0
  
    //Checking for recycleable Nodes
    if (udg_PP_RecyclableNodes == 0) then
        set udg_PP_NodeNumber = udg_PP_NodeNumber + 1
        set Node = udg_PP_NodeNumber
    else
        set udg_PP_RecyclableNodes = udg_PP_RecyclableNodes - 1
        set Node = udg_PP_RecycleNodes[udg_PP_RecyclableNodes]
    endif

    //Sets up this Node
    set udg_PP_NextNode[Node] = 0
    set udg_PP_NextNode[udg_PP_LastNode] = Node
    set udg_PP_PrevNode[Node] = udg_PP_LastNode
    set udg_PP_LastNode = Node
  
    set udg_PP_AbilityCounter = udg_PP_AbilityCounter + 1
  
    return Node
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to add new particles to the rings of units      //
//  when they have aquired a pickup or been hit by the main       //
//  particle (if it has been configured to add particles)         //
////////////////////////////////////////////////////////////////////
function PP_AddParticleToRing takes unit u, integer UNode, integer Number, real Level returns nothing
    //Set up locals
    local integer Node
    local integer TempInt = 0
    local real TempReal
   
    //Creates the particles
    loop
        set TempInt = TempInt + 1
        exitwhen (TempInt > Number)
       
        //Create Unit
        set Node = PP_CreateNode()
        set udg_PP_Unit[Node] = CreateUnit(PP_DummyPlayer(), PP_DummyID(), GetUnitX(u), GetUnitY(u), 0)
        set udg_PP_CurrentEffect[Node] = AddSpecialEffectTarget(PP_ParticleRingEffect(), udg_PP_Unit[Node], PP_AttachmentPoint())
        call SetUnitScale(udg_PP_Unit[Node], PP_ParticleRingScaleBase() + (PP_ParticleRingScalePerLevel() * Level), 0., 0.)
        if UnitAddAbility(udg_PP_Unit[Node], 'Amrf') and UnitRemoveAbility(udg_PP_Unit[Node], 'Amrf') then
        endif
        call DestroyEffect(AddSpecialEffectTarget(PP_ParticleRingSpawnEffect(), udg_PP_Unit[Node], PP_AttachmentPoint()))
        call SetUnitFlyHeight(udg_PP_Unit[Node], PP_ParticleRingHeightOffset(), 0.)
        //Set up Data of the Particle
        set udg_PP_Caster[Node] = u
        set udg_PP_StageID[Node] = PP_ParticleRingStageID()
        //Update host unit information
        set udg_PP_Integer2[UNode] = udg_PP_Integer2[UNode] + 1
        set udg_PP_Real4[UNode] = udg_PP_Real4[UNode] + PP_ParticleRingStoredHealthDamageBase() + (PP_ParticleRingStoredHealthDamagePerLevel() * Level)
        set udg_PP_Real5[UNode] = udg_PP_Real5[UNode] + PP_ParticleRingStoredManaDamageBase() + (PP_ParticleRingStoredManaDamagePerLevel() * Level)
        set udg_PP_Integer3[UNode] = udg_PP_Integer3[UNode] + PP_ParticleRingStoredSalvosBase() + (PP_ParticleRingStoredSalvosPerLevel() * R2I(Level))
        set TempReal = I2R(udg_PP_Integer2[UNode] * udg_PP_Integer3[UNode])
        set udg_PP_Real7[UNode] = (udg_PP_Real4[UNode] - udg_PP_Real9[UNode]) / TempReal
        set udg_PP_Real8[UNode] = (udg_PP_Real5[UNode] - udg_PP_Real10[UNode]) / TempReal
    endloop
   
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to move all projectile-type entities in the     //
//  abiity - the physics engine of the spell                      //
////////////////////////////////////////////////////////////////////
function PP_Move takes integer Node returns boolean
    //set up locals
    local real x = GetUnitX(udg_PP_Unit[Node])
    local real y = GetUnitY(udg_PP_Unit[Node])
    local real dy = udg_PP_Real9[Node] - y
    local real dx = udg_PP_Real8[Node]- x
    local real x2
    local real y2
    local real Angle = Atan2(dy, dx)
    local real Angle2 = Atan2((PP_GetZ(udg_PP_Real8[Node], udg_PP_Real9[Node]) + udg_PP_Real14[Node]) - (PP_GetZ(x, y) + GetUnitFlyHeight(udg_PP_Unit[Node])), Pow(dx * dx + dy * dy, 0.5))
    local real Angle3 = Atan2(udg_PP_Real4[Node], udg_PP_Real3[Node])
    local real Angle4 = Atan(udg_PP_Real2[Node])
    local real TempReal = Pow(Pow(udg_PP_Real2[Node], 2) + Pow(udg_PP_Real3[Node], 2) + Pow(udg_PP_Real4[Node], 2), 0.5) * udg_PP_Real6[Node]
    local real TempReal2 = 1/(1 + udg_PP_Real6[Node])
   
    //Calculate new velocities
    set udg_PP_Real2[Node] = ((udg_PP_Real2[Node] + (TempReal + udg_PP_Real5[Node]) * Sin(Angle2)) * udg_PP_Real1[Node]) * TempReal2
    set udg_PP_Real3[Node] = ((udg_PP_Real3[Node] + (TempReal + udg_PP_Real5[Node]) * Cos(Angle) * Cos(Angle2)) * udg_PP_Real1[Node]) * TempReal2
    set udg_PP_Real4[Node] = ((udg_PP_Real4[Node] + (TempReal + udg_PP_Real5[Node]) * Sin(Angle) * Cos(Angle2)) * udg_PP_Real1[Node]) * TempReal2
    set udg_PP_Real7[Node] = udg_PP_Real7[Node] + udg_PP_Real2[Node] - PP_Gravity()
   
    set x2 = x + udg_PP_Real3[Node]
    set y2 = y + udg_PP_Real4[Node]
   
    //Make sure the location is within the map bounds
    if PP_ValidateLocation(x2, y2) then
        call SetUnitX(udg_PP_Unit[Node], x2)
        call SetUnitY(udg_PP_Unit[Node], y2)
    endif
   
    //Update target unit information if applicable
    if not(udg_PP_TargetUnit[Node] == null) then
        set udg_PP_Real8[Node] = GetUnitX(udg_PP_TargetUnit[Node])
        set udg_PP_Real9[Node] = GetUnitY(udg_PP_TargetUnit[Node])
        set udg_PP_Real14[Node] = GetUnitFlyHeight(udg_PP_TargetUnit[Node]) + PP_TargetAimOffset()
    endif
   
    //Apply visuals
    call SetUnitFlyHeight(udg_PP_Unit[Node], udg_PP_Real7[Node] - PP_GetZ(x2, y2), 0.)
    call SetUnitFacing(udg_PP_Unit[Node], bj_RADTODEG * Atan2(udg_PP_Real4[Node], udg_PP_Real3[Node]))
    call SetUnitAnimationByIndex(udg_PP_Unit[Node], R2I(Atan2((udg_PP_Real2[Node]), Pow((udg_PP_Real3[Node] * udg_PP_Real3[Node]) + (udg_PP_Real4[Node] * udg_PP_Real4[Node]), 0.5)) * bj_RADTODEG + 0.5) + 90)
   
    //Check if the unit has crashed into the ground
    return (GetUnitFlyHeight(udg_PP_Unit[Node]) <= PP_HeightLet())
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to identify when a projectile has hit it's      //
//  target unit so that damage can be dealt & stun can be         //
//  applied if applicable                                         //
////////////////////////////////////////////////////////////////////
function PP_HitTarget takes integer Node returns boolean
    //Set up locals
    local real x = GetUnitX(udg_PP_Unit[Node])
    local real y = GetUnitY(udg_PP_Unit[Node])
    local real dx = x - udg_PP_Real8[Node]
    local real dy = y - udg_PP_Real9[Node]
    local real dz = (PP_GetZ(udg_PP_Real8[Node], udg_PP_Real9[Node]) + GetUnitFlyHeight(udg_PP_TargetUnit[Node])) - (PP_GetZ(x, y) + GetUnitFlyHeight(udg_PP_Unit[Node]))
   
    //Measure distance between the Unit and its Target and return if it's close enough
    return (dx * dx + dy * dy + dz * dz <= udg_PP_Real10[Node])
endfunction

////////////////////////////////////////////////////////////////////
//  The main function which is used to handle all the key         //
//  components of the spell, including handling all of the spell  //
//  effects of both parts and all units which are being affected  //
//  by anything from the spell                                    //
////////////////////////////////////////////////////////////////////
function PP_Loop takes nothing returns nothing
    //Sets up locals
    local integer Node = 0
    local integer TempNode
    local integer TempNode2
    local integer TempInt = 0
    local integer TempInt2
    local real x
    local real y
    local real x2
    local real y2
    local real dy
    local real dx
    local real TempReal
    local real TempReal2
    local real TempReal3
    local real Angle
    local real Angle2
    local unit u
    local boolean TempBoolean
    local boolean TempBoolean2
   
    loop
        set TempInt = TempInt + 1
        exitwhen TempInt > udg_PP_AbilityCounter
        set Node = udg_PP_NextNode[Node]
       
        //This loop is structured in order to be as efficient as possible
        //As such, functions ran more often are closer to the top
        //So fewer comparisons are run
       
        //Projectiles
        if (udg_PP_StageID[Node] == PP_ReleasedParticleojectileStageID()) then
            //Move the projectile
            if (PP_Move(Node)) then
                //Destroy on crash
                call DestroyEffect(udg_PP_CurrentEffect[Node])
                call KillUnit(udg_PP_Unit[Node])
                call PP_Recycle(Node)
            //Check if it hit the target
            elseif (PP_HitTarget(Node)) then
                call DestroyEffect(udg_PP_CurrentEffect[Node])
               
                //Damage the target appropriately (healing or damaging)
                if (udg_PP_Boolean2[Node]) then
                    call UnitDamageTarget(udg_PP_Caster[Node], udg_PP_TargetUnit[Node], udg_PP_Real11[Node], false, false, PP_AttackType(), PP_DamageType(), PP_WeaponType())
                    call SetUnitState(udg_PP_TargetUnit[Node], UNIT_STATE_MANA, GetUnitState(udg_PP_TargetUnit[Node], UNIT_STATE_MANA) - udg_PP_Real12[Node])
                else
                    call SetWidgetLife(udg_PP_TargetUnit[Node], GetWidgetLife(udg_PP_TargetUnit[Node]) + udg_PP_Real11[Node])
                    call SetUnitState(udg_PP_TargetUnit[Node], UNIT_STATE_MANA, GetUnitState(udg_PP_TargetUnit[Node], UNIT_STATE_MANA) + udg_PP_Real12[Node])
                endif
               
                //Check if the projectile activates particle stacks
                if (udg_PP_Boolean1[Node]) then
                    set TempBoolean = false
                    set TempInt2 = 0
                    set TempNode = 0
                   
                    //Check if the target unit is already part of the linked list
                    loop
                        set TempInt2 = TempInt2 + 1
                        exitwhen (TempInt2 > udg_PP_AbilityCounter) or (TempBoolean)
                        set TempNode = udg_PP_NextNode[TempNode]
                       
                        if (udg_PP_Unit[TempNode] == udg_PP_TargetUnit[Node]) then
                            //Mark the unit as found
                            set TempBoolean = true
                        endif
                       
                    endloop
               
                    //Check if the unit was found
                    if (TempBoolean) then
                   
                        //Check if this unit's particle stacks are active
                        if not(udg_PP_Boolean2[TempNode]) then
                            //Give Particle to the target's particle ring
                            call PP_AddParticleToRing(udg_PP_Unit[TempNode], TempNode, udg_PP_Integer1[Node], udg_PP_Real13[Node])
                       
                            //Check if the unit has any particle stacks
                            if (udg_PP_Integer2[TempNode] > 0) then
                                //Activate stacks
                                set udg_PP_Boolean2[TempNode] = true
                                set udg_PP_Real6[TempNode] = 0
                                set udg_PP_Real9[TempNode] = 0
                                set udg_PP_Real10[TempNode] = 0
                                set udg_PP_Real11[TempNode] = udg_PP_Real13[Node]
                            endif
                           
                        endif
                       
                    else
                        //Set up new node data
                        set TempNode = PP_CreateNode()
                        set udg_PP_Unit[TempNode] = udg_PP_TargetUnit[Node]
                        set udg_PP_Caster[TempNode] = udg_PP_Caster[Node]
                        set udg_PP_Player[TempNode] = udg_PP_Player[Node]
                        set udg_PP_Real3[TempNode] = 0
                        set udg_PP_Real4[TempNode] = 0
                        set udg_PP_Real5[TempNode] = 0
                        set udg_PP_Real6[TempNode] = 0
                        set udg_PP_Real9[TempNode] = 0
                        set udg_PP_Real10[TempNode] = 0
                        set udg_PP_Real11[TempNode] = udg_PP_Real13[Node]
                        set udg_PP_Integer2[TempNode] = 0
                        set udg_PP_Integer3[TempNode] = 0
                        set udg_PP_StageID[TempNode] = PP_SlowedUnitStageID()
                        set udg_PP_Boolean1[TempNode] = false
                        //Give Particle to the target's particle ring
                        call PP_AddParticleToRing(udg_PP_Unit[TempNode], TempNode, udg_PP_Integer1[Node], udg_PP_Real13[Node])
                       
                        //Check if the unit has any particle stacks
                        if (udg_PP_Integer2[TempNode] > 0) then
                            set udg_PP_Boolean2[TempNode] = true
                        else
                            set udg_PP_Boolean2[TempNode] = false
                        endif
                       
                    endif
                   
                endif
               
                //Recycle Projectile
                call KillUnit(udg_PP_Unit[Node])
                call PP_Recycle(Node)
            endif
       
        //Affected Enemy Units
        elseif (udg_PP_StageID[Node] == PP_SlowedUnitStageID()) then
           
            //Check if the unit is ready to be removed
            //(Slow and Stacks Inactive)
            if not(udg_PP_Boolean1[Node]) and not(udg_PP_Boolean2[Node]) then
           
                set TempInt2 = 0
                set TempNode = 0
               
                //Find and remove particle stacks
                loop
                    set TempInt2 = TempInt2 + 1
                    exitwhen (TempInt2 > udg_PP_AbilityCounter)
                    set TempNode = udg_PP_NextNode[TempNode]
                   
                    if (udg_PP_StageID[TempNode] == PP_ParticleRingStageID()) and (udg_PP_Caster[TempNode] == udg_PP_Unit[Node]) then
                        call DestroyEffect(udg_PP_CurrentEffect[TempNode])
                        call DestroyEffect(AddSpecialEffectTarget(PP_ParticleRingDestroyEffect(), udg_PP_Unit[TempNode], PP_AttachmentPoint()))
                        call KillUnit(udg_PP_Unit[TempNode])
                        call PP_Recycle(TempNode)
                    endif
                   
                endloop
               
                //Recycle Unit
                call PP_Recycle(Node)
                set TempInt = TempInt - 1
            else
                set TempBoolean = false
                set x = GetUnitX(udg_PP_Unit[Node])
                set y = GetUnitY(udg_PP_Unit[Node])
               
                //Enemy is slowed
                if (udg_PP_Boolean1[Node]) then
               
                    //Check if the unit is alive
                    if (IsUnitType(udg_PP_Unit[Node], UNIT_TYPE_DEAD)) then
                        //Sets up slow to be removed
                        set udg_PP_Integer1[Node] = 1
                        set udg_PP_Real1[Node] = 0
                    endif
                   
                    //Check slow timeout
                    if (udg_PP_Real1[Node] <= 0) then
                       
                        //Check if the slow is ready to be removed
                        if (udg_PP_Integer1[Node] == 1) then
                            //Remove the slow from the unit
                            call UnitRemoveAbility(udg_PP_Unit[Node], PP_BuffID())
                            call DestroyEffect(udg_PP_CurrentEffect[Node])
                            //Mark the slow as inactive for this unit
                            set udg_PP_Boolean1[Node] = false
                        else
                            //Decrease slow level by 1
                            set udg_PP_Integer1[Node] = udg_PP_Integer1[Node] - 1
                            call SetUnitAbilityLevel(udg_PP_Unit[Node], PP_BuffID(), udg_PP_Integer1[Node])
                            set udg_PP_Real1[Node] = udg_PP_Real2[Node]
                        endif
                       
                    else
                        set udg_PP_Real1[Node] = udg_PP_Real1[Node] - PP_TimerSpeed()
                    endif
                   
                endif
               
                //Particle Ring Activated
                if (udg_PP_Boolean2[Node]) then
               
                    //Check if there are any salvos left and that the unit is still alive
                    if (udg_PP_Integer3[Node] > 0) and not(IsUnitType(udg_PP_Unit[Node], UNIT_TYPE_DEAD)) then
                       
                        //Check if the salvo is ready to be fired
                        if (udg_PP_Real6[Node] <= 0) then
                            //Update tracking variables
                            set udg_PP_Real6[Node] = PP_ParticleRingFireRate()
                            set udg_PP_Integer3[Node] = udg_PP_Integer3[Node] - 1
                            set udg_PP_Real9[Node] = udg_PP_Real9[Node] + (udg_PP_Real7[Node] * R2I(udg_PP_Integer2[Node]))
                            set udg_PP_Real10[Node] = udg_PP_Real10[Node] + (udg_PP_Real8[Node] * R2I(udg_PP_Integer2[Node]))
                            //Mark the salvo as ready to fire
                            set TempBoolean = true
                            //Stun the unit again
                            set TempNode = PP_CreateNode()
                            set udg_PP_Unit[TempNode] = CreateUnit(PP_DummyPlayer(), PP_DummyID(), x, y, 0.)
                            call UnitAddAbility(udg_PP_Unit[TempNode], PP_StunID())
                            call IssueTargetOrderById(udg_PP_Unit[TempNode], PP_StunOrderID(), udg_PP_Unit[Node])
                            call KillUnit(udg_PP_Unit[TempNode])
                            call PP_Recycle(TempNode)
                        else
                            set udg_PP_Real6[Node] = udg_PP_Real6[Node] - PP_TimerSpeed()
                        endif
                       
                    else
                        //Final Explosion
                        set udg_PP_Boolean2[Node] = false
                        set udg_PP_Integer1[Node] = 1
                        set udg_PP_Real1[Node] = 0
                        //Set up aesthetics
                        set TempNode = PP_CreateNode()
                        set udg_PP_Unit[TempNode] = CreateUnit(PP_DummyPlayer(), PP_DummyID(), x, y, 0.)
                        call SetUnitScale(udg_PP_Unit[TempNode], PP_ParticleFinaleinalScale(), 0., 0.)
                        if UnitAddAbility(udg_PP_Unit[TempNode], 'Amrf') and UnitRemoveAbility(udg_PP_Unit[TempNode], 'Amrf') then
                        endif
                        call SetUnitFlyHeight(udg_PP_Unit[TempNode], PP_ParticleFinaleHeightOffset(), 0.)
                        call DestroyEffect(AddSpecialEffectTarget(PP_ParticleFinaleinalEffect(), udg_PP_Unit[TempNode], PP_AttachmentPoint()))

                        call GroupEnumUnitsInRange(udg_PP_TempGroup, x, y, PP_ParticleFinaleAOE(), null)
                       
                        //Damage Enemy Units
                        loop
                            set u = FirstOfGroup(udg_PP_TempGroup)
                            exitwhen u == null
                          
                            if (PP_TargetFilter(u, udg_PP_Player[Node])) then
                                call UnitDamageTarget(udg_PP_Caster[Node], u, (udg_PP_Real4[Node] * PP_ParticleFinalePercentageHealth()) + (udg_PP_Real4[Node] - udg_PP_Real9[Node]), false, false, PP_AttackType(), PP_DamageType(), PP_WeaponType())
                                call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) - ((udg_PP_Real5[Node] * PP_ParticleFinalePercentageMana()) + (udg_PP_Real5[Node] - udg_PP_Real10[Node])))
                            endif
                           
                            call GroupRemoveUnit(udg_PP_TempGroup, u)
                        endloop
                       
                        call KillUnit(udg_PP_Unit[TempNode])
                        call PP_Recycle(TempNode)
                    endif
                   
                endif
               
                //Enemy Particle Ring
                set udg_PP_Real3[Node] = udg_PP_Real3[Node] + PP_ParticleRingSpinSpeed()
                set TempInt2 = 0
                set TempNode = 0
                set TempReal = udg_PP_Real3[Node]
                set TempReal2 = (2 * bj_PI) / I2R(udg_PP_Integer2[Node])
               
                //Find all particle stacks
                loop
                    set TempInt2 = TempInt2 + 1
                    exitwhen (TempInt2 > udg_PP_AbilityCounter)
                    set TempNode = udg_PP_NextNode[TempNode]
                   
                    if (udg_PP_StageID[TempNode] == PP_ParticleRingStageID()) and (udg_PP_Caster[TempNode] == udg_PP_Unit[Node]) then
                        //Update position
                        set TempReal = TempReal + TempReal2
                        set x2 = x + PP_ParticleRingOffset() * Cos(TempReal)
                        set y2 = y + PP_ParticleRingOffset() * Sin(TempReal)
                        call SetUnitX(udg_PP_Unit[TempNode], x2)
                        call SetUnitY(udg_PP_Unit[TempNode], y2)
                        call SetUnitFlyHeight(udg_PP_Unit[TempNode], PP_ParticleRingHeightOffset() - (PP_GetZ(x, y) - PP_GetZ(x2, y2)), 0.)
                        call SetUnitFacing(udg_PP_Unit[TempNode], TempReal * bj_RADTODEG)
                       
                        //Check if a salvo is marked as ready
                        if (TempBoolean) then
                            //Fire projectile
                            set TempNode2 = PP_CreateNode()
                            //Set up salvo data
                            set TempReal3 = PP_GetZ(x2, y2)
                            set dy = (y2 - y)
                            set dx = (x2 - x)
                            set Angle = Atan2(dy, dx)
                            set Angle2 = bj_DEGTORAD * PP_ReleasedParticleProjectileLaunchAngle()
                            //Create Unit
                            set udg_PP_Unit[TempNode2] = CreateUnit(PP_DummyPlayer(), PP_DummyID(), x2, y2, 0.)
                            if UnitAddAbility(udg_PP_Unit[TempNode2], 'Amrf') and UnitRemoveAbility(udg_PP_Unit[TempNode2], 'Amrf') then
                            endif
                            //Set up projectile data
                            set udg_PP_Caster[TempNode2] = udg_PP_Caster[Node]
                            set udg_PP_Player[TempNode2] = udg_PP_Player[Node]
                            set udg_PP_CurrentEffect[TempNode2] = AddSpecialEffectTarget(PP_ParticleReleaseEffect(), udg_PP_Unit[TempNode2], PP_AttachmentPoint())
                            call SetUnitScale(udg_PP_Unit[TempNode2], PP_ParticleReleaseScale(), 0., 0.)
                            set udg_PP_Real1[TempNode2] = PP_ReleasedParticleProjectileAccel()
                            set udg_PP_Real2[TempNode2] = PP_ReleasedParticleProjectileSpeed() * Sin(Angle2)
                            set udg_PP_Real3[TempNode2] = PP_ReleasedParticleProjectileSpeed() * Cos(Angle) * Cos(Angle2)
                            set udg_PP_Real4[TempNode2] = PP_ReleasedParticleProjectileSpeed() * Sin(Angle) * Cos(Angle2)
                            set udg_PP_Real5[TempNode2] = PP_ReleasedParticleProjectileTurnRate()
                            set udg_PP_Real6[TempNode2] = PP_ReleasedParticleProjectileTurnEfficiency()
                            set udg_PP_Real7[TempNode2] = TempReal3 + PP_ParticleRingHeightOffset() + PP_ReleasedParticleLaunchOffset()
                            set udg_PP_Real8[TempNode2] = x2
                            set udg_PP_Real9[TempNode2] = y2
                            set udg_PP_Real10[TempNode2] = PP_ReleasedParticleProjectileAOE() * PP_ReleasedParticleProjectileAOE()
                            set udg_PP_Real11[TempNode2] = udg_PP_Real7[Node]
                            set udg_PP_Real12[TempNode2] = udg_PP_Real8[Node]
                            set udg_PP_TargetUnit[TempNode2] = udg_PP_Unit[Node]
                            set udg_PP_Real14[TempNode2] = GetUnitFlyHeight(udg_PP_Caster[TempNode]) + PP_TargetAimOffset()
                            set udg_PP_Boolean1[TempNode2] = false
                            set udg_PP_Boolean2[TempNode2] = true
                            set udg_PP_StageID[TempNode2] = PP_ReleasedParticleojectileStageID()
                            call SetUnitFlyHeight(udg_PP_Unit[TempNode2], udg_PP_Real7[TempNode2] - TempReal3, 0.)                           
                        endif
                       
                    endif
                   
                endloop
               
            endif

        //Pickups
        elseif (udg_PP_StageID[Node] == PP_PickupStageID()) then
       
            //Check if the pickup is on the ground
            if (udg_PP_Boolean1[Node]) then
           
                //Move pickup
                if (PP_Move(Node)) then
                    //Mark it as on the ground
                    set udg_PP_Boolean1[Node] = false
                    //Set up data for the grounded pickup
                    set udg_PP_Real1[Node] = PP_ParticleProjectileAccelBase() + (PP_ParticleProjectileAccelPerLevel() * udg_PP_Real10[Node])
                    set udg_PP_Real2[Node] = PP_ParticleProjectileSpeedBase() + (PP_ParticleProjectileSpeedPerLevel() * udg_PP_Real10[Node])
                    set udg_PP_Real3[Node] = PP_PickupAOEBase() + (PP_PickupAOEPerLevel() * udg_PP_Real10[Node])
                    set udg_PP_Real4[Node] = PP_PickupDurationBase() + (PP_PickupDurationPerLevel() * udg_PP_Real10[Node])
                    set udg_PP_Real5[Node] = PP_ParticleProjectileTurnRateBase() + (PP_ParticleProjectileTurnRatePerLevel() * udg_PP_Real10[Node])
                    set udg_PP_Real6[Node] = PP_ParticleProjectileTurnEfficiencyBase() + (PP_ParticleProjectileTurnEfficiencyPerLevel() * udg_PP_Real10[Node])
                    set udg_PP_Real7[Node] = udg_PP_Real7[Node] + PP_ParticleProjectileLaunchHeightOffset()
               
                    if (udg_PP_Boolean2[Node]) then
                        set udg_PP_Real8[Node] = PP_DamagingPickupDamageHealthBase() + (PP_DamagingPickupDamageHealthPerLevel() * udg_PP_Real10[Node])
                        set udg_PP_Real9[Node] = PP_DamagingPickupDamageManaBase() + (PP_DamagingPickupDamageManaPerLevel() * udg_PP_Real10[Node])
                    else
                        set udg_PP_Real8[Node] = PP_HealingPickupHealHealthBase() + (PP_HealingPickupHealHealthPerLevel() * udg_PP_Real10[Node])
                        set udg_PP_Real9[Node] = PP_HealingPickupHealManaBase() + (PP_HealingPickupHealManaPerLevel() * udg_PP_Real10[Node])
                    endif
                   
                    set udg_PP_Real11[Node] = PP_PickupPercentageMainBase() + (PP_PickupPercentageMainPerLevel() * udg_PP_Real10[Node])
                    set udg_PP_Real12[Node] = PP_PickupDurationBase() + (PP_PickupDurationPerLevel() * udg_PP_Real10[Node])
                    set udg_PP_Real13[Node] = PP_PickupDecayRateBase() + (PP_PickupDecayRatePerLevel() * udg_PP_Real10[Node])
                endif
           
            else
                //Lower potency of pickup
                set udg_PP_Real8[Node] = udg_PP_Real8[Node] * udg_PP_Real13[Node]
                set udg_PP_Real9[Node] = udg_PP_Real9[Node] * udg_PP_Real13[Node]
                set udg_PP_Real12[Node] = udg_PP_Real12[Node] - PP_TimerSpeed()
               
                //Check if the projectile is still alive
                if (udg_PP_Real12[Node] > 0) then
                    //Look for enemy units
                    set x = GetUnitX(udg_PP_Unit[Node])
                    set y = GetUnitY(udg_PP_Unit[Node])
                    set TempBoolean = true
                    call GroupEnumUnitsInRange(udg_PP_TempGroup, x, y, udg_PP_Real3[Node], null)
                  
                    //Damage Enemy Units
                    loop
                        set u = FirstOfGroup(udg_PP_TempGroup)
                        exitwhen u == null
                      
                        if (PP_TargetFilter(u, udg_PP_Player[Node])) and (TempBoolean) then
                            //Limit the amount of units damaged to 1
                            set TempBoolean = false
                            //Get the damage which belongs to the explosion and projectiles
                            set udg_PP_Real12[Node] = udg_PP_Real8[Node] * udg_PP_Real11[Node]
                            set udg_PP_Real11[Node] = udg_PP_Real9[Node] * udg_PP_Real11[Node]
                            set udg_PP_Real8[Node] = (udg_PP_Real8[Node] - udg_PP_Real12[Node]) / udg_PP_Integer1[Node]
                            set udg_PP_Real9[Node] = (udg_PP_Real9[Node] - udg_PP_Real11[Node]) / udg_PP_Integer1[Node]
                           
                            //Blow up the pickup
                            call DestroyEffect(AddSpecialEffectTarget(PP_PickupDetonateEffect(), u, PP_AttachmentPoint()))
                            call DestroyEffect(udg_PP_CurrentEffect[Node])
                           
                            set TempInt2 = 0
                            set TempBoolean2 = false
                            set TempNode = 0
                           
                            //Check if the target already belongs to the linked list
                            loop
                                set TempInt2 = TempInt2 + 1
                                exitwhen (TempInt2 > udg_PP_AbilityCounter) or (TempBoolean2 == true)
                                set TempNode = udg_PP_NextNode[TempNode]
                               
                                if (udg_PP_Unit[TempNode] == u) then
                                    //Mark the unit as found
                                    set TempBoolean2 = true
                                endif
                                   
                            endloop
                           
                            //Check if the unit was found
                            if (TempBoolean2) then
                               
                                //Check if the slow is active on this unit
                                if (UnitAddAbility(udg_PP_Unit[TempNode], PP_BuffID())) then
                                    //Set up slow
                                    call SetUnitAbilityLevel(udg_PP_Unit[TempNode], PP_BuffID(), udg_PP_Integer3[Node])
                                    set udg_PP_Real1[TempNode] = PP_SlowTimeoutBase() + (PP_SlowTimeoutPerLevel() * udg_PP_Real10[Node])
                                    set udg_PP_Real2[TempNode] = udg_PP_Real1[TempNode]
                                    set udg_PP_Real3[TempNode] = 0
                                    set udg_PP_Integer1[TempNode] = udg_PP_Integer3[Node]
                                    //Mark the slow as active
                                    set udg_PP_Boolean1[TempNode] = true
                                    set udg_PP_CurrentEffect[TempNode] = AddSpecialEffectTarget(PP_SlowEffect(), udg_PP_Unit[TempNode], PP_AttachmentPoint())
                                else
                                    //Modify the slow as appropriate (increase or decrease level)
                                    set udg_PP_Real1[TempNode] = udg_PP_Real2[TempNode]
                                   
                                    if (udg_PP_Integer1[TempNode] + udg_PP_Integer4[Node] <= 0) then
                                        set udg_PP_Integer1[TempNode] = 1
                                        call SetUnitAbilityLevel(udg_PP_Unit[TempNode], PP_BuffID(), udg_PP_Integer1[TempNode])
                                    else
                                        set udg_PP_Integer1[TempNode] = udg_PP_Integer1[TempNode] + udg_PP_Integer4[Node]
                                        call SetUnitAbilityLevel(udg_PP_Unit[TempNode], PP_BuffID(), udg_PP_Integer1[TempNode])
                                    endif      
                               
                                endif
                               
                            else
                                //Set up new node and the slow
                                set TempNode = PP_CreateNode()
                                set udg_PP_Unit[TempNode] = u
                                call UnitAddAbility(udg_PP_Unit[TempNode], PP_BuffID())
                                call SetUnitAbilityLevel(udg_PP_Unit[TempNode], PP_BuffID(), udg_PP_Integer3[Node])
                                set udg_PP_Caster[TempNode] = udg_PP_Caster[Node]
                                set udg_PP_Player[TempNode] = udg_PP_Player[Node]
                                set udg_PP_Real1[TempNode] = PP_SlowTimeoutBase() + (PP_SlowTimeoutPerLevel() * udg_PP_Real10[Node])
                                set udg_PP_Real2[TempNode] = udg_PP_Real1[TempNode]
                                set udg_PP_Real3[TempNode] = 0
                                set udg_PP_Real4[TempNode] = 0
                                set udg_PP_Real5[TempNode] = 0
                                set udg_PP_Real9[TempNode] = 0
                                set udg_PP_Real10[TempNode] = 0
                                set udg_PP_Integer1[TempNode] = udg_PP_Integer3[Node]
                                set udg_PP_Integer2[TempNode] = 0
                                set udg_PP_Integer3[TempNode] = 0
                                set udg_PP_StageID[TempNode] = PP_SlowedUnitStageID()
                                //Mark the slow as active
                                set udg_PP_Boolean1[TempNode] = true
                                set udg_PP_Boolean2[TempNode] = false
                                set udg_PP_CurrentEffect[TempNode] = AddSpecialEffectTarget(PP_SlowEffect(), udg_PP_Unit[TempNode], PP_AttachmentPoint())
                            endif
                           
                            //Damage the target unit
                            if (udg_PP_Boolean2[Node]) then
                                call UnitDamageTarget(udg_PP_Caster[Node], u, udg_PP_Real12[Node], false, false, PP_AttackType(), PP_DamageType(), PP_WeaponType())
                                call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) - udg_PP_Real11[Node])
                            else
                                call SetWidgetLife(u, GetWidgetLife(u) + udg_PP_Real12[Node])
                                call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) + udg_PP_Real11[Node])
                            endif
                           
                            //Give Particle to the target's particle ring
                            call PP_AddParticleToRing(u, TempNode, udg_PP_Integer2[Node], udg_PP_Real10[Node])
                           
                            //Set up projectile data
                            set TempReal = PP_ParticleProjectileSpeedBase() + (PP_ParticleProjectileSpeedPerLevel() * udg_PP_Real10[Node])
                            set TempReal3 = PP_ParticleProjectileAOEBase() + (PP_ParticleProjectileAOEPerLevel() * udg_PP_Real10[Node])
                            set Angle2 = bj_DEGTORAD * (PP_ParticleProjectileLaunchAngleBase() + (PP_ParticleProjectileLaunchAnglePerLevel() * udg_PP_Real10[Node]))
                            set udg_PP_Real10[Node] = PP_ParticleProjectileScaleBase() + (PP_ParticleProjectileScalePerLevel() * udg_PP_Real10[Node])
                            set TempInt2 = 0
                            set Angle = 0
                            set TempReal2 = (360 / udg_PP_Integer1[Node]) * bj_DEGTORAD
                           
                            //Create projectiles
                            loop
                                set TempInt2 = TempInt2 + 1
                                exitwhen (TempInt2 > udg_PP_Integer1[Node])
                               
                                set Angle = Angle + TempReal2
                                set TempNode = PP_CreateNode()
                                //Apply projectile data
                                set udg_PP_Boolean1[TempNode] = false
                                set udg_PP_Boolean2[TempNode] = udg_PP_Boolean2[Node]
                                set udg_PP_Real1[TempNode] = udg_PP_Real1[Node]
                                set udg_PP_Real2[TempNode] = TempReal * Sin(Angle2)
                                set udg_PP_Real3[TempNode] = TempReal * Cos(Angle) * Cos(Angle2)
                                set udg_PP_Real4[TempNode] = TempReal * Sin(Angle) * Cos(Angle2)
                                set udg_PP_Real5[TempNode] = udg_PP_Real5[Node]
                                set udg_PP_Real6[TempNode] = udg_PP_Real6[Node]
                                set udg_PP_Real7[TempNode] = udg_PP_Real7[Node]
                                set udg_PP_TargetUnit[TempNode] = u
                                set udg_PP_Real8[TempNode] = GetUnitX(u)
                                set udg_PP_Real9[TempNode] = GetUnitY(u)
                                set udg_PP_Real10[TempNode] = TempReal3 * TempReal3
                                set udg_PP_Real11[TempNode] = udg_PP_Real8[Node]
                                set udg_PP_Real12[TempNode] = udg_PP_Real9[Node]
                                set udg_PP_Real14[TempNode] = GetUnitFlyHeight(udg_PP_TargetUnit[TempNode]) + PP_TargetAimOffset()
                                set udg_PP_StageID[TempNode] = PP_ReleasedParticleojectileStageID()
                                set udg_PP_Integer1[TempNode] = 0
                               
                                //Create unit
                                set udg_PP_Unit[TempNode] = CreateUnit(PP_DummyPlayer(), PP_DummyID(), x + PP_ParticleProjectileLaunchDistOffset() * Cos(Angle), y + PP_ParticleProjectileLaunchDistOffset() * Sin(Angle), bj_RADTODEG * Atan2(udg_PP_Real4[TempNode], udg_PP_Real3[TempNode]))
                                set udg_PP_Caster[TempNode] = udg_PP_Caster[Node]
                                set udg_PP_Player[TempNode] = udg_PP_Player[Node]
                                if UnitAddAbility(udg_PP_Unit[TempNode], 'Amrf') and UnitRemoveAbility(udg_PP_Unit[TempNode], 'Amrf') then
                                endif
                                call SetUnitScale(udg_PP_Unit[TempNode], udg_PP_Real10[Node], 0., 0.)
                               
                                //Put on the right effect for its type
                                if (udg_PP_Boolean2[Node]) then
                                    set udg_PP_CurrentEffect[TempNode] = AddSpecialEffectTarget(PP_DamagingPickupProjectileEffect(), udg_PP_Unit[TempNode], PP_AttachmentPoint())
                                else
                                    set udg_PP_CurrentEffect[TempNode] = AddSpecialEffectTarget(PP_HealingPickupProjectileEffect(), udg_PP_Unit[TempNode], PP_AttachmentPoint())
                                endif
                               
                            endloop
                           
                            //Recycle Pickup
                            call KillUnit(udg_PP_Unit[Node])
                            call PP_Recycle(Node)
                        endif
                      
                        call GroupRemoveUnit(udg_PP_TempGroup, u)
                    endloop
               
                else
                    //Recycle Pickup
                    call DestroyEffect(udg_PP_CurrentEffect[Node])
                    call KillUnit(udg_PP_Unit[Node])
                    call PP_Recycle(Node)
                endif
                   
            endif
           
        endif
       
    endloop
   
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to check that the ability being cast by a hero  //
//  is the right ability and check what the target is in order    //
//  to play the correct part of the ability                       //
////////////////////////////////////////////////////////////////////
function PP_OnCast takes nothing returns boolean
    //Set up locals
    local integer SpellID = GetSpellAbilityId()
    local integer Node
    local integer iLevel
    local unit Target = GetSpellTargetUnit()
    local unit u
    local unit u2
    local real x
    local real y
    local real x2
    local real y2
    local real dy
    local real dx
    local real rLevel
    local real Angle
    local real Angle2
    local real TempReal
    local boolean TempBoolean
   
    //Check that the right spell was cast
    if (SpellID == PP_AbilityID()) then
        //Set up spell data
        set u = GetTriggerUnit()
        set x = GetUnitX(u)
        set y = GetUnitY(u)
        set iLevel = GetUnitAbilityLevel(u, PP_AbilityID())
        set rLevel = I2R(iLevel)
        set Node = PP_CreateNode()
        //Create Unit
        set udg_PP_Unit[Node] = CreateUnit(PP_DummyPlayer(), PP_DummyID(), x, y, 0.)
        set udg_PP_Caster[Node] = u
        set udg_PP_Player[Node] = GetOwningPlayer(u)
        if UnitAddAbility(udg_PP_Unit[Node], 'Amrf') and UnitRemoveAbility(udg_PP_Unit[Node], 'Amrf') then
        endif
           
        //Check what part of the spell to use
        if (Target == null) then
            //Energy Pickup Type
            set x2 = GetSpellTargetX()
            set y2 = GetSpellTargetY()
            set dy = (y2 - y)
            set dx = (x2 - x)
            set Angle = Atan2(dy, dx)
            set Angle2 = bj_DEGTORAD * (PP_PickupProjectileLaunchAngleBase() + (PP_PickupProjectileLaunchAnglePerLevel() * rLevel))
            //Apply effects
            set udg_PP_CurrentEffect[Node] = AddSpecialEffectTarget(PP_PickupEffect(), udg_PP_Unit[Node], PP_AttachmentPoint())
            call SetUnitScale(udg_PP_Unit[Node], PP_PickupScaleBase() + (PP_PickupScalePerLevel() * rLevel), 0., 0.)
            set TempReal = PP_PickupProjectileSpeedBase() + (PP_PickupProjectileSpeedPerLevel() * rLevel)
            //Set up pickup projectile data
            set udg_PP_StageID[Node] = PP_PickupStageID()
            set udg_PP_Boolean1[Node] = true
            set udg_PP_Real1[Node] = PP_PickupProjectileAccelBase() + (PP_PickupProjectileAccelPerLevel() * rLevel)
            set udg_PP_Real2[Node] = TempReal * Sin(Angle2)
            set udg_PP_Real3[Node] = TempReal * Cos(Angle) * Cos(Angle2)
            set udg_PP_Real4[Node] = TempReal * Sin(Angle) * Cos(Angle2)
            set udg_PP_Real5[Node] = PP_PickupProjectileTurnRateBase() + (PP_PickupProjectileTurnRatePerLevel() * rLevel)
            set udg_PP_Real6[Node] = PP_PickupProjectileTurnEfficiencyBase() + (PP_PickupProjectileTurnEfficiencyPerLevel() * rLevel)
            set udg_PP_Real7[Node] = PP_GetZ(x, y) + GetUnitFlyHeight(u) + PP_PickupProjectileLaunchOffset()
            set udg_PP_Real8[Node] = x2
            set udg_PP_Real9[Node] = y2
            set udg_PP_Real10[Node] = rLevel
            set udg_PP_Real14[Node] = 0.
            set udg_PP_Integer1[Node] = PP_ParticleProjectileCountBase() + (PP_ParticleProjectileCountPerLevel() * iLevel)
           
            //Find units near the target zone
            call GroupEnumUnitsInRange(udg_PP_TempGroup, x2, y2, PP_PickupTypeDistBase() + (PP_PickupTypeDistPerLevel() * rLevel), null)
            set TempBoolean = false
           
            //Check if there are any units
            loop
                set u2 = FirstOfGroup(udg_PP_TempGroup)
                exitwhen (u2 == null)
               
                if (PP_TargetFilter(u2, udg_PP_Player[Node])) then
                    //Mark that a unit was found
                    set TempBoolean = true
                endif
               
                call GroupRemoveUnit(udg_PP_TempGroup, u2)
            endloop
           
            //Check if a unit was found as well as
            //Check which way around the player wants
            //(Damaging and then Heal or Heal and then Damaging)
            if (TempBoolean) then
                set udg_PP_Boolean2[Node] = PP_DThenH()
            else
                set udg_PP_Boolean2[Node] = not(PP_DThenH())
            endif
           
            //Apply appropriate data for damaging or healing pickups
            if (udg_PP_Boolean2[Node]) then
                set udg_PP_Integer2[Node] = PP_DamagingPickupParticleRingCountBase() + (PP_DamagingPickupParticleRingCountPerLevel() * iLevel)
                set udg_PP_Integer3[Node] = PP_DamagingPickupStartLevelBase() + (PP_DamagingPickupStartLevelPerLevel() * iLevel)
                set udg_PP_Integer4[Node] = PP_DamagingPickupLevelModifierBase() + (PP_DamagingPickupLevelModifierPerLevel() * iLevel)
            else
                set udg_PP_Integer2[Node] = PP_HealingPickupParticleRingCountBase() + (PP_HealingPickupParticleRingCountPerLevel() * iLevel)
                set udg_PP_Integer3[Node] = PP_HealingPickupStartLevelBase() + (PP_HealingPickupStartLevelPerLevel() * iLevel)
                set udg_PP_Integer4[Node] = PP_HealingPickupLevelModifierBase() + (PP_HealingPickupLevelModifierPerLevel() * iLevel)
            endif
           
            set udg_PP_TargetUnit[Node] = Target
            call SetUnitFlyHeight(udg_PP_Unit[Node], udg_PP_Real7[Node] - PP_GetZ(x, y), 0.)
           
        else
            //Particle Ring Activator Type
            set x2 = GetUnitX(Target)
            set y2 = GetUnitY(Target)
            set dy = (y2 - y)
            set dx = (x2 - x)
            set Angle = Atan2(dy, dx)
            set Angle2 = bj_DEGTORAD * (PP_ParticleLaunchAngleBase() + (PP_ParticleLaunchAnglePerLevel() * rLevel))
            //Apply effects
            set udg_PP_CurrentEffect[Node] = AddSpecialEffectTarget(PP_ParticleEffect(), udg_PP_Unit[Node], PP_AttachmentPoint())
            call SetUnitScale(udg_PP_Unit[Node], PP_ParticleScaleBase() + (PP_ParticleScalePerLevel() * rLevel), 0., 0.)
            set TempReal = PP_ParticleSpeedBase() + (PP_ParticleSpeedPerLevel() * rLevel)
            //Set up particle data
            set udg_PP_Real1[Node] = PP_ParticleAccelBase() + (PP_ParticleAccelPerLevel() * rLevel)
            set udg_PP_Real2[Node] = TempReal * Sin(Angle2)
            set udg_PP_Real3[Node] = TempReal * Cos(Angle) * Cos(Angle2)
            set udg_PP_Real4[Node] = TempReal * Sin(Angle) * Cos(Angle2)
            set udg_PP_Real5[Node] = PP_ParticleTurnRateBase() + (PP_ParticleTurnRatePerLevel() * rLevel)
            set udg_PP_Real6[Node] = PP_ParticleTurnEfficiencyBase() + (PP_ParticleTurnEfficiencyPerLevel() * rLevel)
            set udg_PP_Real7[Node] = PP_GetZ(x, y) + GetUnitFlyHeight(u) + PP_ParticleLaunchOffset()
            set udg_PP_Real8[Node] = x2
            set udg_PP_Real9[Node] = y2
            set TempReal = PP_ParticleAOEBase() + (PP_ParticleAOEPerLevel() * rLevel)
            set udg_PP_Real10[Node] = TempReal * TempReal
            set udg_PP_Real11[Node] = PP_ParticleHealthDamageBase() + (PP_ParticleHealthDamagePerLevel() * rLevel)
            set udg_PP_Real12[Node] = PP_ParticleManaDamageBase() + (PP_ParticleManaDamagePerLevel() * rLevel)
            set udg_PP_Integer1[Node] = PP_ParticleParticleRingCountBase() + (PP_ParticleParticleRingCountPerLevel() * iLevel)
            set udg_PP_Real13[Node] = rLevel
            set udg_PP_TargetUnit[Node] = Target
            set udg_PP_Real14[Node] = GetUnitFlyHeight(udg_PP_TargetUnit[Node]) + PP_TargetAimOffset()
            //Mark as an activator particle and as a damaging particle
            set udg_PP_Boolean1[Node] = true
            set udg_PP_Boolean2[Node] = true
            set udg_PP_StageID[Node] = PP_ReleasedParticleojectileStageID()
            call SetUnitFlyHeight(udg_PP_Unit[Node], udg_PP_Real7[Node] - PP_GetZ(x, y), 0.)
           
            set Target = null
        endif
       
        //Make them face the right way
        call  SetUnitFacing(udg_PP_Unit[Node], bj_RADTODEG * Atan2(udg_PP_Real4[Node], udg_PP_Real3[Node]))

        //Check if this is the first instance and start the timer
        if (udg_PP_AbilityCounter == 1) then
            call TimerStart(udg_PP_Timer, PP_TimerSpeed(), true, function PP_Loop)
        endif
       
    endif
   
    return false
endfunction

////////////////////////////////////////////////////////////////////
//  Initialisation trigger, applies the conditions to triggers    //
//  and sets up the global location used to get location Z's      //
//  as well as the map bounds                                     //
////////////////////////////////////////////////////////////////////
function InitTrig_Punishing_Particles takes nothing returns nothing
    //Set up locals
    local trigger PP = CreateTrigger()

    //Set up hero casting triggers
    call TriggerRegisterAnyUnitEventBJ(PP, EVENT_PLAYER_UNIT_SPELL_EFFECT)

    //Applies the function to check for each spell to the trigger
    call TriggerAddCondition(PP, Condition(function PP_OnCast))

    //Set up the location used to find Z heights
    set udg_PP_Loc = Location(0,0)
    //Set up the Timer used to run the loop
    set udg_PP_Timer = CreateTimer()

    //Sets up the variables used to make sure a point is within the map area
    set udg_PP_MapMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
    set udg_PP_MapMinX = GetRectMinX(bj_mapInitialPlayableArea)
    set udg_PP_MapMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
    set udg_PP_MapMinY = GetRectMinY(bj_mapInitialPlayableArea)
endfunction
////////////////////////////////////////////////////////////////////
// End of the spell                                               //
////////////////////////////////////////////////////////////////////

Spellset Information

- 1827 lines of pure JASS
- 143 Configurables reaching all aspects of the ability
- 1 ability 2 parts, compact and powerful
- Extensive Readme to help you configure the ability
- Doesn't interfere with game scorescreen
- Compatiable with Vanilla WE
- Functions for ground and flying heros
- Arcs are stable on uneven terrain
- Features can be adapted to your liking due to the configuration including complete removal
- Attractive yet functional
- All levels are set to look identical with default configuration for extra deception
- Particle Types are decided upon launch rather than upon landing, allowing you to continue to deceive enemies who understand the ability
- Combines Deception with Skill and Tactics
- Useful outside of Deception as a skilshot
- Easy to reach target filtering
- Two parts can be easily seperated into two different abilities
- Ignores magic immune enemies correctly
- You can potentially infinitely stack Energy Stacks (though there'd never be a reason to do this and most would die before you can get to a number particularly high)
- Designed to efficiently use each variable, reusing them as much as possible
- Variable reuse reduces the amount of variables to import to 40
- Buffs for all status conditions
- Try switching the particle model to something more inviting like a treasure chest if players refuse to pick up particles to make it more deceptive (the arc may need to be modified to make this work effectively)
- Comes with omnipresent Mr. Fluffy and his entourage of Tanks
Tips and Tricks


- You can use the ability as a skillshot or to play a mindgame with your opponent dependent on your playstyle
- Make sure your opponent is aware that the ability can heal them - throw out a lot of healing particles while they have recieved negligible damage before mixing in damaging ones
- If your opponent is too hard for you to skillshot, start out by using a healing particle to slow them down before switching to damaging ones - be sure to hit them plenty of times though otherwise the investment will be in vain and be weary of the mana you are consuming, use this method sparingly
- Block off retreat paths with damaging particles, the slow and extra damage will allow you to punish them particularly hard if you catch them out
- A portion of the total damage of the projectile is dealt immediately on pickup the remaining damage stored in the smaller particles can be dodged: low health enemies may be forced to wastefully use their escape to dodge this damage
- Use the targeting marker to identify what kind of particle you are firing - if the circle overlaps with the centre of the unit then the particle will be damaging (A unit going green does not necessarily mean the projectile will be damaging)
- With a modified target filter the ability can also be used as a strong heal for allies while retaining offensive uses (but be careful if you do this since you may damage them and they will be slowed)


- Utilise this part to create fatal punishes on your opponents while they remain stunned
- Enemies are stunned longer and take more damage the more energy stacks you have built up on them, use this in sync with part 1 to quickly turn around a fight
- at level 3, 1 healing particle 1 damaging particle and part 2 will deal more damage than a fully charged healing particle (cancelling out the first part) use this to lure out low health enemies or fix mistakes you have made if you have the mana to spare
- This does nothing at all if the enemy has not yet got any energy stacks on them so be careful not to misclick when sending out particles - it will still cost you mana
- If you kill an enemy during the second part, all remaining damage that would've been released onto them is added on to the area damage, use it to finish off low health enemies for maximum benefit
- The projectile can miss: wait for a direct line of sight before using it and if possible do so when the enemy cannot use an escape to dodge
- Heavily stacked enemies can be easily scared away from a fight if they know you can stun lock them the moment they overextend allowing you to put heavy pressure on the player


- If you have an escape it may be worth picking up a healing particle and quickly following it up with the escape in order to avoid taking skillshot hits from the ability
- While the part 2 projectile is very fast it can be dodged via making it hit into walls or the ground (if there is uneven terrain) when holding a lot of stacks try to avoid giving them a direct line of sight or if your reactions are very fast and you have a blink-type ability, blink over the projectile - if it's near a wall the particle turning may result in it missing
- If your opponent is bad with landing skillshots and you are in good shape there's little need to pickup the particles, be careful about getting too greedy
- Positioning is everything, getting into a bad spot where your retreat path can be blocked off/made awkward by a particle can result in an easily given death

Helpful Files

- Variable Creator: Copy and paste into your map, you now have all the variables needed for the spell to run
  • Variable Creator
    • Events
    • Conditions
    • Actions
      • Set PP_AbilityCounter = 0
      • Set PP_Boolean1[0] = False
      • Set PP_Boolean2[0] = False
      • Set PP_Caster[0] = PP_Caster[0]
      • Set PP_CurrentEffect[0] = PP_CurrentEffect[0]
      • Set PP_Integer1[0] = 0
      • Set PP_Integer2[0] = 0
      • Set PP_Integer3[0] = 0
      • Set PP_Integer4[0] = 0
      • Set PP_LastNode = 0
      • Set PP_Loc = PP_Loc
      • Set PP_MapMaxX = 0.00
      • Set PP_MapMaxY = 0.00
      • Set PP_MapMinX = 0.00
      • Set PP_MapMinY = 0.00
      • Set PP_NextNode[0] = 0
      • Set PP_NodeNumber = 0
      • Set PP_Player[0] = PP_Player[0]
      • Set PP_PrevNode[0] = 0
      • Set PP_Real1[0] = 0.00
      • Set PP_Real2[0] = 0.00
      • Set PP_Real3[0] = 0.00
      • Set PP_Real4[0] = 0.00
      • Set PP_Real5[0] = 0.00
      • Set PP_Real6[0] = 0.00
      • Set PP_Real7[0] = 0.00
      • Set PP_Real8[0] = 0.00
      • Set PP_Real9[0] = 0.00
      • Set PP_Real10[0] = 0.00
      • Set PP_Real11[0] = 0.00
      • Set PP_Real12[0] = 0.00
      • Set PP_Real13[0] = 0.00
      • Set PP_Real14[0] = 0.00
      • Set PP_RecyclableNodes = 0
      • Set PP_RecycleNodes[0] = 0
      • Set PP_StageID[0] = 0
      • Set PP_TargetUnit[0] = PP_TargetUnit[0]
      • Set PP_TempGroup = PP_TempGroup
      • Set PP_Timer = PP_Timer
      • Set PP_Unit[0] = PP_Unit[0]

Images

Tooltip

Part 1

Part 2

GIFs


149016d1443612645-zephyr-contest-13-deception-zephyr-contest-13-tooltip.png

Healing Particle

Damaging Particle

Thin Line

148443d1441850108-zephyr-contest-13-deception-zephyr-contest-13-screenshot-1.1.png

148444d1441850108-zephyr-contest-13-deception-zephyr-contest-13-screenshot-1.2.png
148445d1441850108-zephyr-contest-13-deception-zephyr-contest-13-screenshot-2.1.png

148446d1441850108-zephyr-contest-13-deception-zephyr-contest-13-screenshot-2.2.png
148447d1441850135-zephyr-contest-13-deception-zephyr-contest-13-screenshot-3.png

Energy Release

Finale

Adding Extra

Overload

Adding Extra (cleaner)

148448d1441850135-zephyr-contest-13-deception-zephyr-contest-13-screenshot-4.png
148449d1441850135-zephyr-contest-13-deception-zephyr-contest-13-screenshot-5.png
148450d1441850135-zephyr-contest-13-deception-zephyr-contest-13-screenshot-6.png
148460d1441850916-zephyr-contest-13-deception-zephyr-contest-13-screenshot-7.png
148461d1441850916-zephyr-contest-13-deception-zephyr-contest-13-screenshot-8.png

Normal

Dying Early

Piling On

Damaging Particle

Healing Particle

KILLCIDE%20HQ%20GIF%201.jpg

Credit to KILLCIDE
148463d1441850916-zephyr-contest-13-deception-zephyr-contest-13-gif-2.gif
148464d1441850916-zephyr-contest-13-deception-zephyr-contest-13-gif-3.gif
148465d1441851069-zephyr-contest-13-deception-zephyr-contest-13-gif-4.gif
KILLCIDE%20HQ%20GIF%202.jpg

Credit to KILLCIDE


Changelog


-=V1.01=-
- Altered recycling to utilise instant killing
-=V1.00=-
- Initial Upload

[TD]Punishing Particles[/TD]
Winning entry of Zephyr Contest #13
Tooltip

Icon
icons_18026_btn.jpg
[td]
Alternate
icons_18027_btn.jpg
[/TD]
[td]
Q - Punishing Particles
Launches experimental energies when aimed to land near enemy units which rapidly decay, damaging or healing them when stepped on depending on how close to an enemy the projectile target was. They also apply energy stacks onto enemy units, launching energy directly at an enemy releases the stored energy

Level 1 - Energy heals enemies by 500 and deal 175 damage, energy stacks deal 75 damage
Level 2 - Energy heals enemies by 600 and deal 250 damage, energy stacks deal 100 damage
Level 3 - Energy heals enemies by 700 and deal 325 damage, energy stacks deal 125 damage

[/td]
[tr][td]

Keywords:
Particle, Punish, Deception, Skillshot, Explode, Mine, Bomb, Stun, Disable, Trap, Slow, Heal, Homing, SFX.[/td][/tr]
Contents

Punishing Particles (Map)

Reviews
03:10, 10th Feb 2016 IcemanBo: version: Punishing Particles V1.00 Good concept. Good code. Good visuals. I could not reproduce the bug that was mentioned in Bannar's review: Link For some things I agree with him that some names are not...

Moderator

M

Moderator

03:10, 10th Feb 2016
IcemanBo:

version: Punishing Particles V1.00

Good concept.
Good code.
Good visuals.

I could not reproduce the bug that was mentioned in Bannar's review: Link
For some things I agree with him that some names are not really intuitive or it's hard to follow too big code blocks.

The extra duration for showing effects is not needed, and the units could be
killed instantly. I assume it is the same thing that we discussed
about at the BUS spell pack. Me would change it by time.

But the spell is good, works fine, and I could not fine bugs. I give it a good Recommended.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Man! Another thoroughly beautiful spell by Tank Commander!

I have very minor critique and some necessary critique:

Minor: please use real variable names for your globals instead of Real1/2/etc. Like X/Y/Z velocity/deceleration/etc.

Minor: Use 2 functions for the event. As it is right now you create > 12 locals and assign a unit target regardless of if it's the right spell.

Minor: You don't need to inline the anyunitevent BJ. Red != evil in some cases and this is one of them.

Necessary: All local agents must be nulled by the end of the function. I'm looking at you, "unit Target".

Maybe I just don't get it:

JASS:
                            set udg_PP_Real3[TempNode2] = PP_ReleasedParticleProjectileSpeed() * Cos(Angle) * Cos(Angle2)
                            set udg_PP_Real4[TempNode2] = PP_ReleasedParticleProjectileSpeed() * Sin(Angle) * Cos(Angle2)

I saw a similar block of code elsewhere in your script as well with the same mistake (I think it's a mistake). The second line should be Sin in both cases, right?
 
The generic variable naming convention is due to all the variable reuse - while generally considered bad practice to reuse them for a bunch of stuff I thought it was necessary to keep the imported globals to a minimum (with reuse I've got 20 generic variables without it'd spike to 80-100 given all 5 components of the spell use them and not all in the same way) - I could do it but it'd take a rather long time to do so, understandably as-is the code is much harder to navigate from an outside perspective

would two event functions be better? the target assignment is to differentiate between which effect to activate (since all locals except that one would be needed regardless of target) - it'd be adding a function with the appropriate event listener to avoid 1 local

I'll remove the inlining as requested

Didn't notice I'd missed that one - thanks for pointing it out (hopefully I didn't miss any others)

I'll leave the version number as is though since I'm just cleaning up

As for the
ZVel = Speed * Sin(Angle2)
XVel = Speed * Cos(Angle) * Cos(Angle2)
YVel = Speed * Sin(Angle) * Cos(Angle2)
it's because Angle2 is the Angle of offset between the XY and Z planes (if you were to imagine a normal graph, X Axis-> XY, Y Axis-> Z
so Speed * Sin(Angle2) gives the Z component and Speed * Cos(Angle2) gives the XY Component which I then break down further into the X and Y using Cos(Angle) and Sin(Angle) respectively - hopefully that clears it up at least a little bit, at any rate it's intentional or my projectiles would fly really weirdly

Thanks for the comment and feedback
 
Top