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

Silithid Devourer Spellset V1.01

HC8 - Team Special Ops Entry
Artist: A.R.
Coder: Tank-Commander
Hero
Stats
Abilities
Showcases
hero-icon-jpg.246766
Proper Names: Ferl'piej the Deranged, Kikidrill the Hematophagous, Amorg'tik, Tremal the Sanguine, Qilarid, Mutrali, Vourg the Ruthless

Silithid Devourer
Warrior Hero, exceptional at disruptive spells and duelling other heroes. Can learn Rupturing Stab, Leech Life, Corpse Crusher, and Blood Boil.

Attacks land units.

Role: Disruptive Tank Theme: Bloody Silithid, violent & durable
Playstyle
Boasting high agility and strength the Silithid Devourer has a strong ability to duel with access to powerful single target damage and stun abilities as well as the ability to regenerate health and mana quickly. The cost of these high stats is a low max mana leading to a need to manage it carefully and difficulty in prolonged fights - The Devourer relies on consuming corpses of allies or enemies to regenerate its mana and keep up its onslaught.

There are three distinct ways on how to play the Silithid Devourer based on what abiliies you pick:
Sustained Offensive: Max Q then E; pick up W only if you want the utility, needs healing support hero (e.g. Shadow Hunter)
Full Tank: Max W then E, pick up Q early on for damage and buff to W heal, low damage output
All-Out Offensive: Max Q then W; do not pick up E, needs mana regen hero (e.g. archmage, bloodmage)

