function EnableScriptUsage takes nothing returns nothing
endfunction
Name | Type | is_array | initial_value |
BUS_Loc | location | No | |
BUS_MapMaxX | real | No | |
BUS_MapMaxY | real | No | |
BUS_MapMinX | real | No | |
BUS_MapMinY | real | No | |
BUS_Rect | rect | No | |
BUS_Stunner | unit | No | |
BUS_TreeChecker | unit | No | |
BUSK_CurrentEffect | effect | Yes | |
BUSK_CurrentHeight | real | Yes | |
BUSK_KnockbackX | real | Yes | |
BUSK_KnockbackY | real | Yes | |
BUSK_KnockbackZ | real | Yes | |
BUSK_NextNode | integer | Yes | |
BUSK_NodeNumber | integer | No | |
BUSK_PrevNode | integer | Yes | |
BUSK_RecyclableNodes | integer | No | |
BUSK_RecycleNodes | integer | Yes | |
BUSK_ReturnHeight | real | Yes | |
BUSK_Timer | timer | No | |
BUSK_TreeCounter | integer | No | |
BUSK_Unit | unit | Yes | |
BUSK_UnitType | boolean | Yes | |
MC_Angle | real | Yes | |
MC_AngleAdjust | real | Yes | |
MC_AngleLimit | real | Yes | |
MC_AOE | real | Yes | |
MC_CleaveScale | real | Yes | |
MC_Delay | real | Yes | |
MC_DelayReset | real | Yes | |
MC_Division | real | Yes | |
MC_EffectDistance | real | Yes | |
MC_HealthDamage | real | Yes | |
MC_KnockbackForce | real | Yes | |
MC_ManaDamage | real | Yes | |
MC_NextNode | integer | Yes | |
MC_NodeNumber | integer | No | |
MC_Player | player | Yes | |
MC_PrevNode | integer | Yes | |
MC_RecyclableNodes | integer | No | |
MC_RecycleNodes | integer | Yes | |
MC_TempGroup | group | No | |
MC_Timer | timer | No | |
MC_Unit | unit | Yes |
////////////////////////////////////////////////////////////////////
// Boss Ultimate Spellpack Knockback V1.01 //
// Author: Tank-Commander //
// Purpose: Handles physics simulation within the spellpack //
// Used for: Scathe, Sheer Force //
// //
// 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 spellpack 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 //
//----------------------------------------------------------------//
// CONFIGURABLE INFORMATION/HELP: //
// //
// - Viewing data values: To see data values in the editor you //
// need to press Ctrl + D, to shift back to normal viewing //
// press it again //
// //
// - Effects: Pathnames for effects used in the spells should //
// have two "\"s throughout or the effect will not work (the //
// WE progress bar will not go away when saving, however if //
// fixed afterwards the save will still work, but the progress //
// bar will still remain until the WE is closed) //
// e.g. "units\\human\\Footman\\Footman" //
// //
// - Removing Effects: to remove an effect you don't want from //
// the ability, set the model path to that of Dummy.mdl //
// //
// - Timer: Some configurables have PerSecond values, the code //
// automatically accounts for changes to the timer as to //
// maintain consistency with what the user has chosen //
// All times in the system are expressions of seconds //
// //
//----------------------------------------------------------------//
// TimerSpeed: This is the amount of time in seconds between //
// each iteration of the Knockback Loop function //
constant function BUSKR_TimerSpeed takes nothing returns real
return 0.031250000
endfunction
//----------------------------------------------------------------//
// TreeCheckerId: This is the raw data Id of the unit used for //
// locating and potentially killing trees, it is advised this //
// unit uses dummy.mdl for its model for optimal usage and must //
// be able to harvest trees //
constant function BUSKR_TreeCheckerId takes nothing returns integer
return 'u001'
endfunction
//----------------------------------------------------------------//
// StunOrderId: This is the order Id of the ability used to //
// stun units that have been knocked back, by default this is //
// the order ID for storm bolt (use the one in the object editor //
// which has been placed onto the TreeChecker dummy unit there) //
constant function BUSKR_StunOrderId takes nothing returns integer
return 852095
endfunction
//----------------------------------------------------------------//
// AttachmentPoint: This is the point on affected units that the //
// effects will be placed on while they are being knocked back //
constant function BUSKR_AttachmentPoint takes nothing returns string
return "origin"
endfunction
//----------------------------------------------------------------//
// HeightResetRate: This is the speed at which units return to //
// their original heights (both flying units and ground units) //
constant function BUSKR_HeightResetRate takes nothing returns real
return 18.5
endfunction
//----------------------------------------------------------------//
// ForceAirDecay: This is the ratio at which momentum decays //
// while a unit is in the Air (Current Force / ForceDecay ) //
constant function BUSKR_ForceAirDecay takes nothing returns real
return 1.10
endfunction
//----------------------------------------------------------------//
// ForceGroundDecay: This is the ratio at which momentum decays //
// while a unit is on the ground (Current Force / Force Decay) //
constant function BUSKR_ForceGroundDecay takes nothing returns real
return 2.
endfunction
//----------------------------------------------------------------//
// DragLet: This is a "let" value for when units will be reset //
// (if the units current total knockback is less than this value //
// it will be treated as its knockback value is 0, this also //
// applies to fly heights and should not be 0 or less) //
constant function BUSKR_DragLet takes nothing returns real
return 2.
endfunction
//----------------------------------------------------------------//
// Use3DKnockback: This only applies to ground units, the value //
// determines whether or not ground units which are hit by a //
// force will be knocked into the air, this includes "bouncing" //
// off the ground when hit by a force coming from above (this //
// only occurs if the unit is currently on the ground) //
constant function BUSKR_Use3DKnockback takes nothing returns boolean
return true
endfunction
//----------------------------------------------------------------//
// StunGround: This determines whether or not ground units hit //
// into the air will be stunned for that time (so they do not //
// alter the arc) //
constant function BUSKR_StunGround takes nothing returns boolean
return true
endfunction
//----------------------------------------------------------------//
// KnockbackEffect: This is the path of the effect that will be //
// attached to units that are currently being knocked back //
constant function BUSKR_KnockbackEffect takes nothing returns string
return "Abilities\\Spells\\Other\\Tornado\\Tornado_Target.mdl"
endfunction
//----------------------------------------------------------------//
// GroundedEffect: This is the path of the effect that will be //
// created when a flying unit is knocked into the ground (fly //
// height of 0) //
constant function BUSKR_GroundedEffect takes nothing returns string
return "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
endfunction
//----------------------------------------------------------------//
// InstantKillGround: This determines whether or not grounded //
// flyers will be instantly killed upon impact with the ground //
constant function BUSKR_InstantKillGround takes nothing returns boolean
return false
endfunction
//----------------------------------------------------------------//
// KillTrees; This determines whether or not trees will be //
// killed when a unit (on the ground) is pushed through them, if //
// set to false the system will attempt to prevent the unit from //
// moving into trees (but cannot be garunteed as 100% effective //
// due to knockback values potentially "jumping" the unit into //
// a clearing in the trees - TreeKillRadius can supplement this //
// issue //
constant function BUSKR_KillTrees takes nothing returns boolean
return false
endfunction
//----------------------------------------------------------------//
// TreeKillRadius: This determines the size of the area checked //
// when looking for trees to kill, but also functions as the //
// "let" radius for preventing units from being pushed through //
// trees, only applies to grounded units //
constant function BUSKR_TreeKillRadius takes nothing returns real
return 250.00
endfunction
//----------------------------------------------------------------//
// PreventUnpathable: This determines whether or not the system //
// will prevent units being knocked back into positions that //
// units cannot move in (it will assume ground units are not //
// able to move in deep water) //
constant function BUSKR_PreventUnpathable takes nothing returns boolean
return false
endfunction
//----------------------------------------------------------------//
// DummyPlayer: This is the player that is given ownership of //
// all the dummy units used by the system //
constant function BUSCR_DummyPlayer takes nothing returns player
return Player(14)
endfunction
//----------------------------------------------------------------//
// END OF CONFIGURATION //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// TreeDestroy: Function used to count trees and kill them if //
// the configuration has been set up to do so //
////////////////////////////////////////////////////////////////////
function BUS_TreeDestroy takes nothing returns nothing
local destructable Tree = GetEnumDestructable()
set udg_BUSK_TreeCounter = udg_BUSK_TreeCounter + 1
call IssueTargetOrderById(udg_BUS_TreeChecker, 852018, Tree)
if (GetUnitCurrentOrder(udg_BUS_TreeChecker) == 852018) and (BUSKR_KillTrees()) then
call KillDestructable(Tree)
endif
call IssueImmediateOrderById(udg_BUS_TreeChecker, 851972)
set Tree = null
endfunction
////////////////////////////////////////////////////////////////////
// GetTrees: Part of the system used to locate trees and pass //
// them on to the TreeDestroy function //
////////////////////////////////////////////////////////////////////
function BUS_GetTrees takes real x, real y returns nothing
//Move Rect and find trees
call MoveRectTo(udg_BUS_Rect, x, y)
set udg_BUSK_TreeCounter = 0
call MoveLocation(udg_BUS_Loc, x, y)
set bj_enumDestructableCenter = udg_BUS_Loc
set bj_enumDestructableRadius = BUSKR_TreeKillRadius()
call EnumDestructablesInRect(udg_BUS_Rect, filterEnumDestructablesInCircleBJ, function BUS_TreeDestroy)
endfunction
////////////////////////////////////////////////////////////////////
// GetZ: Used to get the Z height of a given location, used to //
// make sure that entities going over cliffs aren't incorrectly //
// moved/placed on the z plane //
////////////////////////////////////////////////////////////////////
function BUS_GetZ takes real x, real y returns real
call MoveLocation(udg_BUS_Loc, x, y)
return GetLocationZ(udg_BUS_Loc)
endfunction
////////////////////////////////////////////////////////////////////
// KnockbackLoop: function used to handle moving and changing //
// the heights knocked back units as well as slowing down their //
// speeds to 0 //
////////////////////////////////////////////////////////////////////
function BUS_KnockbackLoop takes nothing returns nothing
local integer Node = 0
local real x
local real x2
local real y
local real y2
local real TempReal
loop
set Node = udg_BUSK_NextNode[Node]
exitwhen Node == 0
set x2 = GetUnitX(udg_BUSK_Unit[Node])
set y2 = GetUnitY(udg_BUSK_Unit[Node])
set x = x2 + udg_BUSK_KnockbackX[Node]
set y = y2 + udg_BUSK_KnockbackY[Node]
set TempReal = BUS_GetZ(x, y)
if ((udg_BUS_MapMinX <= x) and (x <= udg_BUS_MapMaxX) and (udg_BUS_MapMinY <= y)and (y <= udg_BUS_MapMaxY)) then
if not((BUSKR_PreventUnpathable()) and not(IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY))) or not(udg_BUSK_UnitType[Node]) then
if (udg_BUSK_UnitType[Node] and udg_BUSK_CurrentHeight[Node] - TempReal <= 0) then
call BUS_GetTrees(x, y)
if (udg_BUSK_TreeCounter == 0) or (BUSKR_KillTrees()) then
call SetUnitX(udg_BUSK_Unit[Node], x)
call SetUnitY(udg_BUSK_Unit[Node], y)
endif
else
call SetUnitX(udg_BUSK_Unit[Node], x)
call SetUnitY(udg_BUSK_Unit[Node], y)
endif
endif
endif
if BUSKR_StunGround() then
call IssueTargetOrderById(udg_BUS_Stunner, BUSKR_StunOrderId(), udg_BUSK_Unit[Node])
endif
set udg_BUSK_CurrentHeight[Node] = udg_BUSK_CurrentHeight[Node] + udg_BUSK_KnockbackZ[Node] + (BUS_GetZ(x2, y2) - TempReal)
if ((udg_BUSK_CurrentHeight[Node] - TempReal) > 0) then
set udg_BUSK_KnockbackX[Node] = udg_BUSK_KnockbackX[Node] / BUSKR_ForceAirDecay()
set udg_BUSK_KnockbackY[Node] = udg_BUSK_KnockbackY[Node] / BUSKR_ForceAirDecay()
set udg_BUSK_KnockbackZ[Node] = udg_BUSK_KnockbackZ[Node] / BUSKR_ForceAirDecay()
else
set udg_BUSK_KnockbackX[Node] = udg_BUSK_KnockbackX[Node] / BUSKR_ForceGroundDecay()
set udg_BUSK_KnockbackY[Node] = udg_BUSK_KnockbackY[Node] / BUSKR_ForceGroundDecay()
set udg_BUSK_KnockbackZ[Node] = 0
if not(udg_BUSK_UnitType[Node]) then
call DestroyEffect(AddSpecialEffectTarget(BUSKR_GroundedEffect(), udg_BUSK_Unit[Node], BUSKR_AttachmentPoint()))
if (BUSKR_InstantKillGround()) then
call KillUnit(udg_BUSK_Unit[Node])
call DestroyEffect(udg_BUSK_CurrentEffect[Node])
set udg_BUSK_RecycleNodes[udg_BUSK_RecyclableNodes] = Node
set udg_BUSK_RecyclableNodes = udg_BUSK_RecyclableNodes + 1
set udg_BUSK_NextNode[udg_BUSK_PrevNode[Node]] = udg_BUSK_NextNode[Node]
set udg_BUSK_PrevNode[udg_BUSK_NextNode[Node]] = udg_BUSK_PrevNode[Node]
if (udg_BUSK_PrevNode[0] == 0) then
call PauseTimer(udg_BUSK_Timer)
endif
endif
endif
endif
call SetUnitFlyHeight(udg_BUSK_Unit[Node], udg_BUSK_CurrentHeight[Node] - TempReal, 0.00)
if (udg_BUSK_UnitType[Node]) then
if(udg_BUSK_KnockbackX[Node] + udg_BUSK_KnockbackY[Node] + udg_BUSK_KnockbackZ[Node] < BUSKR_DragLet()) and (udg_BUSK_CurrentHeight[Node] - TempReal <= 0) then
call SetUnitFlyHeight(udg_BUSK_Unit[Node], 0, 0.00)
call DestroyEffect(udg_BUSK_CurrentEffect[Node])
set udg_BUSK_RecycleNodes[udg_BUSK_RecyclableNodes] = Node
set udg_BUSK_RecyclableNodes = udg_BUSK_RecyclableNodes + 1
set udg_BUSK_NextNode[udg_BUSK_PrevNode[Node]] = udg_BUSK_NextNode[Node]
set udg_BUSK_PrevNode[udg_BUSK_NextNode[Node]] = udg_BUSK_PrevNode[Node]
if (udg_BUSK_PrevNode[0] == 0) then
call PauseTimer(udg_BUSK_Timer)
endif
endif
set udg_BUSK_CurrentHeight[Node] = udg_BUSK_CurrentHeight[Node] - BUSKR_HeightResetRate()
else
set TempReal = udg_BUSK_ReturnHeight[Node] - (udg_BUSK_CurrentHeight[Node] - TempReal)
if (TempReal * TempReal <= BUSKR_HeightResetRate() * BUSKR_HeightResetRate()) and (udg_BUSK_KnockbackZ[Node] < BUSKR_DragLet()) then
call SetUnitFlyHeight(udg_BUSK_Unit[Node], udg_BUSK_ReturnHeight[Node], 0.00)
call DestroyEffect(udg_BUSK_CurrentEffect[Node])
if (udg_BUSK_KnockbackX[Node] + udg_BUSK_KnockbackY[Node] + udg_BUSK_KnockbackZ[Node] < BUSKR_DragLet()) then
set udg_BUSK_RecycleNodes[udg_BUSK_RecyclableNodes] = Node
set udg_BUSK_RecyclableNodes = udg_BUSK_RecyclableNodes + 1
set udg_BUSK_NextNode[udg_BUSK_PrevNode[Node]] = udg_BUSK_NextNode[Node]
set udg_BUSK_PrevNode[udg_BUSK_NextNode[Node]] = udg_BUSK_PrevNode[Node]
if (udg_BUSK_PrevNode[0] == 0) then
call PauseTimer(udg_BUSK_Timer)
endif
endif
elseif (udg_BUSK_ReturnHeight[Node] > (udg_BUSK_CurrentHeight[Node] - TempReal)) then
set udg_BUSK_CurrentHeight[Node] = udg_BUSK_CurrentHeight[Node] + BUSKR_HeightResetRate()
else
set udg_BUSK_CurrentHeight[Node] = udg_BUSK_CurrentHeight[Node] - BUSKR_HeightResetRate()
endif
endif
endloop
endfunction
////////////////////////////////////////////////////////////////////
// StartKnockback: Function used to start a unit's knockback //
// and adds it into the loop to be knocked back, it also //
// prevents a unit having two iterations within the loop //
////////////////////////////////////////////////////////////////////
function BUS_StartKnockback takes unit u, real Force, real x, real x2, real y, real y2, real z, real z2 returns nothing
local boolean TempBoolean = true
local integer Node = 0
local real dx
local real dy
local real Angle
local real Angle2
loop
set Node = udg_BUSK_NextNode[Node]
if (udg_BUSK_Unit[Node] == u) then
set TempBoolean = false
endif
exitwhen (Node == 0) or (udg_BUSK_Unit[Node] == u)
endloop
if (TempBoolean) then
if (udg_BUSK_RecyclableNodes == 0) then
set udg_BUSK_NodeNumber = udg_BUSK_NodeNumber + 1
set Node = udg_BUSK_NodeNumber
else
set udg_BUSK_RecyclableNodes = udg_BUSK_RecyclableNodes - 1
set Node = udg_BUSK_RecycleNodes[udg_BUSK_RecyclableNodes]
endif
set udg_BUSK_NextNode[Node] = 0
set udg_BUSK_NextNode[udg_BUSK_PrevNode[0]] = Node
set udg_BUSK_PrevNode[Node] = udg_BUSK_PrevNode[0]
set udg_BUSK_PrevNode[0] = Node
set udg_BUSK_Unit[Node] = u
set udg_BUSK_ReturnHeight[Node] = GetUnitFlyHeight(u)
set udg_BUSK_CurrentHeight[Node] = udg_BUSK_ReturnHeight[Node] + BUS_GetZ(x, y)
set udg_BUSK_CurrentEffect[Node] = AddSpecialEffectTarget(BUSKR_KnockbackEffect(), u, BUSKR_AttachmentPoint())
set udg_BUSK_UnitType[Node] = IsUnitType(u, UNIT_TYPE_GROUND)
set udg_BUSK_KnockbackX[Node] = 0.
set udg_BUSK_KnockbackY[Node] = 0.
set udg_BUSK_KnockbackZ[Node] = 0.
if UnitAddAbility(udg_BUSK_Unit[Node], 'Amrf') and UnitRemoveAbility(udg_BUSK_Unit[Node], 'Amrf') then
endif
if (udg_BUSK_PrevNode[Node] == 0) then
call TimerStart(udg_BUSK_Timer, BUSKR_TimerSpeed(), true, function BUS_KnockbackLoop)
endif
endif
set dy = y - y2
set dx = x - x2
if (udg_BUSK_UnitType[Node]) and (udg_BUSK_CurrentHeight[Node] - BUS_GetZ(x, y) <= BUSKR_DragLet()) then
set Angle2 = Atan2((z2 - z), SquareRoot((dy * dy) + (dx * dx)))
if(BUSKR_Use3DKnockback()) then
if ((z2 - z) < 0) then
set Angle2 = Atan2((z - z2), SquareRoot((dy * dy) + (dx * dx)))
endif
set udg_BUSK_KnockbackZ[Node] = udg_BUSK_KnockbackZ[Node] + Sin(Angle2) * Force
endif
else
set Angle2 = Atan2((z - z2), SquareRoot((dy * dy) + (dx * dx)))
set udg_BUSK_KnockbackZ[Node] = udg_BUSK_KnockbackZ[Node] + Sin(Angle2) * Force
endif
set Angle = Atan2(dy, dx)
set udg_BUSK_KnockbackX[Node] = udg_BUSK_KnockbackX[Node] + Cos(Angle) * Cos(Angle2) * Force
set udg_BUSK_KnockbackY[Node] = udg_BUSK_KnockbackY[Node] + Sin(Angle) * Cos(Angle2) * Force
endfunction
////////////////////////////////////////////////////////////////////
// InitTrig BUS Knockback: function used to set up the timer //
// used by the system and the tree checking unit //
////////////////////////////////////////////////////////////////////
function InitTrig_BUS_Knockback takes nothing returns nothing
set udg_BUSK_Timer = CreateTimer()
set udg_BUS_TreeChecker = CreateUnit(BUSCR_DummyPlayer(), BUSKR_TreeCheckerId(), 0., 0. , 0.)
set udg_BUS_Stunner = CreateUnit(BUSCR_DummyPlayer(), BUSKR_TreeCheckerId(), 0., 0., 0.)
set udg_BUS_Rect = Rect(0, 0, BUSKR_TreeKillRadius(), BUSKR_TreeKillRadius())
set udg_BUS_Loc = Location(0,0)
set udg_BUS_MapMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
set udg_BUS_MapMinX = GetRectMinX(bj_mapInitialPlayableArea)
set udg_BUS_MapMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
set udg_BUS_MapMinY = GetRectMinY(bj_mapInitialPlayableArea)
endfunction
////////////////////////////////////////////////////////////////////
// End of the system //
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// 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 //
////////////////////////////////////////////////////////////////////