////////////////////////////////////////////////////////////////////
// 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 //
///////////////////////////////////////////////////////////////////