Early Game: When first summoning the hero you will have the choice between single target damage (Q), single target drain & stun (W), or mana regen with a weak AOE damage/slow (E). Generally speaking your Q is the best option with W being a good option against opponents likely to use a melee hero to harass you as you can stun them and get some free hits with other units (possibly even bodyblock/force tp) at level 2 E will become necessary (use it to consume all the corpses of your creep camp to get the mana you spent back. When starting W do not use it when there is more than one enemy left in a creep camp the manacost is high (half your mana at level 1) and takes 2-3 corpses to regenerate the mana from your E (30 mana per corpse)

Mid Game: By now you should have your second hero and you should have leveled your Q for damage or W/E for additional sustain depending on how you play, the Devourer does well with heroes which can regenerate either his health or mana and Heroes with access to other debuffs or buffs as they complement the melee nature of the Devourer. When engaging with the enemy his E is good for pursuing enemies and his W can secure a body-blocked or slowed straggler comfortably - catching a hero with it will lock it down for 4 seconds. At level 6 the ultimate (R) can be used to give a massive damage surge for 60 seconds but will beg the need for potions or a hero who can heal them due to the self-inflicted damage. The increased speed can also assist in making a game-winning capture of a hero but be careful as the damage cannot be cancelled and it adds up quickly (a total of 600 out of your max 1045 health at level 6 disregarding passive regen).

Late Game: Towards the end of the game the Devourer really comes in to its own - at this point the mana issues will be all but nonexistent save for spamming your high level Q or low level W, the ability damage should make picking away at enemy armies easy providing more than enough corpses and using your R will be much safer making the damage spike potential much more threatening
Tips
Combos: The Devourer's abilities interact with each other to enhance its combat capabilities centering around their Q ability which applies a bleed effect on enemies hit. Using the W on a bleeding enemy regenerates extra health and extends the bleeding effect and using the E on them refunds the cost of casting it. In addition the R also applies bonuses making the Q and W deal additional damage and increasing the projectile speed of the E. Which allows you to use the following combos:
- Q->W
- Q->E->W
- Q->W->E->Q
- Q->W->E->W->Q (Bodyblocked Opponent)
- Q->E->Q
- R->Q->W->Chase->Q->E->W
- R->Q->E->Q->Q->E->Q->Q->E->(etc.)
- R->E->Q->W
- R->E->W->Q

Leeching Life: Your W is very expensive at low levels so it should be saved for when you direly need health or to disable priority targets (enemy heroes, boss mob in a creep camp) in addition it has the ability to whiff if your opponent gets too far away from you before you latch on (it won't cost you any mana if you miss but the ability will still go on cooldown), this necessitates that when chasing a fast enemy use your E to slow your target or to have body blocked them to give you time to grab them

Manacost Nightmare: If you're struggling to manage the mana or simply need to boost the amount you can use spells pick up either mana potions, a mantle of intelligence + 3 (+45 mana) or other similar items. Alternatively train units or Heroes that can help with your mana issues (Archmage, Obsidian Statue or Bloodmage). Health Regeneration/Armour can also help with this by lowering the need to use your W to just the times you want to stun a unit for a long time (Shadow Hunter, Dreadlord, Lich, Paladin, Keeper of the Grove)

Graverobber: your E can be used to interfere with necromancer centred undead play by consuming the corpses they would use to summon skeletons though they may also deny your mana regen by getting to them first. Similarly when playing undead or attacking an undead player the graveyard can be used as an easy means to regenerate mana as it periodically spawns corpses you can consume
hero-preview-png.246767
Model Information:
- Race: Silithid
- 242.4kb for all textures and models (main and portrait)
- 19kb for all icons (BTN/DISBTN/Scorescreen)
- 8 Limbs (4 arms/claws, 4 legs)
- Stinger
- Atrophied wings
- Completely custom animations including unique ones for each ability
- Attachment points on all limbs (Sprite first for stinger, second & third for extra claws)
- Built-in Sound effects on spell throw and channel
- Custom texture for the body and an animated texture for the abdomen
- Team colour on the wings and abdomen
Model Thread
Icon Thread
StatBaseGrowthMax
latest
223.0049Primary
latest
192.0037
latest
111.0020
IconTooltip
impale.gif
Q - Rupturing Stab Manacost: 30/45/60 Cooldown: 6 seconds
The caster swings at the target with its stinger dealing damage and causing them to bleed for 4 seconds. Targetting bleeding enemies with other abilities applies bonuses.

Level 1 - 50 initial damage, 30 bleed damage.
Level 2 - 75 initial damage, 60 bleed damage.
Level 3 - 100 initial damage, 90 bleed damage.
latest
W - Leech Life Manacost: 80/65/50 Cooldown: 8 seconds
Latches on to enemy units pinning them and draining blood dealing damage and restoring health to the caster over 4 seconds bleeding enemies bleed for 4 more seconds and restores extra health to the caster.

Level 1 - 75 damage dealt, 100 health restored, 35 extra restored.
Level 2 - 85 damage dealt, 120 health restored, 70 extra restored.
Level 3 - 95 damage dealt, 140 health restored, 105 extra restored.
latest
E - Corpse Crusher Manacost: 50 Cooldown: 18/16/14 seconds
Allows the caster to passively consume corpses to regenerate mana, can be cast actively to launch a corpse at the target point that will deal 50 damage and slow enemies in an area for a duration. Hitting a bleeding enemy with the corpse refunds half the cost of the spell.

Level 1 - 30 mana restored, weak slow, lasts 5 seconds.
Level 2 - 45 mana restored, medium slow, lasts 6 seconds.
Level 3 - 60 mana restored, strong slow, lasts 7 seconds.
latest
R - Blood Boil Manacost: 120 Cooldown: 120 seconds
The caster's heart rate surges increasing their attack and movement speed substantially while also increasing mana regeneration at the cost of 10 health every second, additionally their abilities are enhanced. Lasts 60 seconds.

Rupturing Stab - stab damage increased by 25 per level, bleed damage increased by 15 per level.
Life Leech - damage increased by 50.
Corpse Crusher - faster projectile.

Rupturing Stab

Leech Life

Corpse Crusher

Blood Boil

GIFCode
rupturing-stab-gif-gif.246769
JASS:
////////////////////////////////////////////////////////////////////
//                     RUPTURING STAB V1.01                       //
//  Author: Tank-Commander                                        //
//  Purpose: Simple Stab & Damage over time ability to use in     //
//           combo with other abilities                           //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the system    //
//                                                                //
//                                                                //
//  If you have used this spell in your map, you are required     //
//  to give credits to Tank-Commander for the creation of it      //
//  If you would like to use snippets of code from this for       //
//  whatever, getting permission and crediting the source/linking //
//  would be much appreciated.                                    //
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  README:                                                       //
//    Before modifying this spell a few things need to be         //
//    understood and read, this is one of those things, while     //
//    most modification can be considered intuitive, it still     //
//    helps to read through these intstructions, as they will     //
//    inform you about how to configure this spell to your        //
//    desire.                                                     //
//----------------------------------------------------------------//
//  Initial importing: The variable creator trigger can be        //
//  imported first and if you have the correct settings (file,    //
//  preferences, General, automatically create unknown variables  //
//  checked, then when you paste in the variable creator it       //
//  will automatically give you all the variables you need for    //
//  this spell                                                    //
//                                                                //
//  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   //
//  value are not the same as listed in the configuration, those  //
//  configurables will need to be changed to work correctly       //
//----------------------------------------------------------------//
//  TimerSpeed: This is the rate at which the loop timer of the   //
//  ability runs the default value 0.03125 allows the loop        //
//  function to run 32 times per second giving a smooth ability   //
constant function RuSt_TimerSpeed takes nothing returns real
    return 0.031250000
endfunction
//----------------------------------------------------------------//
//  Ability: This is the raw code for the ability used as a base  //
//  to see raw data press ctrl + d in the object editor, do it    //
//  again to revert to normal                                     //
constant function RuSt_Ability takes nothing returns integer
    return 'A000'
endfunction
//----------------------------------------------------------------//
//  BonusBuff: This is the raw code for the ability used to       //
//  Apply damage bonuses to the ability (it can be either a       //
//  regular ability or a specific buff which the caster can be    //
//  afflicted with to give optionally temporary or permanent      //
//  buffs to this ability                                         //
constant function RuSt_BonusBuff takes nothing returns integer
    return 'B003'
endfunction
//----------------------------------------------------------------//
//  BleedBuff: This is the raw code of the ability used to        //
//  debuff units damaged by this ability, it should be based off  //
//  slow aura                                                     //
constant function RuSt_BleedBuff takes nothing returns integer
    return 'A001'
endfunction
//----------------------------------------------------------------//
//  DrainBuff: This is the same as BuffAbility except used for    //
//  extending the duration of BleedBuff based on the target of    //
//  the ability having this buff applied to them                  //
constant function RuSt_DrainBuff takes nothing returns integer
    return 'B001'
endfunction
//----------------------------------------------------------------//
//  BleedDelay: This is the time in seconds between each bit of   //
//  damage dealt to the target via bleeding                       //
function RuSt_BleedDelay takes real level returns real
    return 0.312500000
endfunction
//----------------------------------------------------------------//
//  BleedDuration: This is how long the target will bleed for in  //
//  seconds
function RuSt_BleedDuration takes real level returns real
    return 4.
endfunction
//----------------------------------------------------------------//
//  BleedDamageHealth: This is the amount of damage the target    //
//  takes over the duration of the bleeding                       //
function RuSt_BleedDamageHealth takes real level returns real
    return 15 + 15 * level
endfunction
//----------------------------------------------------------------//
//  BleedDamageMana: This is this is the amount of mana lost by   //
//  the target over the duration of the bleeding                  //
function RuSt_BleedDamageMana takes real level returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  BonusBleedDamageHealth: This is the extra bleed damage taken  //
//  if the caster has BonusBuff applied to them when first        //
//  stabbing                                                      //
function RuSt_BonusBleedDamageHealth takes real level returns real
    return 15 * level
endfunction
//----------------------------------------------------------------//
//  BonusBleedDamageMana: This is the extra bleed mana lost if    //
//  the caster has BonusBuff applied to them when first stabbing  //
function RuSt_BonusBleedDamageMana takes real level returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  StabDamageHealth: This is the amount of damage taken by the   //
//  target when they are first stabbed                            //
function RuSt_StabDamageHealth takes real level returns real
    return 25 + (25 * level)
endfunction
//----------------------------------------------------------------//
//  StabDamageMana: This is the amount of mana lost by the target //
//  when they are first stabbed                                   //
function RuSt_StabDamageMana takes real level returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  BonusStabDamageHealth: This is the extra Stab damage taken if //
// the caster has BonusBuff applied to them when first stabbing   //
function RuSt_BonusStabDamageHealth takes real level returns real
    return 25 * level
endfunction
//----------------------------------------------------------------//
//  BonusStabDamageMana: This is the extra Stab mana lost if the  //
//  caster has BonusBuff applied to them when first stabbing      //
function RuSt_BonusStabDamageMana takes real level returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  StabEffect: This is the effect played on the target when      //
//  they are stabbed                                              //
constant function RuSt_StabEffect takes nothing returns string
    return "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl"
endfunction
//----------------------------------------------------------------//
//  StabAttachmentPoint: This is the location on the target that  //
//  StabEffect is played on                                       //
constant function RuSt_StabAttachmentPoint takes nothing returns string
    return "chest"
endfunction
//----------------------------------------------------------------//
//  BleedingEffect: This is the effect played on the target for   //
//  the duration that they are bleeding                           //
constant function RuSt_BleedingEffect takes nothing returns string
    return "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
endfunction
//----------------------------------------------------------------//
//  BleedingAttachmentPoint: This is the location on the target   //
//  that BleedingEffect & DrainCombinedEffect are played on       //
constant function RuSt_BleedingAttachmentPoint takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
//  BloodiedEffect: This is the effect played on the caster on    //
//  the body part or weapon they used to stab the target          //
constant function RuSt_BloodiedEffect takes nothing returns string
    return "Objects\\Spawnmodels\\Other\\HumanBloodCinematicEffect\\HumanBloodCinematicEffect.mdl"
endfunction
//----------------------------------------------------------------//
//  BloodiedAttachmentPoint: This is the location on the caster   //
//  that BloodiedEffect is played on                              //
constant function RuSt_BloodiedAttachmentPoint takes nothing returns string
    return "sprite first"
endfunction
//----------------------------------------------------------------//
//  DrainCombinedEffect: This is the effect played to denote      //
//  that the duration of the bleeding is being extended via the   //
//  target having DrainBuff applied to them                       //
constant function RuSt_DrainCombinedEffect takes nothing returns string
    return "Abilities\\Spells\\Undead\\CarrionSwarm\\CarrionSwarmDamage.mdl"
endfunction
//----------------------------------------------------------------//
//  AttackType: This is the attack type used by the spell         //
constant function RuSt_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_NORMAL
endfunction
//----------------------------------------------------------------//
//  DamageType: This is the damagetype used by the spell, armour  //
//  reduces the damage based on this type                         //
constant function RuSt_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_NORMAL
endfunction
//----------------------------------------------------------------//
//  WeaponType: This is the weapontype used by the spell
constant function RuSt_WeaponType takes nothing returns weapontype
    return null
endfunction
//----------------------------------------------------------------//
//                      END OF CONFIGURATION                      //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
//  Funtion used to check if a unit is still bleeding            //
///////////////////////////////////////////////////////////////////
function RuSt_IsNotBleeding takes unit u returns boolean
    local integer node = 0
  
    loop
        set node = udg_RuSt_NextNode[node]
        exitwhen node == 0
 
        if udg_RuSt_Unit[node] == u then
            return false
        endif

    endloop
  
    return true
endfunction

///////////////////////////////////////////////////////////////////
//  Function used to primarily deal Damage over time but also    //
//  adds artificial delay to mimic warcraft mechanics for        //
//  Damage over time while keeping in strict time with the       //
//  duration of the ability                                      //
///////////////////////////////////////////////////////////////////
function RuSt_Loop takes nothing returns nothing
    local integer node = 0
  
    loop
        set node = udg_RuSt_NextNode[node]
        exitwhen node == 0
      
        //Extend duration if the enemy is being leeched
        if GetUnitAbilityLevel(udg_RuSt_Unit[node], RuSt_DrainBuff()) > 0 then
            set udg_RuSt_Duration[node] = udg_RuSt_Duration[node] + RuSt_TimerSpeed()
            call DestroyEffect(AddSpecialEffectTarget(RuSt_DrainCombinedEffect(), udg_RuSt_Unit[node], RuSt_BleedingAttachmentPoint()))
        endif
      
        //Check if the spell is not finished
        if udg_RuSt_Duration[node] > 0. then
            set udg_RuSt_Duration[node] = udg_RuSt_Duration[node] - RuSt_TimerSpeed()
            set udg_RuSt_DamageDelay[node] = udg_RuSt_DamageDelay[node] - RuSt_TimerSpeed()
          
            //Check if it's time to deal damage again
            if udg_RuSt_DamageDelay[node] <= 0. then
                //Deal damage
                set udg_RuSt_DamageDelay[node] = udg_RuSt_DamageDelayStart[node]
                call DestroyEffect(AddSpecialEffectTarget(RuSt_BleedingEffect(), udg_RuSt_Unit[node], RuSt_BleedingAttachmentPoint()))
                call UnitDamageTarget(udg_RuSt_Caster[node], udg_RuSt_Unit[node], udg_RuSt_BleedDamageHealth[node], true, false, RuSt_AttackType(), RuSt_DamageType(), RuSt_WeaponType())
                call SetUnitState(udg_RuSt_Unit[node], UNIT_STATE_MANA, GetUnitState(udg_RuSt_Unit[node], UNIT_STATE_MANA) - udg_RuSt_BleedDamageMana[node])
            endif
          
        else
            call DestroyEffect(udg_RuSt_BloodiedEffect[node])
          
            //Recycle
            set udg_RuSt_RecycleNodes[udg_RuSt_RecyclableNodes] = node
            set udg_RuSt_RecyclableNodes = udg_RuSt_RecyclableNodes + 1
            set udg_RuSt_NextNode[udg_RuSt_PrevNode[node]] = udg_RuSt_NextNode[node]
            set udg_RuSt_PrevNode[udg_RuSt_NextNode[node]] = udg_RuSt_PrevNode[node]
          
            //Check if this is the last instance of this unit bleeding
            if RuSt_IsNotBleeding(udg_RuSt_Unit[node]) then
                call UnitRemoveAbility(udg_RuSt_Unit[node], RuSt_BleedBuff())

                //Pause timer if this was the last instance
                if (udg_RuSt_PrevNode[0] == 0) then
                    call PauseTimer(udg_RuSt_Timer)
                endif
              
            endif
          
        endif
      
    endloop
  
endfunction

///////////////////////////////////////////////////////////////////
//  Function used to deal damage from the ability and cause      //
//  target units to start bleeding                               //
///////////////////////////////////////////////////////////////////
function RuSt_Cast takes nothing returns boolean
    //Set up locals
    local integer node
    local real level
  
    if (GetSpellAbilityId() == RuSt_Ability()) then
        //Set up Node
        if (udg_RuSt_RecyclableNodes == 0) then
            set udg_RuSt_NodeNumber = udg_RuSt_NodeNumber + 1
            set node = udg_RuSt_NodeNumber
        else
            set udg_RuSt_RecyclableNodes = udg_RuSt_RecyclableNodes - 1
            set node = udg_RuSt_RecycleNodes[udg_RuSt_RecyclableNodes]
        endif

        set udg_RuSt_NextNode[node] = 0
        set udg_RuSt_NextNode[udg_RuSt_PrevNode[0]] = node
        set udg_RuSt_PrevNode[node] = udg_RuSt_PrevNode[0]
        set udg_RuSt_PrevNode[0] = node
      
        //Start timer if this is the only instance
        if (udg_RuSt_PrevNode[node] == 0) then
            call TimerStart(udg_RuSt_Timer, RuSt_TimerSpeed(), true, function RuSt_Loop)
        endif
      
        //Apply Data
        set udg_RuSt_Unit[node] = GetSpellTargetUnit()
        set udg_RuSt_Caster[node] = GetTriggerUnit()
        set udg_RuSt_BloodiedEffect[node] = AddSpecialEffectTarget(RuSt_BloodiedEffect(), udg_RuSt_Caster[node], RuSt_BloodiedAttachmentPoint())
        set level = GetUnitAbilityLevel(udg_RuSt_Caster[node], RuSt_Ability())
        set udg_RuSt_DamageDelayStart[node] = RuSt_BleedDelay(level)
        set udg_RuSt_DamageDelay[node] = udg_RuSt_DamageDelayStart[node]
        set udg_RuSt_Duration[node] = RuSt_BleedDuration(level)

        //Stab Effect
        call UnitAddAbility(udg_RuSt_Unit[node], RuSt_BleedBuff())
        call DestroyEffect(AddSpecialEffectTarget(RuSt_StabEffect(), udg_RuSt_Unit[node], RuSt_StabAttachmentPoint()))
      
        //Check for Blood Boil Bonuses
        if GetUnitAbilityLevel(udg_RuSt_Caster[node], RuSt_BonusBuff()) > 0 then
            set udg_RuSt_BleedDamageHealth[node] = (RuSt_BleedDamageHealth(level) + RuSt_BonusBleedDamageHealth(level)) / (udg_RuSt_Duration[node] / udg_RuSt_DamageDelay[node])
            set udg_RuSt_BleedDamageMana[node] = (RuSt_BleedDamageMana(level) + RuSt_BonusBleedDamageMana(level)) / (udg_RuSt_Duration[node] / udg_RuSt_DamageDelay[node])
            call UnitDamageTarget(udg_RuSt_Caster[node], udg_RuSt_Unit[node], RuSt_StabDamageHealth(level) + RuSt_BonusStabDamageHealth(level), true, false, RuSt_AttackType(), RuSt_DamageType(), RuSt_WeaponType())
            call SetUnitState(udg_RuSt_Unit[node], UNIT_STATE_MANA, GetUnitState(udg_RuSt_Unit[node], UNIT_STATE_MANA) - (RuSt_StabDamageMana(level) + RuSt_BonusStabDamageMana(level)))
        else
            set udg_RuSt_BleedDamageHealth[node] = RuSt_BleedDamageHealth(level) / (udg_RuSt_Duration[node] / udg_RuSt_DamageDelay[node])
            set udg_RuSt_BleedDamageMana[node] = RuSt_BleedDamageMana(level) / (udg_RuSt_Duration[node] / udg_RuSt_DamageDelay[node])
            call UnitDamageTarget(udg_RuSt_Caster[node], udg_RuSt_Unit[node], RuSt_StabDamageHealth(level), true, false, RuSt_AttackType(), RuSt_DamageType(), RuSt_WeaponType())
            call SetUnitState(udg_RuSt_Unit[node], UNIT_STATE_MANA, GetUnitState(udg_RuSt_Unit[node], UNIT_STATE_MANA) - RuSt_StabDamageMana(level))
        endif
      
    endif
  
    return false
endfunction

///////////////////////////////////////////////////////////////////
//  Function used to initialise the event listeners of the spell //
///////////////////////////////////////////////////////////////////
function InitTrig_Rupturing_Stab takes nothing returns nothing
    local trigger t = CreateTrigger()
  
    //Set up trigger
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function RuSt_Cast))
endfunction
///////////////////////////////////////////////////////////////////
//  END OF THE SPELL                                             //
///////////////////////////////////////////////////////////////////
GIFCode
life-leech-gif-gif.246770
JASS:
////////////////////////////////////////////////////////////////////
//                       LEECH LIFE V1.01                         //
//  Author: Tank-Commander                                        //
//  Requires: Dummy.mdl                                           //
//  Purpose: Stun and drain health from the target, useful to     //
//           disable enemy heroes                                 //
//                                                                //
//  Credits: Vexorian (dummy.mdl)                                 //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the system    //
//                                                                //
//                                                                //
//  If you have used this spell in your map, you are required     //
//  to give credits to Tank-Commander for the creation of it      //
//  If you would like to use snippets of code from this for       //
//  whatever, getting permission and crediting the source/linking //
//  would be much appreciated.                                    //
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  README:                                                       //
//    Before modifying this spell a few things need to be         //
//    understood and read, this is one of those things, while     //
//    most modification can be considered intuitive, it still     //
//    helps to read through these intstructions, as they will     //
//    inform you about how to configure this spell to your        //
//    desire.                                                     //
//----------------------------------------------------------------//
//  Initial importing: The variable creator trigger can be        //
//  imported first and if you have the correct settings (file,    //
//  preferences, General, automatically create unknown variables  //
//  checked, then when you paste in the variable creator it       //
//  will automatically give you all the variables you need for    //
//  this spell                                                    //
//                                                                //
//  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   //
//  value are not the same as listed in the configuration, those  //
//  configurables will need to be changed to work correctly       //
//----------------------------------------------------------------//
//  TimerSpeed: This is the rate at which the loop timer of the   //
//  ability runs the default value 0.03125 allows the loop        //
//  function to run 32 times per second giving a smooth ability   //
constant function LeLi_TimerSpeed takes nothing returns real
    return 0.031250000
