////////////////////////////////////////////////////////////////////
// MASSIVE CLEAVE V1.01 //
// Author: Tank-Commander //
// Purpose: A powerful keepaway ability for scaring opposition //
// //
// Requires: //
// - (optional) BUS_Knockback by Tank-Commander //
// remove lines outlined by "******" in code if not used //
// //
// Notes: //
// - Read the readme before you try modifying the config //
// - Use the "Helpful files" to help you import the system //
// //
// Credits: //
// - (Dummy.mdl) Vexorian //
// //
// If you have used this spell in your map, you are required //
// to give credits to Tank-Commander for the creation of it //
// If you would like to use snippets of code from this for //
// whatever, getting permission and crediting the source/linking //
// would be much appreciated. //
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// README: //
// Before modifying this spell a few things need to be //
// understood and read, this is one of those things, while //
// most modification can be considered intuitive, it still //
// helps to read through these intstructions, as they will //
// inform you about how to configure this spell to your //
// desire. //
//----------------------------------------------------------------//
// Initial importing: The variable creator trigger can be //
// imported first and if you have the correct settings (file, //
// preferences, General, automatically create unknown variables //
// checked, then when you paste in the variable creator it //
// will automatically give you all the variables you need for //
// this spell //
// //
// 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 //
//----------------------------------------------------------------//
// MISC //
//----------------------------------------------------------------//
// TimerSpeed: the time in seconds between each iteration of //
// the main loop function (default is 0.031250000) it's //
// recommended you leave it like that //
constant function MC_TimerSpeed takes nothing returns real
return 0.031250000
endfunction
//----------------------------------------------------------------//
// Ability: This is the data value of the ability used as the //
// base, make sure it is based off channel (to view raw data //
// press ctrl + d, pressing again switches back) //
constant function MC_Ability takes nothing returns integer
return 'A000'
endfunction
//----------------------------------------------------------------//
// DummyID: This is the data value of the unit to be used as the //
// dummy for blood effects generated by the cleave //
constant function MC_DummyID takes nothing returns integer
return 'u000'
endfunction
//----------------------------------------------------------------//
// OrderID: This is the order ID used by the ability to tell if //
// the unit is still channelling the ability and cancelling it //
// if it stops (852474 is default for 1.28) //
constant function MC_OrderID takes nothing returns integer
return 852473
endfunction
//----------------------------------------------------------------//
// Attachmentpoint: This is the location on the Dummy unit that //
// blood effects will be placed on //
constant function MC_AttachmentPoint takes nothing returns string
return "origin"
endfunction
//----------------------------------------------------------------//
// Clockwise: Determines what direction the cleave will take //
// (set it to match the handedness of the unit(s) using the //
// ability, righthanded: anticlockwise, lefthanded: clockwise //
constant function MC_Clockwise takes nothing returns boolean
return false
endfunction
//----------------------------------------------------------------//
// Timer: How long in seconds dummy units are alive for //
constant function MC_Timer takes nothing returns real
return 3.
endfunction
//----------------------------------------------------------------//
// DummyPlayer: The owning player of the dummy units //
constant function MC_DummyPlayer takes nothing returns player
return Player(14)
endfunction
//----------------------------------------------------------------//
// AttackType: The attack type used when dealing damage //
constant function MC_AttackType takes nothing returns attacktype
return ATTACK_TYPE_MAGIC
endfunction
//----------------------------------------------------------------//
// DamageType: The damage type used when dealing damage //
constant function MC_DamageType takes nothing returns damagetype
return DAMAGE_TYPE_MAGIC
endfunction
//----------------------------------------------------------------//
// MAIN ABILITY //
//----------------------------------------------------------------//
// AOE: The Area around the caster that the cleave reaches //
function MC_AOE takes real level returns real
return 250 + (25 * level)
endfunction
//----------------------------------------------------------------//
// EffectDistance: How far away from the caster blood effects //
// are created
function MC_EffectDistance takes real level returns real
return 150 + (25 * level)
endfunction
//----------------------------------------------------------------//
// Angle: The angle covered by the cleave (radians) bj_PI = 90 //
// degrees, it will eb centered around the unit facing angle //
function MC_Angle takes real level returns real
return 1.7 + (0.26 * level)
endfunction
//----------------------------------------------------------------//
// Divisions: This is the amount of segments in your cleave //
// it's recommended to have at least 3 for all levels //
function MC_Divisions takes real level returns real
return 2 + (1 * level)
endfunction
//----------------------------------------------------------------//
// HealthDamage: The amount of damage your cleave deals to //
// the health of units hit //
function MC_HealthDamage takes real level returns real
return 80 + (20 * level)
endfunction
//----------------------------------------------------------------//
// ManaDamage: The amount of damage your cleave deals to the //
// mana of units hit //
function MC_ManaDamage takes real level returns real
return 0.
endfunction
//----------------------------------------------------------------//
// InitialDelay: The time after starting the ability that the //
// first cleave segment is used and units are hit in seconds //
function MC_InitialDelay takes real level returns real
return .20 + (-0.01 * level)
endfunction
//----------------------------------------------------------------//
// DivisionDelay: The time between each cleave segment being //
// used and units getting hit in seconds //
function MC_DivisionDelay takes real level returns real
return 0.04 + (-0.01 * level)
endfunction
//----------------------------------------------------------------//
// KnockbackForce: The force at which units are hit by the //
// cleave (only relevent to knockback and not damage) //
function MC_KnockbackForce takes real level returns real
return 50 + (10 * level)
endfunction
//----------------------------------------------------------------//
// KnockbackCleaveAngle: This is the angle taken from straight //
// forward that units are knocked back in (positive values //
// lead to the unit getting pushed in the direction of the //
// cleaver) //
constant function MC_KnockbackCleaveAngle takes nothing returns real
return bj_PI / 3
endfunction
//----------------------------------------------------------------//
// PitchCalcVal: The hit height of the cleaver on target units //
// (used to work out their angle when launched, higher values //
// result in more steep arcs but may need more KnockbackForce //
// to be effective) //
constant function MC_PitchCalcVal takes nothing returns real
return 200.
endfunction
//----------------------------------------------------------------//
// CleaveHitEffect: The effect created when the cleaver hits //
// a unit successfully //
constant function MC_CleaveHitEffect takes nothing returns string
return "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl"
endfunction
//----------------------------------------------------------------//
// CleaveHitScale: The size of the effect created when the //
// cleave hits a unit successfully (1 = 100% model size) //
constant function MC_CleaveHitScale takes real level returns real
return 1 + (0.2 * level)
endfunction
//----------------------------------------------------------------//
// FilterMaxZ: The Highest fly height a unit can have while //
// still being considered a valid target //
constant function MC_FilterMaxZ takes nothing returns real
return 0.01
endfunction
//----------------------------------------------------------------//
// CleaveAngleLet: Added to angles to compensate for floating //
// point arithmetic errors (read-only) //
constant function MC_CleaveAngleLet takes nothing returns real
return 0.01
endfunction
//----------------------------------------------------------------//
// AngleCorrection: Used to convert negative angles into //
// positive ones (read-only) //
constant function MC_AngleCorrection takes nothing returns real
return bj_PI * 2
endfunction
//----------------------------------------------------------------//
// END OF CONFIGURATION //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Target filter function used to differentiate between units //
// that can be hit by the spell and those that cannot //
////////////////////////////////////////////////////////////////////
function MC_TargetFilter takes unit u, player pl returns boolean
return (not IsUnitType(u, UNIT_TYPE_STRUCTURE)) and (GetUnitFlyHeight(u) <= MC_FilterMaxZ()) and (not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)) and (IsUnitEnemy(u, pl)) and (GetUnitTypeId(u) != MC_DummyID()) and not(IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0)
endfunction
////////////////////////////////////////////////////////////////////
// Check cleave function used ot prevent two channels being done //
// by the same unit at the same time //
////////////////////////////////////////////////////////////////////
function MC_CheckCleave takes unit u returns nothing
local integer Node = udg_MC_NextNode[0]
loop
exitwhen (Node == 0) or (udg_MC_Unit[Node] == u)
set Node = udg_MC_NextNode[Node]
endloop
set udg_MC_Delay[Node] = 0.
set udg_MC_Angle[Node] = udg_MC_AngleLimit[Node]
endfunction
////////////////////////////////////////////////////////////////////
// AbsAngle function used to convert negative angles (rads) into //
// positive angles (rads), standardises angles so formulas can //
// be used effectively //
////////////////////////////////////////////////////////////////////
function MC_AbsAngle takes real angle returns real
if angle < 0. then
return angle + MC_AngleCorrection()
else
return angle
endif
endfunction
////////////////////////////////////////////////////////////////////
// Validate Angle function used to check if a given unit is //
// within the boundaries of the current cleave segment //
////////////////////////////////////////////////////////////////////
function MC_ValidateAngle takes real boundary, real boundary2, real angle returns boolean
//Check what direction the cleave is going in
//And then check if the angle is within the two boundaries
if MC_Clockwise() then
if boundary <= 0 and angle < boundary2 then
set angle = angle - MC_AngleCorrection()
endif
return (boundary - angle >= -MC_CleaveAngleLet() and boundary2 - angle <= MC_CleaveAngleLet())
else
if boundary2 >= MC_AngleCorrection() and angle < boundary then
set angle = angle + MC_AngleCorrection()
endif
return (boundary2 - angle >= -MC_CleaveAngleLet() and boundary - angle <= MC_CleaveAngleLet())
endif
return false
endfunction
////////////////////////////////////////////////////////////////////
// Loop function used to control the cleave, damaging units //
// knocking them in the air and creating effects //
////////////////////////////////////////////////////////////////////
function MC_MassiveCleaveLoop takes nothing returns nothing
//Set up Locals
local integer Node = udg_MC_NextNode[0]
local real angle
local real angle2
local real tempReal
local real x
local real y
local real x2
local real y2
local real dy
local real dx
local boolean bool
local unit u
//Cycle through each node
loop
exitwhen Node == 0
//Check if it's time to create another cleave point
if udg_MC_Delay[Node] < MC_TimerSpeed() then
//Check if the cleave is finished
if (udg_MC_Angle[Node] >= (udg_MC_AngleLimit[Node] - MC_CleaveAngleLet()) and not(MC_Clockwise())) or ((udg_MC_Angle[Node] <= (udg_MC_AngleLimit[Node] + MC_CleaveAngleLet())) and MC_Clockwise()) then
set udg_MC_RecycleNodes[udg_MC_RecyclableNodes] = Node
set udg_MC_RecyclableNodes = udg_MC_RecyclableNodes + 1
set udg_MC_NextNode[udg_MC_PrevNode[Node]] = udg_MC_NextNode[Node]
set udg_MC_PrevNode[udg_MC_NextNode[Node]] = udg_MC_PrevNode[Node]
//Pause timer if this was the last instance
if (udg_MC_PrevNode[0] == 0) then
call PauseTimer(udg_MC_Timer)
endif
else
set x = GetUnitX(udg_MC_Unit[Node])
set y = GetUnitY(udg_MC_Unit[Node])
set bool = false
set udg_MC_Delay[Node] = udg_MC_DelayReset[Node]
set angle = udg_MC_Angle[Node] + udg_MC_Division[Node]
//Find units to cleave
call GroupEnumUnitsInRange(udg_MC_TempGroup, x, y, udg_MC_AOE[Node], null)
loop
set u = FirstOfGroup(udg_MC_TempGroup)
exitwhen u == null
//Valid target type
if MC_TargetFilter(u, udg_MC_Player[Node]) then
set x2 = GetUnitX(u)
set y2 = GetUnitY(u)
set dy = y2 - y
set dx = x2 - x
set angle2 = Atan2(dy, dx)
if angle2 < 0. then
set angle2 = angle2 + MC_AngleCorrection()
endif
//Within the correct arc
if (MC_ValidateAngle(udg_MC_Angle[Node], angle, angle2)) then
set bool = true
//Damage unit and setup knockback data
call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) - udg_MC_ManaDamage[Node])
call UnitDamageTarget(udg_MC_Unit[Node], u, udg_MC_HealthDamage[Node], true, false, MC_AttackType(), MC_DamageType(), null)
//*********************************************
//Knockback (delete if not using BUS_Knockback)
if MC_Clockwise() then
set angle2 = angle2 - MC_KnockbackCleaveAngle()
else
set angle2 = angle2 + MC_KnockbackCleaveAngle()
endif
set tempReal = SquareRoot(dx * dx + dy * dy)
call BUS_StartKnockback(u, udg_MC_KnockbackForce[Node], x2 + tempReal * Cos(angle2), x2, y2 + tempReal * Sin(angle2), y2, 0., MC_PitchCalcVal())
//*********************************************
endif
endif
call GroupRemoveUnit(udg_MC_TempGroup, u)
endloop
//Create blood effect if a unit was hit
if bool then
//Find position
set angle2 = udg_MC_Angle[Node] + udg_MC_AngleAdjust[Node]
//Create effect
set u = CreateUnit(MC_DummyPlayer(), MC_DummyID(), x + udg_MC_EffectDistance[Node] * Cos(angle2), y + udg_MC_EffectDistance[Node] * Sin(angle2), angle2 * bj_RADTODEG)
call SetUnitScale(u, udg_MC_CleaveScale[Node], 0., 0.)
call DestroyEffect(AddSpecialEffectTarget(MC_CleaveHitEffect(), u, MC_AttachmentPoint()))
call UnitApplyTimedLife(u, 'BHwe', MC_Timer())
set u = null
endif
//Move Position
set udg_MC_Angle[Node] = angle
endif
else
set udg_MC_Delay[Node] = udg_MC_Delay[Node] - MC_TimerSpeed()
if not(GetUnitCurrentOrder(udg_MC_Unit[Node]) == MC_OrderID()) then
set udg_MC_Delay[Node] = 0.
set udg_MC_Angle[Node] = udg_MC_AngleLimit[Node]
endif
endif
set Node = udg_MC_NextNode[Node]
endloop
endfunction
////////////////////////////////////////////////////////////////////
// Function used to start the channel process when the abiliyt //
// has been cast //
////////////////////////////////////////////////////////////////////
function MC_StartCleave takes nothing returns boolean
//Set up locals
local integer Node
local real level
local real offset
local real facing
local unit u
//Start the channel
if (GetSpellAbilityId() == MC_Ability()) then
//Get unit and check for multiple channels
set u = GetTriggerUnit()
call MC_CheckCleave(u)
//Set up Node
if (udg_MC_RecyclableNodes == 0) then
set udg_MC_NodeNumber = udg_MC_NodeNumber + 1
set Node = udg_MC_NodeNumber
else
set udg_MC_RecyclableNodes = udg_MC_RecyclableNodes - 1
set Node = udg_MC_RecycleNodes[udg_MC_RecyclableNodes]
endif
set udg_MC_NextNode[Node] = 0
set udg_MC_NextNode[udg_MC_PrevNode[0]] = Node
set udg_MC_PrevNode[Node] = udg_MC_PrevNode[0]
set udg_MC_PrevNode[0] = Node
//Start timer if this is the only instance
if (udg_MC_PrevNode[Node] == 0) then
call TimerStart(udg_MC_Timer, MC_TimerSpeed(), true, function MC_MassiveCleaveLoop)
endif
//Set up cleave data
set udg_MC_Unit[Node] = u
set udg_MC_Player[Node] = GetTriggerPlayer()
set level = GetUnitAbilityLevel(udg_MC_Unit[Node], MC_Ability())
set facing = GetUnitFacing(udg_MC_Unit[Node]) * bj_DEGTORAD
set offset = MC_Angle(level)
if MC_Clockwise() then
set udg_MC_Division[Node] = offset / -MC_Divisions(level)
set offset = offset / 2
set udg_MC_AngleLimit[Node] = MC_AbsAngle(facing + offset)
set udg_MC_Angle[Node] = udg_MC_AngleLimit[Node] - offset * 2
else
set udg_MC_Division[Node] = offset / MC_Divisions(level)
set offset = offset / -2
set udg_MC_Angle[Node] = MC_AbsAngle(facing + offset)
set udg_MC_AngleLimit[Node] = udg_MC_Angle[Node] - offset * 2
endif
set udg_MC_AOE[Node] = MC_AOE(level)
set udg_MC_EffectDistance[Node] = MC_EffectDistance(level)
set udg_MC_AngleAdjust[Node] = (udg_MC_Division[Node] / 2)
set udg_MC_HealthDamage[Node] = MC_HealthDamage(level)
set udg_MC_ManaDamage[Node] = MC_ManaDamage(level)
set udg_MC_Delay[Node] = MC_InitialDelay(level)
set udg_MC_DelayReset[Node] = MC_DivisionDelay(level)
set udg_MC_KnockbackForce[Node] = MC_KnockbackForce(level)
set udg_MC_CleaveScale[Node] = MC_CleaveHitScale(level)
endif
return false
endfunction
////////////////////////////////////////////////////////////////////
// Function used to register the trigger of the ability //
////////////////////////////////////////////////////////////////////
function InitTrig_Massive_Cleave takes nothing returns nothing
//Set up local variables
local trigger t = CreateTrigger()
//Set up trigger
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function MC_StartCleave))
endfunction
////////////////////////////////////////////////////////////////////
//END OF THE SPELL //
////////////////////////////////////////////////////////////////////