endfunction
//----------------------------------------------------------------//
//  Ability: This is the raw code for the ability used as a base  //
//  to see raw data press ctrl + d in the object editor, do it    //
//  again to revert to normal                                     //
constant function LeLi_Ability takes nothing returns integer
    return 'A002'
endfunction
//----------------------------------------------------------------//
//  BonusBuff: This is the raw code for the ability used to       //
//  Apply damage bonuses to the ability (it can be either a       //
//  regular ability or a specific buff which the caster can be    //
//  afflicted with to give optionally temporary or permanent      //
//  buffs to this ability                                         //
constant function LeLi_BonusBuff takes nothing returns integer
    return 'B003'
endfunction
//----------------------------------------------------------------//
//  BleedBuff: This is the raw code of the ability used to        //
//  debuff units damaged by this ability, it should be based off  //
//  slow aura                                                     //
constant function LeLi_BleedBuff takes nothing returns integer
    return 'B000'
endfunction
//----------------------------------------------------------------//
//  DummyPlayer: This is the player who will own all dummy units  //
//  created by this spell, it should be a neutral player as to    //
//  not mess with scorescreens                                    //
constant function LeLi_DummyPlayer takes nothing returns player
    return Player(14)
endfunction
//----------------------------------------------------------------//
//  DummyUnit: This is the raw code for the unit used as a dummy  //
//  by the ability, it should use dummy.mdl as its model or some  //
//  other invisible model                                         //
constant function LeLi_DummyUnit takes nothing returns integer
    return 'u000'
endfunction
//----------------------------------------------------------------//
//  DummyStunAbility: This is the raw code of the ability used    //
//  to stun targets of the ability and hold them in place         //
constant function LeLi_DummyStunAbility takes nothing returns integer
    return 'A003'
endfunction
//----------------------------------------------------------------//
//  DummyStunOrder: This is the OrderID to go with the stun       //
//  ability in order ot get the dummy to use it, for stormbolt    //
//  as a base ability this is 852095                              //
constant function LeLi_DummyStunOrder takes nothing returns integer
    return 852095
endfunction
//----------------------------------------------------------------//
//  Order: This is the OrderID to match the ability to make sure  //
//  the caster is always channeling the spell, for drain this is  //
//  852488 (852387 for pre-1.28 versions of warcraft III)         //
constant function LeLi_Order takes nothing returns integer
    return 852488
endfunction
//----------------------------------------------------------------//
//  StopOrder: This is the OrderID to match the order the unit is //
//  given when the unit escapes its range as to stop channeling   //
//  the spell for "stop" this is 851972                           //
constant function LeLi_StopOrder takes nothing returns integer
    return 851972
endfunction
//----------------------------------------------------------------//
//  DeathAnimation: This is the animation name used if the unit   //
//  is killed during the channel of the spell                     //
constant function LeLi_DeathAnimation takes nothing returns string
    return "death"
endfunction
//----------------------------------------------------------------//
//  LoopAnimation: The animation name used during the channel of  //
//  the spell                                                     //
constant function LeLi_LoopAnimation takes nothing returns string
    return "channel"
endfunction
//----------------------------------------------------------------//
//  StopAnimation: This is the animation used when the spell      //
//  ends once the channel has ended to reset the unit animations  //
constant function LeLi_StopAnimation takes nothing returns string
    return "stand"
endfunction
//----------------------------------------------------------------//
//  MaxRange: This is the maximum distance at which the unit is   //
//  able to grab on to a unit, units leaving this radius (either  //
//  from teleporting away or running) will stop the drain, the    //
//  value is squared to make calculations faster                  //
function LeLi_MaxRange takes nothing returns real
    return 62500. //this is 250 * 250
endfunction
//----------------------------------------------------------------//
//  Delay: This is the time in seconds between damage dealt and   //
//  life restored to the caster                                   //
function LeLi_Delay takes real level returns real
    return 0.8
endfunction
//----------------------------------------------------------------//
//  Duration: This is the time in seconds that the spell lasts    //
//  for                                                           //
function LeLi_Duration takes real level returns real
    return 4.05
endfunction
//----------------------------------------------------------------//
//  HealthDamage: This is the amount of damage dealt to the       //
//  target over the duration of life leech                        //
function LeLi_HealthDamage takes real level returns real
    return 65 + (10 * level)
endfunction
//----------------------------------------------------------------//
//  ManaDamage: This is the amount of mana lost by the target     //
//  over the duration of life leech                               //
function LeLi_ManaDamage takes real level returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  BonusHealthDamage: This is the extra damage taken if the      //
//  caster has BonusBuff applied to them when first grabbing      //
//  the target                                                    //
function LeLi_BonusHealthDamage takes real level returns real
    return 50.
endfunction
//----------------------------------------------------------------//
//  BonusManaDamage: This is the extra mana lost if the caster    //
//  has BonusBuff applied to them when first grabbing the target  //
function LeLi_BonusManaDamage takes real level returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  HealthHeal: This is the amount of health restored to the      //
//  caster over the duration of life leech                        //
function LeLi_HealthHeal takes real level returns real
    return 80 + (20 * level)
endfunction
//----------------------------------------------------------------//
//  ManaHeal: This is the amount of mana restored to the caster   //
//  over the duration of life leech                               //
function LeLi_ManaHeal takes real level returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  BonusHealthHeal: This is the amount of extra health restored  //
//  if the target has BleedBuff when first grabbed                //
function LeLi_BonusHealthHeal takes real level returns real
    return 35 * level
endfunction
//----------------------------------------------------------------//
//  BonusManaHeal: This is the amount of extra mana restored if   //
//  the target has BleedBuff when first grabbed                   //
function LeLi_BonusManaHeal takes real level returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  ManaRefund: The amount of mana given back to the caster when  //
//  lifeleech misses (units avoid being grabbed entirely)         //
function LeLi_ManaRefund takes real level returns real
    return 95. - (15. * level)
endfunction
//----------------------------------------------------------------//
//  AttackType: This is the attack type used by the spell         //
constant function LeLi_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_NORMAL
endfunction
//----------------------------------------------------------------//
//  DamageType: This is the damagetype used by the spell, armour  //
//  reduces the damage based on this type                         //
constant function LeLi_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_NORMAL
endfunction
//----------------------------------------------------------------//
//  WeaponType: This is the weapon type used by the spell         //
constant function LeLi_WeaponType takes nothing returns weapontype
    return null
endfunction
//----------------------------------------------------------------//
//  StunDissapate: This is the time in seconds in takes for the   //
//  stun dummy to be removed                                      //
constant function LeLi_StunDissapate takes nothing returns real
    return 2.
endfunction
//----------------------------------------------------------------//
//                      END OF CONFIGURATION                      //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
//  Function used to deal damage delayed to mimic ingame         //
//  mechanics, apply stun and control the animations of the      //
//  caster to allow them to visually leech, also permits         //
//  the caster to cancel the ability rather than relying on      //
//  pausing them when the spell starts                           //
///////////////////////////////////////////////////////////////////
function LeLi_Loop takes nothing returns nothing
    local integer node = 0
    local integer tempNode
    local unit u
    local real dx
    local real dy
  
    loop
        set node = udg_LeLi_NextNode[node]
        exitwhen node == 0
        set dx = GetUnitX(udg_LeLi_Unit[node]) - GetUnitX(udg_LeLi_Caster[node])
        set dy = GetUnitY(udg_LeLi_Unit[node]) - GetUnitY(udg_LeLi_Caster[node])
      
        if (udg_LeLi_Duration[node] >= 0.) and (GetUnitCurrentOrder(udg_LeLi_Caster[node]) == LeLi_Order()) and not(IsUnitType(udg_LeLi_Unit[node],UNIT_TYPE_DEAD)) and (dx * dx + dy * dy < LeLi_MaxRange()) then
            set udg_LeLi_Duration[node] = udg_LeLi_Duration[node] - LeLi_TimerSpeed()
            set udg_LeLi_Delay[node] = udg_LeLi_Delay[node] - LeLi_TimerSpeed()
          
            //Successfully grabbed, disable refund and queue animations
            call QueueUnitAnimation(udg_LeLi_Caster[node], LeLi_LoopAnimation())
            if not (udg_LeLi_GrabbedTarget[node]) then
                set udg_LeLi_GrabbedTarget[node] = true
            endif
          
            //Stun unit being leeched
            call SetUnitX(udg_LeLi_Stunner, GetUnitX(udg_LeLi_Unit[node]))
            call SetUnitY(udg_LeLi_Stunner, GetUnitY(udg_LeLi_Unit[node]))
            call IssueTargetOrderById(udg_LeLi_Stunner, LeLi_DummyStunOrder(), udg_LeLi_Unit[node])
          
            //Drain life
            if udg_LeLi_Delay[node] <= 0. then
                set udg_LeLi_Delay[node] = udg_LeLi_DelayStart[node]
                call UnitDamageTarget(udg_LeLi_Caster[node], udg_LeLi_Unit[node], udg_LeLi_HealthDamage[node], true, false, LeLi_AttackType(), LeLi_DamageType(), LeLi_WeaponType())
                call SetWidgetLife(udg_LeLi_Caster[node], GetWidgetLife(udg_LeLi_Caster[node]) + udg_LeLi_HealthHeal[node])
                call SetUnitState(udg_LeLi_Unit[node], UNIT_STATE_MANA, GetUnitState(udg_LeLi_Unit[node], UNIT_STATE_MANA) - udg_LeLi_ManaDamage[node])
                call SetUnitState(udg_LeLi_Caster[node], UNIT_STATE_MANA, GetUnitState(udg_LeLi_Caster[node], UNIT_STATE_MANA) + udg_LeLi_ManaHeal[node])
            endif
          
        else
          
            //In the event of the unit "dodging" stop the channel
            if GetUnitCurrentOrder(udg_LeLi_Caster[node]) == LeLi_Order() then
                call IssueImmediateOrderById(udg_LeLi_Caster[node], LeLi_StopOrder())
            endif
          
            //Refund manacost on whiff
            if not(udg_LeLi_GrabbedTarget[node]) then
                call SetUnitState(udg_LeLi_Caster[node], UNIT_STATE_MANA, GetUnitState(udg_LeLi_Caster[node], UNIT_STATE_MANA) + udg_LeLi_ManaRefund[node])
            endif
          
            call SetUnitPropWindow(udg_LeLi_Unit[node], GetUnitDefaultPropWindow(udg_LeLi_Unit[node]) * bj_DEGTORAD)
          
            if IsUnitType(udg_LeLi_Caster[node], UNIT_TYPE_DEAD) then
                call SetUnitAnimation(udg_LeLi_Caster[node], LeLi_DeathAnimation())
            else
                call SetUnitAnimation(udg_LeLi_Caster[node], LeLi_StopAnimation())
            endif
          
            //Recycle
            set udg_LeLi_RecycleNodes[udg_LeLi_RecyclableNodes] = node
            set udg_LeLi_RecyclableNodes = udg_LeLi_RecyclableNodes + 1
            set udg_LeLi_NextNode[udg_LeLi_PrevNode[node]] = udg_LeLi_NextNode[node]
            set udg_LeLi_PrevNode[udg_LeLi_NextNode[node]] = udg_LeLi_PrevNode[node]

            //Pause timer if this was the last instance
            if (udg_LeLi_PrevNode[0] == 0) then
                call PauseTimer(udg_LeLi_Timer)
            endif
          
        endif
      
    endloop
  
endfunction

///////////////////////////////////////////////////////////////////
//  Function used when the ability is cast to set up spell data  //
//  and to prevent the target from moving as well as work        //
//  out any bonuses from bleeding buffs                          //
///////////////////////////////////////////////////////////////////
function LeLi_Cast takes nothing returns boolean
    local integer node
    local real level
    local real tempreal
  
    if (GetSpellAbilityId() == LeLi_Ability()) then
        //Set up Node
        if (udg_LeLi_RecyclableNodes == 0) then
            set udg_LeLi_NodeNumber = udg_LeLi_NodeNumber + 1
            set node = udg_LeLi_NodeNumber
        else
            set udg_LeLi_RecyclableNodes = udg_LeLi_RecyclableNodes - 1
            set node = udg_LeLi_RecycleNodes[udg_LeLi_RecyclableNodes]
        endif

        set udg_LeLi_NextNode[node] = 0
        set udg_LeLi_NextNode[udg_LeLi_PrevNode[0]] = node
        set udg_LeLi_PrevNode[node] = udg_LeLi_PrevNode[0]
        set udg_LeLi_PrevNode[0] = node
      
        //Start timer if this is the only instance
        if (udg_LeLi_PrevNode[node] == 0) then
            call TimerStart(udg_LeLi_Timer, LeLi_TimerSpeed(), true, function LeLi_Loop)
        endif  

        //Set up date
        set udg_LeLi_Unit[node] = GetSpellTargetUnit()
        set udg_LeLi_Caster[node] = GetTriggerUnit()
        set level = GetUnitAbilityLevel(udg_LeLi_Caster[node], LeLi_Ability())
        set udg_LeLi_Duration[node] = LeLi_Duration(level)
        set udg_LeLi_DelayStart[node] = LeLi_Delay(level)
        set udg_LeLi_Delay[node] = udg_LeLi_DelayStart[node]
        set udg_LeLi_GrabbedTarget[node] = false
        set udg_LeLi_ManaRefund[node] = LeLi_ManaRefund(level)
        set tempreal = udg_LeLi_Duration[node] / udg_LeLi_Delay[node]
        call SetUnitPropWindow(udg_LeLi_Unit[node], 0)
        call SetUnitAnimation(udg_LeLi_Caster[node], LeLi_LoopAnimation())
      
        //Apply bonuses
        if GetUnitAbilityLevel(udg_LeLi_Caster[node], LeLi_BonusBuff()) > 0 then
            set udg_LeLi_HealthDamage[node] = (LeLi_HealthDamage(level) + LeLi_BonusHealthDamage(level)) / tempreal
            set udg_LeLi_ManaDamage[node] = (LeLi_ManaDamage(level) + LeLi_BonusManaDamage(level)) / tempreal
        else
            set udg_LeLi_HealthDamage[node] = LeLi_HealthDamage(level) / tempreal
            set udg_LeLi_ManaDamage[node] = LeLi_ManaDamage(level) / tempreal
        endif
      
        if GetUnitAbilityLevel(udg_LeLi_Unit[node], LeLi_BleedBuff()) > 0 then
            set udg_LeLi_HealthHeal[node] = (LeLi_HealthHeal(level) + LeLi_BonusHealthHeal(level)) / tempreal
            set udg_LeLi_ManaHeal[node] = (LeLi_ManaHeal(level) + LeLi_BonusManaHeal(level)) / tempreal
        else
            set udg_LeLi_HealthHeal[node] = LeLi_HealthHeal(level) / tempreal
            set udg_LeLi_ManaHeal[node] = LeLi_ManaHeal(level) / tempreal
        endif
      
    endif
  
    return false
endfunction

///////////////////////////////////////////////////////////////////
//  Function used to initialise the event listeners of the spell //
///////////////////////////////////////////////////////////////////
function InitTrig_Leech_Life takes nothing returns nothing
    local trigger t = CreateTrigger()
  
    //Set up trigger
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function LeLi_Cast))
  
    //Create stunning unit
    set udg_LeLi_Stunner = CreateUnit(LeLi_DummyPlayer(), LeLi_DummyUnit(), 0., 0., 0.)
    call UnitAddAbility(udg_LeLi_Stunner, LeLi_DummyStunAbility())
    call UnitRemoveAbility(udg_LeLi_Stunner, 'Amov')
endfunction
///////////////////////////////////////////////////////////////////
//  END OF THE SPELL                                             //
///////////////////////////////////////////////////////////////////
GIFCode
corpse-crusher-gif-gif.246774
JASS:
////////////////////////////////////////////////////////////////////
//                     CORPSE CRUSHER V1.01                       //
//  Author: Tank-Commander                                        //
//  Requires: Dummy.mdl                                           //
//  Purpose: Sustain ability to convert corpses into health &     //
//           mana, can also be used to slow units in an AOE       //
//                                                                //
//  Credits: Vexorian (dummy.mdl)                                 //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the system    //
//                                                                //
//                                                                //
//  If you have used this spell in your map, you are required     //
//  to give credits to Tank-Commander for the creation of it      //
//  If you would like to use snippets of code from this for       //
//  whatever, getting permission and crediting the source/linking //
//  would be much appreciated.                                    //
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  README:                                                       //
//    Before modifying this spell a few things need to be         //
//    understood and read, this is one of those things, while     //
//    most modification can be considered intuitive, it still     //
//    helps to read through these intstructions, as they will     //
//    inform you about how to configure this spell to your        //
//    desire.                                                     //
//----------------------------------------------------------------//
//  Initial importing: The variable creator trigger can be        //
//  imported first and if you have the correct settings (file,    //
//  preferences, General, automatically create unknown variables  //
//  checked, then when you paste in the variable creator it       //
//  will automatically give you all the variables you need for    //
//  this spell                                                    //
//                                                                //
//  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   //
//  value are not the same as listed in the configuration, those  //
//  configurables will need to be changed to work correctly       //
//----------------------------------------------------------------//
//  TimerSpeed: This is the rate at which the loop timer of the   //
//  ability runs the default value 0.03125 allows the loop        //
//  function to run 32 times per second giving a smooth ability   //
constant function CoCr_TimerSpeed takes nothing returns real
    return 0.031250000
endfunction
//----------------------------------------------------------------//
//  CheckTimerRate: This is the time in seconds between each      //
//  check for units that have been added to the map with the      //
//  ability (use if units are trained or spawned throughout the   //
//  map)                                                          //
constant function CoCr_CheckTimerRate takes nothing returns real
    return 0.5
endfunction
//----------------------------------------------------------------//
//  CheckTimerPeriodic: Set to true if units that have the        //
//  ability aren't preplaced in the map but also don't learn      //
//  the ability like heroes (trained units for example)           //
constant function CoCr_CheckTimerPeriodic takes nothing returns boolean
    return false
endfunction
//----------------------------------------------------------------//
//  AuraDelay: This is the time in seconds between slowing new    //
//  units and removing it from ones whom have left the AOE        //
constant function CoCr_AuraDelay takes nothing returns real
    return 0.25
endfunction
//----------------------------------------------------------------//
//  Ability: This is the raw code for the ability used as a base  //
//  to see raw data press ctrl + d in the object editor, do it    //
//  again to revert to normal                                     //
constant function CoCr_Ability takes nothing returns integer
    return 'A004'
endfunction
//----------------------------------------------------------------//
//  SlowAbility: This is the raw code for the ability used to     //
//  slow units in the blood pool, should be based off slow aura   //
constant function CoCr_SlowAbility takes nothing returns integer
    return 'A007'
endfunction
//----------------------------------------------------------------//
//  BonusBuff: This is the raw code for the ability used to       //
//  Apply speed bonuses to the ability (it can be either a        //
//  regular ability or a specific buff which the caster can be    //
//  afflicted with to give optionally temporary or permanent      //
//  buffs to this ability                                         //
constant function CoCr_BonusBuff takes nothing returns integer
    return 'A005'
endfunction
//----------------------------------------------------------------//
//  BleedBuff: This is the raw code of the ability used to        //
//  restore mana to the caster if the target has this buff        //
constant function CoCr_BleedBuff takes nothing returns integer
    return 'A001'
endfunction
//----------------------------------------------------------------//
//  DummyPlayer: This is the player who will own all dummy units  //
//  created by this spell, it should be a neutral player as to    //
//  not mess with scorescreens                                    //
constant function CoCr_DummyPlayer takes nothing returns player
    return Player(14)
endfunction
//----------------------------------------------------------------//
//  DummyUnit: This is the raw code for the unit used as a dummy  //
//  by the ability, it should use dummy.mdl as its model or some  //
//  other invisible model                                         //
constant function CoCr_DummyUnit takes nothing returns integer
    return 'u000'
endfunction
//----------------------------------------------------------------//
//  BloodEffect: This is the effect attached to the dummy once    //
//  it lands at the target point                                  //
constant function CoCr_BloodEffect takes nothing returns string
    return "Abilities\\Spells\\Other\\HowlOfTerror\\HowlCaster.mdl"
endfunction
//----------------------------------------------------------------//
//  AttachmentPointBlood: This is the location on the dummy model //
//  that the blood effect is placed onto                          //
constant function CoCr_AttachmentPointBlood takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
//  BloodMiniEffect: This is the effect used to create the pool   //
//  of blood marking the AOE of the ability                       //
constant function CoCr_BloodMiniEffect takes nothing returns string
    return "Abilities\\Weapons\\SludgeMissile\\SludgeMissile.mdl"
endfunction
//----------------------------------------------------------------//
//  AttachmentPointMini: This is the location on the dummy model  //
//  that the mini blood effect is placed onto                     //
constant function CoCr_AttachmentPointMini takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
//  BleedHealEffect: This is the effect created when the ability  //
//  hits a target affected by BleedBuff                           //
constant function CoCr_BleedHealEffect takes nothing returns string
    return "Abilities\\Spells\\Items\\AIma\\AImaTarget.mdl"
endfunction
//----------------------------------------------------------------//
//  AttachmentPointBleedHeal: This is the location on the dummy   //
//  model that the healing effect is placed onto                  //
constant function CoCr_AttachmentPointBleedHeal takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
//  CorpseCrushEffect: This is the effect created when a corpse   //
//  is consumed by the caster                                     //
constant function CoCr_CorpseCrushEffect takes nothing returns string
    return "Objects\\Spawnmodels\\Orc\\OrcLargeDeathExplode\\OrcLargeDeathExplode.mdl"
endfunction
//----------------------------------------------------------------//
//  CorpseHealEffectL This is the effect created on the caster    //
//  when they consume a corpse                                    //
constant function CoCr_CorpseHealEffect takes nothing returns string
    return "Objects\\Spawnmodels\\Orc\\OrcSmallDeathExplode\\OrcSmallDeathExplode.mdl"
endfunction
//----------------------------------------------------------------//
//  AttachmentPointCorpseHeal: This is the location on the dummy  //
//  model that the crushing effect is placed onto                 //
constant function CoCr_AttachmentPointCorpseHeal takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
//  CorpseLaunchEffect: This is the model used for the corpse     //
//  launched toward the target point of the ability from the      //
//  caster                                                        //
constant function CoCr_CorpseLaunchEffect takes nothing returns string
    return "Abilities\\Weapons\\MeatwagonMissile\\MeatwagonMissile.mdl"
endfunction
//----------------------------------------------------------------//
//  AttachmentPointLaunch: This is the location on the dummy      //
//  model that the crushing effect is placed onto                 //
constant function CoCr_AttachmentPointLaunch takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
//  SlowBloodScale: This is the size of the BloodEffect 1 = 100%  //
//  scale or default size                                         //
function CoCr_SlowBloodScale takes real level returns real
    return 1.5
endfunction
//----------------------------------------------------------------//
//  SlowCorpseScale: This is the size of the CorpseLaunchEffect   //
//  1 = 100% scale of default size                                //
function CoCr_SlowCorpseScale takes real level returns real
    return 2.
endfunction
//----------------------------------------------------------------//
//  SlowBloodMiniScale: This is the size of the BloodMiniEffect   //
//  1 = 100% scale of default size                                //
constant function CoCr_SlowBloodMiniScale takes nothing returns real
    return 1.5
endfunction
//----------------------------------------------------------------//
//  SlowBloodMiniSpace: This is the amount of space taken up      //
//  by each BloodMiniEffect - this allows for the models to make  //
//  a seemless pool and should be adjusted to match each model    //
//  try to use as few effects as possible                         //
constant function CoCr_SlowBloodMiniSpace takes nothing returns real
    return 75.
endfunction
//----------------------------------------------------------------//
//  SlowSpinSpeed: This is the degrees per second that each       //
//  BloodMiniEffect moves around in the circle around the centre  //
//  of the blood pool                                             //
constant function CoCr_SlowSpinSpeed takes nothing returns real
    return 73.3385978 * CoCr_TimerSpeed() * bj_DEGTORAD
endfunction
//----------------------------------------------------------------//
//  Gravity: This is the strength of gravity pulling the corpse   //
//  thrown back into the ground, higher values lead to a sharper  //
//  arc taken by the projectile                                   //
constant function CoCr_Gravity takes nothing returns real
    return -60. * CoCr_TimerSpeed()
endfunction
//----------------------------------------------------------------//
//  HeightLet: This is the fly height which is considered close   //
//  enough to the ground to count as grounded (this is because    //
//  fly heights when often set to 0 actually set to ~0.04 so      //
//  comparisons for current fly height below 0 will not work)     //
constant function CoCr_HeightLet takes nothing returns real
    return 5.
endfunction
//----------------------------------------------------------------//
//  StartHeight: This is the starting fly height of the corpse    //
constant function CoCr_StartHeight takes nothing returns real
    return 50.
endfunction
//----------------------------------------------------------------//
//  CorpseAOE: This is the distance from a corpse to the caster   //
//  for the caster to consume the corpse                          //
function CoCr_CorpseAOE takes real level returns real
    return 150.
endfunction
//----------------------------------------------------------------//
//  CorpseHealHealth: This is the health restored to the caster   //
//  by consuming a corpse                                         //
function CoCr_CorpseHealHealth takes real level returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  CorpseHealMana: This is the mana restored to the caster by    //
//  consuming a corpse                                            //
function CoCr_CorpseHealMana takes real level returns real
    return 30 + (10 * (level - 1))
endfunction
//----------------------------------------------------------------//
//  SlowAOE: This is the distance from the centre of the blood    //
//  pool that units within are affected by the slow of the pool   //
function CoCr_SlowAOE takes real level returns real
    return 250.
endfunction
//----------------------------------------------------------------//
//  SlowDuration: This is the duration that the slowing blood     //
//  pool lasts for in seconds                                     //
function CoCr_SlowDuration takes real level returns real
    return 4. + (1. * level)
endfunction
//----------------------------------------------------------------//
//  SlowHealthDamage: This is the damage dealt to units hit by    //
//  the launched corpse upon impact                               //
function CoCr_SlowHealthDamage takes real level returns real
    return 50.
endfunction
//----------------------------------------------------------------//
//  SlowManaDamage: This is the amount of mana lost by units hit  //
//  by the launched corpse upon impact                            //
function CoCr_SlowManaDamage takes real level returns real  
    return 0.
endfunction
//----------------------------------------------------------------//
//  SlowSpeed: This is the speed of the launched corpse in        //
//  distance per second                                           //
function CoCr_SlowSpeed takes real level returns real
    return 1400. * CoCr_TimerSpeed()
endfunction
//----------------------------------------------------------------//
//  SlowBonusSpeed: This is the bonus speed of the launched       //
//  corpse in distance per second when the casted is affected by  //
//  BonusBuff                                                     //
function CoCr_SlowBonusSpeed takes real level returns real
    return 800. * CoCr_TimerSpeed()
endfunction
//----------------------------------------------------------------//
//  BleedHealHealth: This is the amount of health restored to     //
//  the caster if a target hit by the launched corpse is affected //
//  by BleedBuff                                                  //
function CoCr_BleedHealHealth takes real level returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  BleedHealMana: This is the amount of mana restored to the     //
//  caster if a target hit by the launched corpse is affected     //
//  by BleedBuff                                                  //
function CoCr_BleedHealMana takes real level returns real
    return 25.
endfunction
//----------------------------------------------------------------//
//  AttackType: This is the attack type used by the spell         //
constant function CoCr_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_NORMAL
endfunction
//----------------------------------------------------------------//
//  DamageType: This is the damagetype used by the spell, armour  //
//  reduces the damage based on this type                         //
constant function CoCr_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_NORMAL
endfunction
//----------------------------------------------------------------//
//  WeaponType: This is the weapon type used by the spell         //
constant function CoCr_WeaponType takes nothing returns weapontype
    return null
endfunction
//----------------------------------------------------------------//
//                         READ ONLY                              //
//----------------------------------------------------------------//
//  CasterStageID: StageID used by casters in the spell           //
constant function CoCr_CasterStageID takes nothing returns integer
    return 1
endfunction
//----------------------------------------------------------------//
//  CorpseStageID: StageID used by launched corpses in the spell  //
constant function CoCr_CorpseStageID takes nothing returns integer
    return 2
endfunction
//----------------------------------------------------------------//
//  BloodPoolStageID: StageID used by the central dummy in the    //
//  blood pool                                                    //
constant function CoCr_BloodPoolStageID takes nothing returns integer
    return 3
endfunction
//----------------------------------------------------------------//
//  BloodPoolMiniStageID: StageID used by the spinning effects    //
//  within the AOE of the blood pool                              //
constant function CoCr_BloodPoolMiniStageID takes nothing returns integer
    return 4
endfunction
//----------------------------------------------------------------//
//  2PI: stores the value of 2 * PI in order to keep processing   //
//  fast                                                          //
constant function CoCr_2PI takes nothing returns real
    return 2 * bj_PI
endfunction
//----------------------------------------------------------------//
//                      END OF CONFIGURATION                      //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  Function used to distinguish what counts as a valid target    //
//  for the slowing caused by the pool of blood                   //
////////////////////////////////////////////////////////////////////
function CoCr_DamageFilter takes unit u, player pl returns boolean
    return IsUnitEnemy(u, pl) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE) or IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) or IsUnitType(u, UNIT_TYPE_DEAD) or IsUnitType(u, UNIT_TYPE_FLYING) or GetUnitTypeId(u) == 0)
endfunction

////////////////////////////////////////////////////////////////////
//  This function is used to distinguish what counts as a corpse  //
//  to the ability (this stops heroes/structures and locusts      //
//  being counted as a valid corpse)                              //
////////////////////////////////////////////////////////////////////
function CoCr_CorpseFilter takes unit u returns boolean
    return IsUnitType(u, UNIT_TYPE_DEAD) and not(IsUnitType(u, UNIT_TYPE_HERO)) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE)) and GetUnitAbilityLevel(u,'Aloc') == 0 and not(IsUnitType(u, UNIT_TYPE_SUMMONED)) and not(IsUnitType(u, UNIT_TYPE_MECHANICAL))
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to update level data of a particular node       //
////////////////////////////////////////////////////////////////////
function CoCr_SetLevelData takes unit u, real level, integer node returns nothing
    //Set up all the level data
    set udg_CoCr_Unit[node] = u
    set udg_CoCr_CorpseAOE[node] = CoCr_CorpseAOE(level)
    set udg_CoCr_CorpseHealHealth[node] = CoCr_CorpseHealHealth(level)
    set udg_CoCr_CorpseHealMana[node] = CoCr_CorpseHealMana(level)
    set udg_CoCr_SlowAOE[node] = CoCr_SlowAOE(level)
    set udg_CoCr_SlowBloodScale[node] = CoCr_SlowBloodScale(level)
    set udg_CoCr_SlowCorpseScale[node] = CoCr_SlowCorpseScale(level)
    set udg_CoCr_SlowDuration[node] = CoCr_SlowDuration(level)
    set udg_CoCr_SlowHealthDamage[node] = CoCr_SlowHealthDamage(level)
    set udg_CoCr_SlowManaDamage[node] = CoCr_SlowManaDamage(level)
    set udg_CoCr_SlowSpeed[node] = CoCr_SlowSpeed(level)
    set udg_CoCr_SlowBonusSpeed[node] = CoCr_SlowBonusSpeed(level)
    set udg_CoCr_BleedHealHealth[node] = CoCr_BleedHealHealth(level)
    set udg_CoCr_BleedHealMana[node] = CoCr_BleedHealMana(level)
    set udg_CoCr_Level[node] = R2I(level)
    set udg_CoCr_StageID[node] = CoCr_CasterStageID()
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to get the Z height of various locations        //
////////////////////////////////////////////////////////////////////
function CoCr_GetZ takes real x, real y returns real

    call MoveLocation(udg_CoCr_ZLoc, x, y)
    return GetLocationZ(udg_CoCr_ZLoc)
  
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to remove nodes from the linked list            //
////////////////////////////////////////////////////////////////////
function CoCr_RecycleNode takes integer node returns nothing
    //Recycle
    set udg_CoCr_RecycleNodes[udg_CoCr_RecyclableNodes] = node
    set udg_CoCr_RecyclableNodes = udg_CoCr_RecyclableNodes + 1
    set udg_CoCr_NextNode[udg_CoCr_PrevNode[node]] = udg_CoCr_NextNode[node]
    set udg_CoCr_PrevNode[udg_CoCr_NextNode[node]] = udg_CoCr_PrevNode[node]
  
    //Pause timer if this was the last instance
    if (udg_CoCr_PrevNode[0] == 0) then
        call PauseTimer(udg_CoCr_Timer)
    endif
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to add Nodes to the linked list                 //
////////////////////////////////////////////////////////////////////
function CoCr_CreateNode takes nothing returns integer
    local integer node
    //Set up Node
    if (udg_CoCr_RecyclableNodes == 0) then
        set udg_CoCr_NodeNumber = udg_CoCr_NodeNumber + 1
        set node = udg_CoCr_NodeNumber
    else
        set udg_CoCr_RecyclableNodes = udg_CoCr_RecyclableNodes - 1
        set node = udg_CoCr_RecycleNodes[udg_CoCr_RecyclableNodes]
    endif

    set udg_CoCr_NextNode[node] = 0
    set udg_CoCr_NextNode[udg_CoCr_PrevNode[0]] = node
    set udg_CoCr_PrevNode[node] = udg_CoCr_PrevNode[0]
    set udg_CoCr_PrevNode[0] = node
  
    return node
endfunction
////////////////////////////////////////////////////////////////////
//  Function used to remove the aura from units that are no       //
//  longer being affected by a blood pool                         //
////////////////////////////////////////////////////////////////////
function CoCr_AuraRemove takes nothing returns nothing
    local unit u = GetEnumUnit()
  
    if not(IsUnitInGroup(u, udg_CoCr_TempGroup2)) then
        call UnitRemoveAbility(u, CoCr_SlowAbility())
        call GroupRemoveUnit(udg_CoCr_SlowedGroup, u)
    endif
  
    set u = null
endfunction

////////////////////////////////////////////////////////////////////
//  Main function used to handle all of the various aspects of    //
//  the ability from consuming corpses to moving the projectile   //
//  and managing units that are being affected by the blood pool  //
////////////////////////////////////////////////////////////////////
function CoCr_Loop takes nothing returns nothing
    local integer node = 0
    local integer tempNode
    local real dist
    local real angle
    local real angle2
    local real anglelet
    local real circ
    local unit u
    local real x
    local real y
    local boolean b
  
    loop
        set node = udg_CoCr_NextNode[node]
        exitwhen node == 0
      
        if udg_CoCr_StageID[node] == CoCr_CasterStageID() then
            if not(GetUnitTypeId(udg_CoCr_Unit[node]) == null) then
              
                //Make sure the unit is alive and hasn't used a tome of retraining
                if not(IsUnitType(udg_CoCr_Unit[node], UNIT_TYPE_DEAD)) and GetUnitAbilityLevel(udg_CoCr_Unit[node], CoCr_Ability()) > 0 then
                    //Find corpses
                    call GroupEnumUnitsInRange(udg_CoCr_TempGroup, GetUnitX(udg_CoCr_Unit[node]), GetUnitY(udg_CoCr_Unit[node]), udg_CoCr_CorpseAOE[node], null)
              
                    loop
                        set u = FirstOfGroup(udg_CoCr_TempGroup)
                        exitwhen u == null
                        call GroupRemoveUnit(udg_CoCr_TempGroup, u)
                        if CoCr_CorpseFilter(u) then
                            //Splatter corpse and heal the caster
                            call DestroyEffect(AddSpecialEffect(CoCr_CorpseCrushEffect(), GetUnitX(u), GetUnitY(u)))
                            call SetWidgetLife(udg_CoCr_Unit[node], GetWidgetLife(udg_CoCr_Unit[node]) + udg_CoCr_CorpseHealHealth[node])
                            call SetUnitState(udg_CoCr_Unit[node], UNIT_STATE_MANA, GetUnitState(udg_CoCr_Unit[node], UNIT_STATE_MANA) + udg_CoCr_CorpseHealMana[node])
                            call DestroyEffect(AddSpecialEffectTarget(CoCr_CorpseHealEffect(), udg_CoCr_Unit[node], CoCr_AttachmentPointCorpseHeal()))
                            call RemoveUnit(u)
                        endif
                      
                    endloop
                  
                elseif GetUnitAbilityLevel(udg_CoCr_Unit[node], CoCr_Ability()) == 0 then
                    call CoCr_RecycleNode(node)
                endif
              
            else
                call CoCr_RecycleNode(node)
            endif
          
        elseif udg_CoCr_StageID[node] == CoCr_CorpseStageID() then
            set x = GetUnitX(udg_CoCr_Unit[node]) + udg_CoCr_XVel[node]
            set y = GetUnitY(udg_CoCr_Unit[node]) + udg_CoCr_YVel[node]
          
            //Keep the unit inside the map bounds
            if ((udg_CoCr_MapMinX <= x) and (x <= udg_CoCr_MapMaxX) and (udg_CoCr_MapMinY <= y)and (y <= udg_CoCr_MapMaxY)) then
                call SetUnitX(udg_CoCr_Unit[node], x)
                call SetUnitY(udg_CoCr_Unit[node], y)
            endif
          
            //Change fly height
            set udg_CoCr_CurrentHeight[node] = udg_CoCr_CurrentHeight[node] + udg_CoCr_ZVel[node]
            set udg_CoCr_ZVel[node] = udg_CoCr_ZVel[node] + CoCr_Gravity()
            call SetUnitFlyHeight(udg_CoCr_Unit[node], udg_CoCr_CurrentHeight[node] - CoCr_GetZ(x, y), 0.)
          
            //Check for impact
            if GetUnitFlyHeight(udg_CoCr_Unit[node]) <= CoCr_HeightLet() then
                set udg_CoCr_X[node] = x
                set udg_CoCr_Y[node] = y
                set udg_CoCr_ZVel[node] = GetUnitFacing(udg_CoCr_Unit[node]) * bj_RADTODEG
                set udg_CoCr_StageID[node] = CoCr_BloodPoolStageID()
                call DestroyEffect(udg_CoCr_Effect[node])
                set udg_CoCr_Effect[node] = AddSpecialEffectTarget(CoCr_BloodEffect(), udg_CoCr_Unit[node], CoCr_AttachmentPointBlood())
                call SetUnitScale(udg_CoCr_Unit[node], udg_CoCr_SlowBloodScale[node], 0., 0.)
              
                //Damage
                set b = false
                call GroupEnumUnitsInRange(udg_CoCr_TempGroup, x, y, udg_CoCr_SlowAOE[node], null)
              
                loop
                    set u = FirstOfGroup(udg_CoCr_TempGroup)
                    exitwhen u == null
                    call GroupRemoveUnit(udg_CoCr_TempGroup, u)
                  
                    if CoCr_DamageFilter(u, udg_CoCr_Player[node]) then
                        call UnitDamageTarget(udg_CoCr_Caster[node], u, udg_CoCr_SlowHealthDamage[node], true, true, CoCr_AttackType(), CoCr_DamageType(), CoCr_WeaponType())
                        call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) - udg_CoCr_SlowManaDamage[node])
                  
                        //Heal Bonuses
                        if not(b) and GetUnitAbilityLevel(u, CoCr_BleedBuff()) > 0 then
                            set b = true
                            call SetWidgetLife(udg_CoCr_Caster[node], GetWidgetLife(udg_CoCr_Caster[node]) + udg_CoCr_BleedHealHealth[node])
                            call SetUnitState(udg_CoCr_Caster[node], UNIT_STATE_MANA, GetUnitState(udg_CoCr_Caster[node], UNIT_STATE_MANA) + udg_CoCr_BleedHealMana[node])
                            call DestroyEffect(AddSpecialEffectTarget(CoCr_BleedHealEffect(), udg_CoCr_Caster[node], CoCr_AttachmentPointBleedHeal()))
                        endif
                      
                    endif
                  
                endloop
              
                //Creating the aura effect
                set dist = CoCr_SlowBloodMiniSpace()
                set b = false
              
                loop
                    exitwhen dist > udg_CoCr_SlowAOE[node]
                  
                    set angle2 = CoCr_2PI() / ((CoCr_2PI() * dist) / CoCr_SlowBloodMiniSpace())
                    set angle = angle2
                    set anglelet = CoCr_2PI() + (angle2 / 2)
                    set b = not(b)
                  
                    loop
                        exitwhen angle > anglelet
                      
                        //Create new aura segment and apply effects
                        set x = udg_CoCr_X[node] + dist * Cos(angle)
                        set y = udg_CoCr_Y[node] + dist * Sin(angle)
                        set tempNode = CoCr_CreateNode()
                        set udg_CoCr_Unit[tempNode] = CreateUnit(CoCr_DummyPlayer(), CoCr_DummyUnit(), x, y, angle * bj_RADTODEG + 90)
                        set udg_CoCr_Effect[tempNode] = AddSpecialEffectTarget(CoCr_BloodMiniEffect(), udg_CoCr_Unit[tempNode], CoCr_AttachmentPointMini())
                        call SetUnitScale(udg_CoCr_Unit[tempNode], CoCr_SlowBloodMiniScale(), 0., 0.)
                        call SetUnitFlyHeight(udg_CoCr_Unit[tempNode], 0., 0.)
                        set udg_CoCr_Direction[tempNode] = b
                        set udg_CoCr_HomeNode[tempNode] = node
                        set udg_CoCr_X[tempNode] = dist
                        set udg_CoCr_ZVel[tempNode] = angle
                        set udg_CoCr_StageID[tempNode] = CoCr_BloodPoolMiniStageID()
                      
                        set angle = angle + angle2
                    endloop
                  
                    set dist = dist + CoCr_SlowBloodMiniSpace()
                endloop
              
            endif
         
        elseif udg_CoCr_StageID[node] == CoCr_BloodPoolMiniStageID() then
          
            //Check if the aura is still active
            if IsUnitType(udg_CoCr_Unit[udg_CoCr_HomeNode[node]], UNIT_TYPE_DEAD) or GetUnitTypeId(udg_CoCr_Unit[udg_CoCr_HomeNode[node]]) == 0 then
                call DestroyEffect(udg_CoCr_Effect[node])
                call SetWidgetLife(udg_CoCr_Unit[node], 0.)
                call CoCr_RecycleNode(node)
            else
          
                //Update aura positions
                if udg_CoCr_Direction[node] then
                    set angle = udg_CoCr_ZVel[node] + udg_CoCr_ZVel[udg_CoCr_HomeNode[node]]
                else
                    set angle = udg_CoCr_ZVel[node] - udg_CoCr_ZVel[udg_CoCr_HomeNode[node]]
                endif
          
                call SetUnitX(udg_CoCr_Unit[node], udg_CoCr_X[udg_CoCr_HomeNode[node]] + udg_CoCr_X[node] * Cos(angle))
                call SetUnitY(udg_CoCr_Unit[node], udg_CoCr_Y[udg_CoCr_HomeNode[node]] + udg_CoCr_X[node] * Sin(angle))
                call SetUnitFacing(udg_CoCr_Unit[node], angle * bj_RADTODEG + 90)
            endif
          
        elseif udg_CoCr_SlowDuration[node] > 0 then
            set udg_CoCr_SlowDuration[node] = udg_CoCr_SlowDuration[node] - CoCr_TimerSpeed()
            set udg_CoCr_ZVel[node] = udg_CoCr_ZVel[node] + CoCr_SlowSpinSpeed()
            //Slow units
            call GroupEnumUnitsInRange(udg_CoCr_TempGroup, udg_CoCr_X[node], udg_CoCr_Y[node], udg_CoCr_SlowAOE[node], null)
          
            loop
                set u = FirstOfGroup(udg_CoCr_TempGroup)
                exitwhen u == null              
                call GroupRemoveUnit(udg_CoCr_TempGroup, u)

                if CoCr_DamageFilter(u, udg_CoCr_Player[node]) then
                    call GroupAddUnit(udg_CoCr_TempGroup2, u)
              
                    if not(IsUnitInGroup(u, udg_CoCr_SlowedGroup)) then
                        call GroupAddUnit(udg_CoCr_SlowedGroup, u)
                    endif
              
                    call UnitAddAbility(u, CoCr_SlowAbility())
                    call SetUnitAbilityLevel(u, CoCr_SlowAbility(), udg_CoCr_Level[node])              
                endif
              
            endloop
          
        else
            //Remove dummy
            call DestroyEffect(udg_CoCr_Effect[node])
            call SetWidgetLife(udg_CoCr_Unit[node], 0.)
            call CoCr_RecycleNode(node)
        endif
      
    endloop
  
    //Clean up slowed units
    if udg_CoCr_AuraDelay < 0 then
        set udg_CoCr_AuraDelay = CoCr_AuraDelay()
        call ForGroup(udg_CoCr_SlowedGroup, function CoCr_AuraRemove)
        call GroupClear(udg_CoCr_TempGroup2)
    else
        set udg_CoCr_AuraDelay = udg_CoCr_AuraDelay - CoCr_TimerSpeed()
    endif
  
endfunction

////////////////////////////////////////////////////////////////////
//  This function is used to find the node of units registered    //
//  in the system, it creates a node for the unit if no node      //
//  can be found for it                                           //
////////////////////////////////////////////////////////////////////
function CoCr_GetUnitNode takes unit u returns integer
    local integer node = 0
  
    loop
        set node = udg_CoCr_NextNode[node]
        exitwhen node == 0
      
        if udg_CoCr_Unit[node] == u then
            return node
        endif
      
    endloop
  
    set node = CoCr_CreateNode()
  
    //Start timer if this is the only instance
    if (udg_CoCr_PrevNode[node] == 0) then
        call TimerStart(udg_CoCr_Timer, CoCr_TimerSpeed(), true, function CoCr_Loop)
    endif
  
    return node
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to create spell data used by the corpse in      //
//  transit and the slow aura area upon landing                   //
////////////////////////////////////////////////////////////////////
function CoCr_Cast takes nothing returns boolean
    local integer node
    local integer tempNode
    local real speed
    local real angle
    local real x
    local real y
    local real x2
    local real y2
    local real dx
    local real dy
  
    if (GetSpellAbilityId() == CoCr_Ability()) then
        set node = CoCr_CreateNode()
        set tempNode = CoCr_GetUnitNode(GetTriggerUnit())
      
        //Set up data
        set udg_CoCr_StageID[node] = CoCr_CorpseStageID()
        set udg_CoCr_Caster[node] = udg_CoCr_Unit[tempNode]
      
        set x = GetUnitX(udg_CoCr_Unit[tempNode])
        set y = GetUnitY(udg_CoCr_Unit[tempNode])
        set x2 = GetSpellTargetX()
        set y2 = GetSpellTargetY()
        set dx = x2 - x
        set dy = y2 - y
        set angle = Atan2(dy, dx)
      
        //Create unit and apply effects
        set udg_CoCr_Unit[node] = CreateUnit(CoCr_DummyPlayer(), CoCr_DummyUnit(), x, y, angle * bj_RADTODEG)
        set udg_CoCr_Player[node] = GetOwningPlayer(udg_CoCr_Unit[tempNode])
        call PauseUnit(udg_CoCr_Unit[node], true)
        if UnitAddAbility(udg_CoCr_Unit[node], 'Amrf') and UnitRemoveAbility(udg_CoCr_Unit[node], 'Amrf') then
        endif
        set udg_CoCr_Effect[node] = AddSpecialEffectTarget(CoCr_CorpseLaunchEffect(), udg_CoCr_Unit[node], CoCr_AttachmentPointLaunch())
        call SetUnitScale(udg_CoCr_Unit[node], udg_CoCr_SlowCorpseScale[tempNode], 0., 0.)
      
        if GetUnitAbilityLevel(udg_CoCr_Caster[node], CoCr_BonusBuff()) > 0 then
            set speed = udg_CoCr_SlowSpeed[tempNode] + udg_CoCr_SlowBonusSpeed[tempNode]
        else
            set speed = udg_CoCr_SlowSpeed[tempNode]
        endif
      
        set udg_CoCr_SlowAOE[node] = udg_CoCr_SlowAOE[tempNode]
        set udg_CoCr_XVel[node] = speed * Cos(angle)
        set udg_CoCr_YVel[node] = speed * Sin(angle)
        set speed = SquareRoot(dx * dx + dy * dy) / speed
        set udg_CoCr_CurrentHeight[node] = CoCr_GetZ(x,y) + CoCr_StartHeight()
        set udg_CoCr_ZVel[node] = ((CoCr_GetZ(x2, y2) - udg_CoCr_CurrentHeight[node]) - (CoCr_Gravity() * speed * speed) / 2)  / speed
        //Duplicate data to prevent level up mid-flight affecting the ability
        set udg_CoCr_SlowHealthDamage[node] = udg_CoCr_SlowHealthDamage[tempNode]
        set udg_CoCr_SlowManaDamage[node] = udg_CoCr_SlowManaDamage[tempNode]
        set udg_CoCr_SlowBloodScale[node] = udg_CoCr_SlowBloodScale[tempNode]
        set udg_CoCr_SlowDuration[node] = udg_CoCr_SlowDuration[tempNode]
        set udg_CoCr_BleedHealHealth[node] = udg_CoCr_BleedHealHealth[tempNode]
        set udg_CoCr_BleedHealMana[node] = udg_CoCr_BleedHealMana[tempNode]
        set udg_CoCr_Level[node] = udg_CoCr_Level[tempNode]
        call SetUnitFlyHeight(udg_CoCr_Unit[node], CoCr_StartHeight(), 0.)
    endif
  
    return false
endfunction

////////////////////////////////////////////////////////////////////
//  Function used when a hero learns an ability to update their   //
//  level data if they have a node (and create one for them if    //
//  they don't)                                                   //
////////////////////////////////////////////////////////////////////
function CoCr_Update takes nothing returns boolean
    local unit u
  
    if GetLearnedSkill() == CoCr_Ability() then
        set u = GetTriggerUnit()
        //Set up all data
        call CoCr_SetLevelData(u, GetLearnedSkillLevel(), CoCr_GetUnitNode(u))
        set u = null
    endif
  
    return false
endfunction


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

        set level = GetUnitAbilityLevel(u, CoCr_Ability())
        //Checks that the selected unit has the ability
        if (level > 0) then
            //Attempts to add the unit
            call CoCr_SetLevelData(u, level, CoCr_GetUnitNode(u))
        endif
 
        call GroupRemoveUnit(udg_CoCr_TempGroup, u)
    endloop
  
    //Checks if this function will run constantly, if not then destroy the trigger
    if (not(CoCr_CheckTimerPeriodic())) then
        call DestroyTrigger(GetTriggeringTrigger())
    endif

    return false
endfunction

///////////////////////////////////////////////////////////////////
//  Function used to initialise the event listeners of the spell //
///////////////////////////////////////////////////////////////////
function InitTrig_Corpse_Crusher takes nothing returns nothing
    local trigger t = CreateTrigger()
  
    //Set up cast trigger
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function CoCr_Cast))
    //Set up learn trigger
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_SKILL)
    call TriggerAddCondition(t, Condition(function CoCr_Update))
    //Set up initial hero enabling trigger
    set t = CreateTrigger()
    //Sets up the event listener, if the function is periodic, then it will run constantly instead of just at the start
    call TriggerRegisterTimerEvent(t, CoCr_CheckTimerRate(), CoCr_CheckTimerPeriodic())
    call TriggerAddCondition(t, Condition(function CoCr_RegisterExisting))
  
    set udg_CoCr_ZLoc = Location(0., 0.)
    set udg_CoCr_MapMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
    set udg_CoCr_MapMinX = GetRectMinX(bj_mapInitialPlayableArea)
    set udg_CoCr_MapMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
    set udg_CoCr_MapMinY = GetRectMinY(bj_mapInitialPlayableArea)
endfunction
///////////////////////////////////////////////////////////////////
//  END OF THE SPELL                                             //
///////////////////////////////////////////////////////////////////
GIFCode
blood-boil-gif-gif.246775
JASS:
////////////////////////////////////////////////////////////////////
//                       BLOOD BOIL V1.01                         //
//  Author: Tank-Commander                                        //
//  Purpose: Ultimate buff ability which enhances other spells    //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the system    //
//                                                                //
//                                                                //
//  If you have used this spell in your map, you are required     //
//  to give credits to Tank-Commander for the creation of it      //
//  If you would like to use snippets of code from this for       //
//  whatever, getting permission and crediting the source/linking //
//  would be much appreciated.                                    //
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  README:                                                       //
//    Before modifying this spell a few things need to be         //
//    understood and read, this is one of those things, while     //
//    most modification can be considered intuitive, it still     //
//    helps to read through these intstructions, as they will     //
//    inform you about how to configure this spell to your        //
//    desire.                                                     //
//----------------------------------------------------------------//
//  Initial importing: The variable creator trigger can be        //
//  imported first and if you have the correct settings (file,    //
//  preferences, General, automatically create unknown variables  //
//  checked, then when you paste in the variable creator it       //
//  will automatically give you all the variables you need for    //
//  this spell                                                    //
//                                                                //
//  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   //
//  value are not the same as listed in the configuration, those  //
//  configurables will need to be changed to work correctly       //
//----------------------------------------------------------------//
//  TimerSpeed: This is the rate at which the loop timer of the   //
//  ability runs the default value 0.03125 allows the loop        //
//  function to run 32 times per second giving a smooth ability   //
constant function BlBo_TimerSpeed takes nothing returns real
    return 0.03125000
endfunction
//----------------------------------------------------------------//
//  Ability: This is the raw code for the ability used as a base  //
//  to see raw data press ctrl + d in the object editor, do it    //
//  again to revert to normal                                     //
constant function BlBo_Ability takes nothing returns integer
    return 'A006'
endfunction
//----------------------------------------------------------------//
//  BuffAbility: This is the raw code for the ability used to     //
//  apply the blood boil buff to the caster, it should be based   //
//  off slow aura                                                 //
constant function BlBo_BuffAbility takes nothing returns integer
    return 'A005'
endfunction
//----------------------------------------------------------------//
//  Duration: This is how long in seconds the ability lasts for   //
function BlBo_Duration takes real level returns real
    return 60.
endfunction
//----------------------------------------------------------------//
//  DamageTick: This is how many seconds there are between each   //
//  set of damage, this should evenly divide into the duration    //
function BlBo_DamageTick takes real level returns real
    return 1.
endfunction
//----------------------------------------------------------------//
//  HealthDamage: This is the total amount of health damage the   //
//  caster takes over the duration of the spell, set this to a    //
//  negative value to make it heal the caster instead             //
function BlBo_HealthDamage takes real level returns real
    return 600.
endfunction
//----------------------------------------------------------------//
//  ManaDamage: This is the total amount of mana damage the       //
//  caster takes over the duration of the spell, set this to a    //
//  negative value to make it heal the caster instead             //
function BlBo_ManaDamage takes real level returns real  
    return -60.
endfunction
//----------------------------------------------------------------//
//  DamageEffect: This is the effect played on the caster when    //
//  they take damage or are healed by the ability                 //
constant function BlBo_DamageEffect takes nothing returns string
    return "Abilities\\Weapons\\HydraliskImpact\\HydraliskImpact.mdl"
endfunction
//----------------------------------------------------------------//
//  DamageAttachmentPoint: This is the location on the hero that  //
//  the DamageEffect is placed onto                               //
constant function BlBo_DamageAttachmentPoint takes nothing returns string
    return "chest"
endfunction
//----------------------------------------------------------------//
//  HueRed: This is how red the hero goes when they take damage   //
constant function BlBo_HueRed takes nothing returns integer
    return 255
endfunction
//----------------------------------------------------------------//
//  HueBlue: This is how blue the hero goes when they take damage
constant function BlBo_HueBlue takes nothing returns integer
    return 120
endfunction
//----------------------------------------------------------------//
//  HueGreen: This is how green the hero goes when they take      //
//  damage                                                        //
constant function BlBo_HueGreen takes nothing returns integer
    return 120
endfunction
//----------------------------------------------------------------//
//  HueRecoverSpeed: This is how quickly the heroes colour is     //
//  restored to normal (255/255/255) this runs at a rate of       //
//  the TimerSpeed, so by default tihs runs 32 times per second   //
//  (e.g. if HueBlue is set to 50 and this to 4  then after 1     //
//  second the blue hue will now be 178                           //
constant function BlBo_HueRecoverSpeed takes nothing returns integer
    return 4
endfunction
//----------------------------------------------------------------//
//  UseHue: Set this to true to make the hero use the hue         //
//  colours when castig the spell, don't use if the hero has a    //
//  default colouration other than 255/255/255                    //
constant function BlBo_UseHue takes nothing returns boolean
    return true
endfunction
//----------------------------------------------------------------//
//                      END OF CONFIGURATION                      //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
//  Function used to check if a unit currently has any remaining //
//  blood boil buffs (this is relevent if the ability can be     //
//  casted multiple times by the same unit)                      //
///////////////////////////////////////////////////////////////////
function BlBo_IsNotBuffed takes unit u returns boolean
    local integer node = 0
  
    loop
        set node = udg_BlBo_NextNode[node]
        exitwhen node == 0
      
        if udg_BlBo_Unit[node] == u then
            return false
        endif
      
    endloop
  
    return true
endfunction

///////////////////////////////////////////////////////////////////
//  Function used to deal damage to units over time when they    //
//  have the buff, delays it to match standard ingame mechanics  //
///////////////////////////////////////////////////////////////////
function BlBo_Loop takes nothing returns nothing
    local integer node = 0
  
    loop
        set node = udg_BlBo_NextNode[node]
        exitwhen node == 0
      
        if udg_BlBo_Duration[node] >= 0 then
            set udg_BlBo_Duration[node] = udg_BlBo_Duration[node] - BlBo_TimerSpeed()
            set udg_BlBo_DamageTickCurrent[node] = udg_BlBo_DamageTickCurrent[node] - BlBo_TimerSpeed()
          
            if udg_BlBo_DamageTickCurrent[node] <= 0 then
                //Deal damage/heal
                set udg_BlBo_DamageTickCurrent[node] = udg_BlBo_DamageTick[node]
                call SetWidgetLife(udg_BlBo_Unit[node], GetWidgetLife(udg_BlBo_Unit[node]) - udg_BlBo_HealthDamage[node])
                call SetUnitState(udg_BlBo_Unit[node], UNIT_STATE_MANA, GetUnitState(udg_BlBo_Unit[node], UNIT_STATE_MANA) - udg_BlBo_ManaDamage[node])
                call DestroyEffect(AddSpecialEffectTarget(BlBo_DamageEffect(), udg_BlBo_Unit[node], BlBo_DamageAttachmentPoint()))
              
                //Reset Colours
                if BlBo_UseHue() then
                    set udg_BlBo_Red[node] = BlBo_HueRed()
                    set udg_BlBo_Blue[node] = BlBo_HueBlue()
                    set udg_BlBo_Green[node] = BlBo_HueGreen()
                endif
              
            //Apply Colour change
            elseif BlBo_UseHue() then
                set udg_BlBo_Red[node] = udg_BlBo_Red[node] + BlBo_HueRecoverSpeed()
                set udg_BlBo_Blue[node] = udg_BlBo_Blue[node] + BlBo_HueRecoverSpeed()
                set udg_BlBo_Green[node] = udg_BlBo_Green[node] + BlBo_HueRecoverSpeed()
            endif
          
            call SetUnitVertexColor(udg_BlBo_Unit[node], udg_BlBo_Red[node], udg_BlBo_Green[node], udg_BlBo_Blue[node], 255)
        else
      
            //Recycle
            set udg_BlBo_RecycleNodes[udg_BlBo_RecyclableNodes] = node
            set udg_BlBo_RecyclableNodes = udg_BlBo_RecyclableNodes + 1
            set udg_BlBo_NextNode[udg_BlBo_PrevNode[node]] = udg_BlBo_NextNode[node]
            set udg_BlBo_PrevNode[udg_BlBo_NextNode[node]] = udg_BlBo_PrevNode[node]

            if BlBo_IsNotBuffed(udg_BlBo_Unit[node]) then
                call UnitRemoveAbility(udg_BlBo_Unit[node], BlBo_BuffAbility())
              
                //Reset colours
                if BlBo_UseHue() then
                    call SetUnitVertexColor(udg_BlBo_Unit[node], 255, 255, 255, 255)
                endif
              
                //Pause timer if this was the last instance
                if (udg_BlBo_PrevNode[0] == 0) then
                    call PauseTimer(udg_BlBo_Timer)
                endif
              
            endif
        endif
      
    endloop
  
endfunction

///////////////////////////////////////////////////////////////////
//  Function used when the spell is cast to apply the buff and   //
//  set up unit data for the loop                                //
///////////////////////////////////////////////////////////////////
function BlBo_Cast takes nothing returns boolean
    local integer node
    local real level

    if (GetSpellAbilityId() == BlBo_Ability()) then
        //Set up Node
        if (udg_BlBo_RecyclableNodes == 0) then
            set udg_BlBo_NodeNumber = udg_BlBo_NodeNumber + 1
            set node = udg_BlBo_NodeNumber
        else
            set udg_BlBo_RecyclableNodes = udg_BlBo_RecyclableNodes - 1
            set node = udg_BlBo_RecycleNodes[udg_BlBo_RecyclableNodes]
        endif

        set udg_BlBo_NextNode[node] = 0
        set udg_BlBo_NextNode[udg_BlBo_PrevNode[0]] = node
        set udg_BlBo_PrevNode[node] = udg_BlBo_PrevNode[0]
        set udg_BlBo_PrevNode[0] = node
      
        //Start timer if this is the only instance
        if (udg_BlBo_PrevNode[node] == 0) then
            call TimerStart(udg_BlBo_Timer, BlBo_TimerSpeed(), true, function BlBo_Loop)
        endif
      
        //set up instance data
        set udg_BlBo_Unit[node] = GetTriggerUnit()
        set level = GetUnitAbilityLevel(udg_BlBo_Unit[node], BlBo_Ability())
        set udg_BlBo_Duration[node] = BlBo_Duration(level)
        set udg_BlBo_DamageTick[node] = BlBo_DamageTick(level)
        set udg_BlBo_DamageTickCurrent[node] = udg_BlBo_DamageTick[node]
        set udg_BlBo_HealthDamage[node] = BlBo_HealthDamage(level) / udg_BlBo_Duration[node]
        set udg_BlBo_ManaDamage[node] = BlBo_ManaDamage(level) / udg_BlBo_Duration[node]
        call UnitAddAbility(udg_BlBo_Unit[node], BlBo_BuffAbility())
        call SetUnitAbilityLevel(udg_BlBo_Unit[node], BlBo_BuffAbility(), R2I(level))
      
        if BlBo_UseHue() then
            set udg_BlBo_Red[node] = BlBo_HueRed()
            set udg_BlBo_Blue[node] = BlBo_HueBlue()
            set udg_BlBo_Green[node] = BlBo_HueGreen()
        endif
      
    endif
  
    return false
endfunction

///////////////////////////////////////////////////////////////////
//  Function used to initialise the event listeners of the spell //
///////////////////////////////////////////////////////////////////
function InitTrig_Blood_Boil takes nothing returns nothing
    local trigger t = CreateTrigger()

    //Set up trigger
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function BlBo_Cast))
endfunction
///////////////////////////////////////////////////////////////////
//  END OF THE SPELL                                             //
///////////////////////////////////////////////////////////////////

-=V1.00=-
- Initial Upload
-=V1.01=-
- Changed prefixes to be 4 characters rather than 2
- Changed all instances of "Node" to "node" for variable naming consistency
- Leech Life now uses 1 dummy rather than creating one each time a unit is stunned
- Added additional Attachment Point options for Corpse Crusher
- Rearranged the config for Rupturing Stab to put attachment points near effects
-=Null=-
- OrderID changes in 1.28 broke Leech Life, resolved this issue
Contents

Silithid Devourer Spellset (Map)

Reviews
KILLCIDE
Never have to worry about ugly code from you, Tank-Commander. Other than extremely clean and well-documented code, there was not anything gamebreaking. This spellpack is a great addition to our database. Needs Fixed Rupturing Stab Nothing Leech...
Level 37
Joined
Jul 22, 2015
Messages
3,485
Never have to worry about ugly code from you, Tank-Commander. Other than extremely clean and well-documented code, there was not anything gamebreaking. This spellpack is a great addition to our database.


Needs Fixed

  • Rupturing Stab
    • Nothing

  • Leech Life
    • Nothing

  • Corpse Crusher
    • Nothing

  • Blood Boil
    • Nothing

Suggestions

  • Rupturing Stab
    • RS is too generic as a prefix for variables and functions
    • I would rearrange the constant functions for effects so that the actual effect and their attachment points are side by side. At the moment, it's a little hard to distinguish which attachment point belongs to which effect without having to go back and forth
    • I would rename local integer Node to local integer node just for code consistency
    • Instead of looping through the instances to check if the unit is still bleeding, you can just check if they stil lhave the buff

  • Leech Life
    • LL is too generic as a prefix for variables and functions
    • I would rename local integer Node to local integer node just for code consistency
    • Instead of constantly creating a stun dummy and then removing it whenever the leeched unit needs to be stunned, just create one on initialization and move it around when you need to

  • Corpse Crusher
    • CC is too generic as a prefix for variables and functions
    • I would create a seperate attachment point for every different effect. Some effects look weird when they are on the origin, some look weird when in overhead, so it's better to have the option to change the attachment point if a user desires to change the effect. I think you got lucky with having them all look good on origin
    • Nothing wrong with function CC_2PI, but I thought it was just funny how you're literally squeezing in nanoseconds xD
    • You currently have 2 seperate loops just to register EVENT_PLAYER_UNIT_SPELL_EFFECT and EVENT_PLAYER_HERO_SKILL when you could have just made your own single loop and used the natives directly
    • In function CC_Update, you can use GetLearnedSkillLevel() over GetUnitAbilityLevel(u, CC_Ability())
    • I would rename local integer Node to local integer node just for code consistency
    • The black magic math you do in your loop is beyond me. I'm going to assume they are right (inb4 someone points out I'm wrong for approving this because something is wrong with the calculations)

  • Blood Boil
    • BB is too generic as a prefix for variables and functions
    • I would add extra constant functions that allow users to put in what the default RGB is for the hero or unit. Currently, you are just assuming they are 255 across the board
    • I would rename local integer Node to local integer node just for code consistency

Status

Approved
 
Level 1
Joined
Dec 26, 2016
Messages
2
good looking hero but i would have preferred if Blood Boil was based on immolation or mana shield so you could diactivate it instead increase the health cost, this way it's like sacrificing Health for Power whenever you want,just an idea though the hero is cool
 
Level 23
Joined
Jul 26, 2008
Messages
1,305
I always thought this spellpack was incredible. It was just difficult to integrate it into a story setting since the spells are tied to the hero model, and there are not many stories where the (main) hero can be a spider/silithid unless the plot is specifically focusing on those races--- unlike a more generic type of creature (humanoid creature) where the story would not necessarily have to accomodate the hero.
 
Top