function EnableScriptUsage takes nothing returns nothing
endfunction
Name | Type | is_array | initial_value |
Attack | integer | Yes | |
EFB_Ability | abilcode | No | |
EFB_AbilityLevel | integer | Yes | |
EFB_Ammo | integer | Yes | |
EFB_Angle | real | No | |
EFB_ExplosionCount | integer | Yes | |
EFB_Int | integer | No | |
EFB_LaunchHeight | real | No | |
EFB_LaunchPower | real | Yes | |
EFB_Loop | integer | No | |
EFB_Loop2 | integer | No | |
EFB_Pitch | real | Yes | |
EFB_Real | real | No | |
EFB_Real2 | real | No | |
EFB_RingCount | integer | Yes | |
EFB_RingIncrement | real | Yes | |
EFB_SlaveCount | integer | Yes | |
EFB_u | unit | No | |
EFB_Unit | unit | Yes | |
EFB_X | real | No | |
EFB_X2 | real | No | |
EFB_Y | real | No | |
EFB_Y2 | real | No | |
MPS_ACTIVE_EVENT | real | No | |
MPS_ACTIVE_NODE | integer | No | |
MPS_AmmoNumber | integer | No | |
MPS_AmmoType | integer | Yes | |
MPS_ATTACK_USED | real | No | |
MPS_AttackMasterAmmo | integer | Yes | |
MPS_AttackName | string | Yes | |
MPS_AttackNumber | integer | No | |
MPS_AttackSalvo | integer | Yes | |
MPS_AttackSlaveAmmo | integer | Yes | |
MPS_Loc | location | No | |
MPS_MapMaxX | real | No | |
MPS_MapMaxY | real | No | |
MPS_MapMinX | real | No | |
MPS_MapMinY | real | No | |
MPS_MASTER_CRASH | real | No | |
MPS_MASTER_CREATE | real | No | |
MPS_MASTER_HIT | real | No | |
MPS_MASTER_UPDATE | real | No | |
MPS_MasterNode | integer | Yes | |
MPS_NO_EVENT | real | No | |
MPS_Projectile | unit | Yes | |
MPS_ProjectileAcceleration | real | Yes | |
MPS_ProjectileAnimation | integer | Yes | |
MPS_ProjectileAOE | real | Yes | |
MPS_ProjectileAttackType | attacktype | Yes | |
MPS_ProjectileCurrentEffect | effect | Yes | |
MPS_ProjectileDamageType | damagetype | Yes | |
MPS_ProjectileDeathModel | string | Yes | |
MPS_ProjectileHealthDamage | real | Yes | |
MPS_ProjectileHeight | real | Yes | |
MPS_ProjectileLauncher | unit | Yes | |
MPS_ProjectileManaDamage | real | Yes | |
MPS_ProjectileModel | string | Yes | |
MPS_ProjectileModelScale | real | Yes | |
MPS_ProjectileName | string | Yes | |
MPS_ProjectileNextNode | integer | Yes | |
MPS_ProjectileNodeNumber | integer | No | |
MPS_ProjectilePlayer | player | Yes | |
MPS_ProjectilePrevNode | integer | Yes | |
MPS_ProjectileRecyclableNodes | integer | No | |
MPS_ProjectileRecycleNodes | integer | Yes | |
MPS_ProjectileRemoveTimer | real | Yes | |
MPS_ProjectileStageID | integer | Yes | |
MPS_ProjectileTargetAimOffset | real | Yes | |
MPS_ProjectileTargetUnit | unit | Yes | |
MPS_ProjectileTimer | timer | No | |
MPS_ProjectileTurnEfficiency | real | Yes | |
MPS_ProjectileTurnRate | real | Yes | |
MPS_ProjectileX | real | Yes | |
MPS_ProjectileY | real | Yes | |
MPS_SALVO_FIRED | real | No | |
MPS_SalvoAngles | real | Yes | |
MPS_SalvoHeightOffset | real | Yes | |
MPS_SalvoMasterAmmoAmount | integer | Yes | |
MPS_SalvoName | string | Yes | |
MPS_SalvoNumber | integer | No | |
MPS_SalvoPower | real | Yes | |
MPS_SalvoSlaveAmmoAmount | integer | Yes | |
MPS_Slave | unit | Yes | |
MPS_SLAVE_CREATE | real | No | |
MPS_SLAVE_DEATH | real | No | |
MPS_SLAVE_UPDATE | real | No | |
MPS_SlaveAngle | real | Yes | |
MPS_SlaveAOE | real | Yes | |
MPS_SlaveAttackType | attacktype | Yes | |
MPS_SlaveCurrentEffect | effect | Yes | |
MPS_SlaveCurrentOffset | real | Yes | |
MPS_SlaveDamageType | damagetype | Yes | |
MPS_SlaveDeathModel | string | Yes | |
MPS_SlaveHealthDamage | real | Yes | |
MPS_SlaveManaDamage | real | Yes | |
MPS_SlaveModel | string | Yes | |
MPS_SlaveModelScale | real | Yes | |
MPS_SlaveName | string | Yes | |
MPS_SlaveNextNode | integer | Yes | |
MPS_SlaveNodeNumber | integer | No | |
MPS_SlaveNumber | integer | No | |
MPS_SlaveOffset | real | Yes | |
MPS_SlavePrevNode | integer | Yes | |
MPS_SlavePull | real | Yes | |
MPS_SlaveRecyclableNodes | integer | No | |
MPS_SlaveRecycleNodes | integer | Yes | |
MPS_SlaveSpin | real | Yes | |
MPS_SlaveType | integer | Yes | |
MPS_SlaveVel | real | Yes | |
MPS_TargetX | real | Yes | |
MPS_TargetY | real | Yes | |
MPS_TargetZ | real | Yes | |
MPS_TempGroup | group | No | |
MPS_XVel | real | Yes | |
MPS_YVel | real | Yes | |
MPS_ZVel | real | Yes |
////////////////////////////////////////////////////////////////////
// MASTER PROJECTILE SYSTEM V1.02b //
// Author: Tank-Commander //
// Requires: Dummy.mdl //
// Purpose: Create complex and attractive projectiles //
// //
// Notes: //
// - Read the readme before you try modifying the config //
// - Use the "Helpful files" to help you import the spell //
// //
// Credits: //
// - (Dummy.mdl) Vexorian //
// //
// If you have used this system 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 system a few things need to be //
// understood and read, this is one of those things, while //
// most modification can be considered intuitive, it still //
// helps to read through these instructions, as they will //
// inform you about how to configure this system to your //
// desire. //
//----------------------------------------------------------------//
constant function MPS_ProjectileTimerSpeed takes nothing returns real
return 0.031250000
endfunction
//----------------------------------------------------------------//
// DummyID: This is the data value of the unit that serves as //
// the dummy, it should have Dummy.mdl set to its model have //
// locust as its ability, movement type float (or fly) and 0 //
// pitch and roll angle for optimal use //
constant function MPS_ProjectileDummyID takes nothing returns integer
return 'u000'
endfunction
//----------------------------------------------------------------//
// AttachmentPoint: This is the location on the Dummy unit that //
// Master and Slave projectiles will have their effects placed //
constant function MPS_ProjectileAttachmentPoint takes nothing returns string
return "origin"
endfunction
//----------------------------------------------------------------//
// DummyPlayer: This is the player who will own all dummy units //
// created by this ability, by default this is Player(14) //
constant function MPS_DummyPlayer takes nothing returns player
return Player(14)
endfunction
//----------------------------------------------------------------//
// HeightLet: This is how far off the ground a projectile can be //
// while still being considered to be in the ground (can prevent //
// odd changes in arc when hitting the ground) //
constant function MPS_HeightLet takes nothing returns real
return 3.00
endfunction
//----------------------------------------------------------------//
// Gravity: This is how fast projectiles are pulled back into //
// the ground //
constant function MPS_Gravity takes nothing returns real
return 0.3065
endfunction
//----------------------------------------------------------------//
// -READ ONLY-READ ONLY-READ ONLY-READ ONLY-READ ONLY-READONLY- //
constant function MPS_ProjectileStageID takes nothing returns integer
return 1
endfunction
//----------------------------------------------------------------//
constant function MPS_ProjectileRecycleStageID takes nothing returns integer
return 2
endfunction
//----------------------------------------------------------------//
// END OF CONFIGURATION //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function used for registering new ammo types into the system //
// call this function after setting up a new ammo type will //
// allow it to be used as an ammo type for spells/turrets/etc. //
////////////////////////////////////////////////////////////////////
function MPS_RegisterAmmo takes integer RegisterNumber returns nothing
local integer Id
if (RegisterNumber == 0) then
set udg_MPS_AmmoNumber = ( udg_MPS_AmmoNumber + 1 )
set Id = udg_MPS_AmmoNumber
else
set Id = RegisterNumber
endif
set udg_MPS_ProjectileName[Id] = StringCase(udg_MPS_ProjectileName[0], true)
set udg_MPS_ProjectileModel[Id] = udg_MPS_ProjectileModel[0]
set udg_MPS_ProjectileDeathModel[Id] = udg_MPS_ProjectileDeathModel[0]
set udg_MPS_ProjectileModelScale[Id] = udg_MPS_ProjectileModelScale[0]
set udg_MPS_ProjectileAOE[Id] = udg_MPS_ProjectileAOE[0]
set udg_MPS_ProjectileHealthDamage[Id] = udg_MPS_ProjectileHealthDamage[0]
set udg_MPS_ProjectileManaDamage[Id] = udg_MPS_ProjectileManaDamage[0]
set udg_MPS_ProjectileAttackType[Id] = udg_MPS_ProjectileAttackType[0]
set udg_MPS_ProjectileDamageType[Id] = udg_MPS_ProjectileDamageType[0]
set udg_MPS_ProjectileAcceleration[Id] = udg_MPS_ProjectileAcceleration[0]
set udg_MPS_ProjectileTurnRate[Id] = udg_MPS_ProjectileTurnRate[0]
set udg_MPS_ProjectileTurnEfficiency[Id] = udg_MPS_ProjectileTurnEfficiency[0]
set udg_MPS_ProjectileTargetAimOffset[Id] = udg_MPS_ProjectileTargetAimOffset[0]
endfunction
////////////////////////////////////////////////////////////////////
// This function returns the Id number of the ammunition //
// which shares the name with the passed parameter, it can be //
// used to consistently refer to ammunition without knowing the //
// order by which it was declared (allowing dynamic introduction //
// of ammunition types //
////////////////////////////////////////////////////////////////////
function MPS_GetAmmoByName takes string name returns integer
local integer iLoop = 0
set name = StringCase(name, true)
loop
set iLoop = iLoop + 1
exitwhen iLoop > udg_MPS_AmmoNumber
if (name == udg_MPS_ProjectileName[iLoop]) then
return iLoop
endif
endloop
return 0
endfunction
////////////////////////////////////////////////////////////////////
// Function used for registering new slave types into the system //
// call this function after setting up a new slave type will //
// allow it to be used as an slave type for spells/turrets/etc. //
////////////////////////////////////////////////////////////////////
function MPS_RegisterSlave takes integer RegisterNumber returns nothing
local integer Id
if (RegisterNumber == 0) then
set udg_MPS_SlaveNumber = ( udg_MPS_SlaveNumber + 1 )
set Id = udg_MPS_SlaveNumber
else
set Id = RegisterNumber
endif
set udg_MPS_SlaveName[Id] = StringCase(udg_MPS_SlaveName[0], true)
set udg_MPS_SlaveModel[Id] = udg_MPS_SlaveModel[0]
set udg_MPS_SlaveDeathModel[Id] = udg_MPS_SlaveDeathModel[0]
set udg_MPS_SlaveModelScale[Id] = udg_MPS_SlaveModelScale[0]
set udg_MPS_SlaveAOE[Id] = udg_MPS_SlaveAOE[0]
set udg_MPS_SlaveHealthDamage[Id] = udg_MPS_SlaveHealthDamage[0]
set udg_MPS_SlaveManaDamage[Id] = udg_MPS_SlaveManaDamage[0]
set udg_MPS_SlaveAttackType[Id] = udg_MPS_SlaveAttackType[0]
set udg_MPS_SlaveDamageType[Id] = udg_MPS_SlaveDamageType[0]
set udg_MPS_SlaveOffset[Id] = udg_MPS_SlaveOffset[0]
set udg_MPS_SlavePull[Id] = udg_MPS_SlavePull[0]
set udg_MPS_SlaveSpin[Id] = udg_MPS_SlaveSpin[0]
endfunction
////////////////////////////////////////////////////////////////////
// This function returns the Id number of the slave //
// which shares the name with the passed parameter, it can be //
// used to consistently refer to a slave without knowing the //
// order by which it was declared (allowing dynamic introduction //
// of slave types //
////////////////////////////////////////////////////////////////////
function MPS_GetSlaveByName takes string name returns integer
local integer iLoop = 0
set name = StringCase(name, true)
loop
set iLoop = iLoop + 1
exitwhen iLoop > udg_MPS_SlaveNumber
if (name == udg_MPS_SlaveName[iLoop]) then
return iLoop
endif
endloop
return iLoop
endfunction
////////////////////////////////////////////////////////////////////
// Function used to get the z height of locations needed by the //
// spell, since it can only be done with locations, this one //
// is reused throughout, instead of creating/destroying them //
////////////////////////////////////////////////////////////////////
function MPS_GetZ takes real x, real y returns real
//Gets the location Z of the selected location
call MoveLocation(udg_MPS_Loc, x, y)
return GetLocationZ(udg_MPS_Loc)
endfunction
////////////////////////////////////////////////////////////////////
// Function used to make sure that the location is within the //
// map bounds so that units cannot be moved outside of it and //
// get permanently stuck //
////////////////////////////////////////////////////////////////////
function MPS_ValidateLocation takes real x, real y returns boolean
//Check if the point is within the map bounds
return (x < udg_MPS_MapMaxX) and (x > udg_MPS_MapMinX) and (y < udg_MPS_MapMaxY) and (y > udg_MPS_MapMinY)
endfunction
////////////////////////////////////////////////////////////////////
// Function used to identify what units are valid damaging //
// targets for a given projectile //
////////////////////////////////////////////////////////////////////
function MPS_ValidateTarget takes unit u, integer Node returns boolean
//Checks if the passed unit can be used as a valid target for damage
return ((IsUnitEnemy(u, udg_MPS_ProjectilePlayer[Node])) and (GetUnitTypeId(u) != MPS_ProjectileDummyID()) and not(IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0))
endfunction
////////////////////////////////////////////////////////////////////
// Function used to update event listeners //
////////////////////////////////////////////////////////////////////
function MPS_FireEvent takes real EventID returns nothing
set udg_MPS_ACTIVE_EVENT = EventID
set udg_MPS_ACTIVE_EVENT = udg_MPS_NO_EVENT
endfunction
////////////////////////////////////////////////////////////////////
// Function used to recycle instances, so that they can used //
// again later, keeping the total array sizes smaller //
////////////////////////////////////////////////////////////////////
function MPS_ProjectileRecycle takes integer Node returns nothing
//Recycles the node
set udg_MPS_ProjectileRecycleNodes[udg_MPS_ProjectileRecyclableNodes] = Node
set udg_MPS_ProjectileRecyclableNodes = udg_MPS_ProjectileRecyclableNodes + 1
set udg_MPS_ProjectileNextNode[udg_MPS_ProjectilePrevNode[Node]] = udg_MPS_ProjectileNextNode[Node]
set udg_MPS_ProjectilePrevNode[udg_MPS_ProjectileNextNode[Node]] = udg_MPS_ProjectilePrevNode[Node]
//Stops the timer if this is the only remaining Node
if (udg_MPS_ProjectilePrevNode[0] == 0) then
call PauseTimer(udg_MPS_ProjectileTimer)
endif
endfunction
////////////////////////////////////////////////////////////////////
// Function used to create new Nodes for the system whenever a //
// unit or effect is added to run in the loop function //
////////////////////////////////////////////////////////////////////
function MPS_ProjectileCreateNode takes nothing returns integer
//set up local
local integer Node = 0
//Checking for recycleable Nodes
if (udg_MPS_ProjectileRecyclableNodes == 0) then
set udg_MPS_ProjectileNodeNumber = udg_MPS_ProjectileNodeNumber + 1
set Node = udg_MPS_ProjectileNodeNumber
else
set udg_MPS_ProjectileRecyclableNodes = udg_MPS_ProjectileRecyclableNodes - 1
set Node = udg_MPS_ProjectileRecycleNodes[udg_MPS_ProjectileRecyclableNodes]
endif
//Sets up this Node
set udg_MPS_ProjectileNextNode[Node] = 0
set udg_MPS_ProjectileNextNode[udg_MPS_ProjectilePrevNode[0]] = Node
set udg_MPS_ProjectilePrevNode[Node] = udg_MPS_ProjectilePrevNode[0]
set udg_MPS_ProjectilePrevNode[0] = Node
return Node
endfunction
///////////////////////////////////////////////////////////////////
// Function used to recycle instances, so that they can used //
// again later, keeping the total array sizes smaller //
////////////////////////////////////////////////////////////////////
function MPS_SlaveRecycle takes integer Node returns nothing
//Recycles the node
set udg_MPS_SlaveRecycleNodes[udg_MPS_SlaveRecyclableNodes] = Node
set udg_MPS_SlaveRecyclableNodes = udg_MPS_SlaveRecyclableNodes + 1
set udg_MPS_SlaveNextNode[udg_MPS_SlavePrevNode[Node]] = udg_MPS_SlaveNextNode[Node]
set udg_MPS_SlavePrevNode[udg_MPS_SlaveNextNode[Node]] = udg_MPS_SlavePrevNode[Node]
endfunction
////////////////////////////////////////////////////////////////////
// Function used to create new Nodes for the system whenever a //
// unit or effect is added to run in the loop function //
////////////////////////////////////////////////////////////////////
function MPS_SlaveCreateNode takes nothing returns integer
//set up local
local integer Node = 0
//Checking for recycleable Nodes
if (udg_MPS_SlaveRecyclableNodes == 0) then
set udg_MPS_SlaveNodeNumber = udg_MPS_SlaveNodeNumber + 1
set Node = udg_MPS_SlaveNodeNumber
else
set udg_MPS_SlaveRecyclableNodes = udg_MPS_SlaveRecyclableNodes - 1
set Node = udg_MPS_SlaveRecycleNodes[udg_MPS_SlaveRecyclableNodes]
endif
//Sets up this Node
set udg_MPS_SlaveNextNode[Node] = 0
set udg_MPS_SlaveNextNode[udg_MPS_SlavePrevNode[0]] = Node
set udg_MPS_SlavePrevNode[Node] = udg_MPS_SlavePrevNode[0]
set udg_MPS_SlavePrevNode[0] = Node
return Node
endfunction
////////////////////////////////////////////////////////////////////
// Function used to move all projectile-type entities in the //
// abiity - the physics engine of the spell //
////////////////////////////////////////////////////////////////////
function MPS_Move takes integer Node returns boolean
//set up locals
local real dy = udg_MPS_TargetY[Node] - udg_MPS_ProjectileY[Node]
local real dx = udg_MPS_TargetX[Node]- udg_MPS_ProjectileX[Node]
local real x
local real y
local real Angle = Atan2(dy, dx)
local real Angle2 = Atan2((MPS_GetZ(udg_MPS_TargetX[Node], udg_MPS_TargetY[Node]) + udg_MPS_TargetZ[Node]) - (MPS_GetZ(udg_MPS_ProjectileX[Node], udg_MPS_ProjectileY[Node]) + GetUnitFlyHeight(udg_MPS_Projectile[Node])), Pow(dx * dx + dy * dy, 0.5))
local real Angle3 = Atan2(udg_MPS_YVel[Node], udg_MPS_XVel[Node])
local real Angle4 = Atan(udg_MPS_ZVel[Node])
local real TempReal = Pow((udg_MPS_ZVel[Node] * udg_MPS_ZVel[Node]) + (udg_MPS_XVel[Node] * udg_MPS_XVel[Node]) + (udg_MPS_YVel[Node] * udg_MPS_YVel[Node]), 0.5) * udg_MPS_ProjectileTurnEfficiency[udg_MPS_AmmoType[Node]]
local real TempReal2 = 1/(1 + udg_MPS_ProjectileTurnEfficiency[udg_MPS_AmmoType[Node]])
//Calculate new velocities
set udg_MPS_ZVel[Node] = ((udg_MPS_ZVel[Node] + (TempReal + udg_MPS_ProjectileTurnRate[udg_MPS_AmmoType[Node]]) * Sin(Angle2)) * udg_MPS_ProjectileAcceleration[udg_MPS_AmmoType[Node]]) * TempReal2
set udg_MPS_XVel[Node] = ((udg_MPS_XVel[Node] + (TempReal + udg_MPS_ProjectileTurnRate[udg_MPS_AmmoType[Node]]) * Cos(Angle) * Cos(Angle2)) * udg_MPS_ProjectileAcceleration[udg_MPS_AmmoType[Node]]) * TempReal2
set udg_MPS_YVel[Node] = ((udg_MPS_YVel[Node] + (TempReal + udg_MPS_ProjectileTurnRate[udg_MPS_AmmoType[Node]]) * Sin(Angle) * Cos(Angle2)) * udg_MPS_ProjectileAcceleration[udg_MPS_AmmoType[Node]]) * TempReal2
set udg_MPS_ProjectileHeight[Node] = udg_MPS_ProjectileHeight[Node] + udg_MPS_ZVel[Node] - MPS_Gravity()
set x = udg_MPS_ProjectileX[Node] + udg_MPS_XVel[Node]
set y = udg_MPS_ProjectileY[Node] + udg_MPS_YVel[Node]
//Make sure the location is within the map bounds
if MPS_ValidateLocation(x, y) then
set udg_MPS_ProjectileX[Node] = x
set udg_MPS_ProjectileY[Node] = y
call SetUnitX(udg_MPS_Projectile[Node], x)
call SetUnitY(udg_MPS_Projectile[Node], y)
endif
//Update target unit information if applicable
if not(udg_MPS_ProjectileTargetUnit[Node] == null) then
set udg_MPS_TargetX[Node] = GetUnitX(udg_MPS_ProjectileTargetUnit[Node])
set udg_MPS_TargetY[Node] = GetUnitY(udg_MPS_ProjectileTargetUnit[Node])
set udg_MPS_TargetZ[Node] = GetUnitFlyHeight(udg_MPS_ProjectileTargetUnit[Node]) + udg_MPS_ProjectileTargetAimOffset[udg_MPS_AmmoType[Node]]
endif
//Apply visuals
call SetUnitFlyHeight(udg_MPS_Projectile[Node], udg_MPS_ProjectileHeight[Node] - MPS_GetZ(x, y), 0.)
call SetUnitFacing(udg_MPS_Projectile[Node], bj_RADTODEG * Atan2(udg_MPS_YVel[Node], udg_MPS_XVel[Node]))
set udg_MPS_ProjectileAnimation[Node] = R2I(Atan2((udg_MPS_ZVel[Node]), Pow((udg_MPS_XVel[Node] * udg_MPS_XVel[Node]) + (udg_MPS_YVel[Node] * udg_MPS_YVel[Node]), 0.5) * bj_RADTODEG + 0.5)) + 70
call SetUnitAnimationByIndex(udg_MPS_Projectile[Node], udg_MPS_ProjectileAnimation[Node])
//Check if the unit has crashed into the ground
if (GetUnitFlyHeight(udg_MPS_Projectile[Node]) <= MPS_HeightLet()) then
call MPS_FireEvent(udg_MPS_MASTER_CRASH)
return true
endif
call MPS_FireEvent(udg_MPS_MASTER_UPDATE)
return false
endfunction
////////////////////////////////////////////////////////////////////
// Function used to identify when a projectile has hit its //
// target unit so that damage can be dealt & stun can be //
// applied if applicable //
////////////////////////////////////////////////////////////////////
function MPS_HitTarget takes integer Node returns boolean
//Set up locals
local real dx = udg_MPS_ProjectileX[Node] - udg_MPS_TargetX[Node]
local real dy = udg_MPS_ProjectileY[Node] - udg_MPS_TargetY[Node]
local real dz = (MPS_GetZ(udg_MPS_ProjectileX[Node], udg_MPS_ProjectileY[Node]) + GetUnitFlyHeight(udg_MPS_Projectile[Node])) - (MPS_GetZ(udg_MPS_TargetX[Node], udg_MPS_TargetY[Node]) + GetUnitFlyHeight(udg_MPS_ProjectileTargetUnit[Node]))
//Measure distance between the Unit and its Target and return if it's close enough
if (dx * dx + dy * dy + dz * dz <= udg_MPS_ProjectileAOE[udg_MPS_AmmoType[Node]] * udg_MPS_ProjectileAOE[udg_MPS_AmmoType[Node]]) then
call MPS_FireEvent(udg_MPS_MASTER_HIT)
return true
endif
return false
endfunction
////////////////////////////////////////////////////////////////////
// Function used to damage all valid units within the impact //
// area of the projectile once it has crashed or hit the target //
////////////////////////////////////////////////////////////////////
function MPS_ProjectileDamageArea takes integer Node returns nothing
//Set up locals
local unit u
local real x
local real y
local real z = GetUnitFlyHeight(udg_MPS_Projectile[Node])
local real dx
local real dy
local real dz
//Get units in range
call GroupEnumUnitsInRange(udg_MPS_TempGroup, udg_MPS_ProjectileX[Node], udg_MPS_ProjectileY[Node], udg_MPS_ProjectileAOE[udg_MPS_AmmoType[Node]], null)
loop
set u = FirstOfGroup(udg_MPS_TempGroup)
exitwhen (u == null)
set x = GetUnitX(u)
set y = GetUnitY(u)
set dx = udg_MPS_ProjectileX[Node] - x
set dy = udg_MPS_ProjectileY[Node] - y
set dz = (MPS_GetZ(udg_MPS_ProjectileX[Node], udg_MPS_ProjectileY[Node]) + z) - (MPS_GetZ(x, y) + GetUnitFlyHeight(u))
//Check if they are close enough and are valid as a damage target
if (dx * dx + dy * dy + dz * dz <= udg_MPS_ProjectileAOE[udg_MPS_AmmoType[Node]] * udg_MPS_ProjectileAOE[udg_MPS_AmmoType[Node]]) and (MPS_ValidateTarget(u, Node)) then
//Apply Damage
call UnitDamageTarget(udg_MPS_ProjectileLauncher[Node], u, udg_MPS_ProjectileHealthDamage[udg_MPS_AmmoType[Node]], false, false, udg_MPS_ProjectileAttackType[udg_MPS_AmmoType[Node]], udg_MPS_ProjectileDamageType[udg_MPS_AmmoType[Node]], null)
call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) - udg_MPS_ProjectileManaDamage[udg_MPS_AmmoType[Node]])
endif
call GroupRemoveUnit(udg_MPS_TempGroup, u)
endloop
endfunction
////////////////////////////////////////////////////////////////////
// Function used to keep all the Slave projectiles attached //
// to their masters, runs once at the end of the loop to keep //
// system efficiency up //
////////////////////////////////////////////////////////////////////
function MPS_UpdateSlaveProjectiles takes nothing returns nothing
//Set up locals
local integer TempInt = 0
local integer Node = 0
local real x
local real y
local real z
local real x2
local real y2
local real Incline
local real facing
local real correction
local real dx
local real dy
local real dz
local unit u
loop
set Node = udg_MPS_SlaveNextNode[Node]
exitwhen Node == 0
set udg_MPS_ACTIVE_NODE = Node
//Find the incline of the Orbit and position on the Orbit
set Incline = Atan2(udg_MPS_ZVel[udg_MPS_MasterNode[Node]] * udg_MPS_ZVel[udg_MPS_MasterNode[Node]], udg_MPS_XVel[udg_MPS_MasterNode[Node]] * udg_MPS_XVel[udg_MPS_MasterNode[Node]] + udg_MPS_YVel[udg_MPS_MasterNode[Node]] * udg_MPS_YVel[udg_MPS_MasterNode[Node]]) + 1.570800
set udg_MPS_SlaveAngle[Node] = udg_MPS_SlaveAngle[Node] + udg_MPS_SlaveSpin[udg_MPS_SlaveType[Node]]
set facing = GetUnitFacing(udg_MPS_Projectile[udg_MPS_MasterNode[Node]]) * bj_DEGTORAD
//Apply Pull in correct direction
if (udg_MPS_SlaveCurrentOffset[Node] > 0.) then
set udg_MPS_SlaveVel[Node] = udg_MPS_SlaveVel[Node] - udg_MPS_SlavePull[udg_MPS_SlaveType[Node]]
set correction = udg_MPS_SlaveAngle[Node]
else
set udg_MPS_SlaveVel[Node] = udg_MPS_SlaveVel[Node] + udg_MPS_SlavePull[udg_MPS_SlaveType[Node]]
set correction = udg_MPS_SlaveAngle[Node] * - 1
endif
//Find new location
set udg_MPS_SlaveCurrentOffset[Node] = udg_MPS_SlaveCurrentOffset[Node] + udg_MPS_SlaveVel[Node]
set x = udg_MPS_ProjectileX[udg_MPS_MasterNode[Node]] + (udg_MPS_SlaveCurrentOffset[Node] * Cos(correction))
set y = udg_MPS_ProjectileY[udg_MPS_MasterNode[Node]] + (udg_MPS_SlaveCurrentOffset[Node] * Sin(correction))
set z = udg_MPS_ProjectileHeight[udg_MPS_MasterNode[Node]] + (udg_MPS_SlaveCurrentOffset[Node] * Sin(Incline) * Cos(facing) * Cos(correction)) + (MPS_GetZ(x, y) - MPS_GetZ(udg_MPS_ProjectileX[udg_MPS_MasterNode[Node]], udg_MPS_ProjectileY[udg_MPS_MasterNode[Node]])) - MPS_GetZ(x, y)
//Apply new location
call SetUnitX(udg_MPS_Slave[Node], x)
call SetUnitY(udg_MPS_Slave[Node], y)
call SetUnitFlyHeight(udg_MPS_Slave[Node], z, 0.)
call SetUnitAnimationByIndex(udg_MPS_Slave[Node], udg_MPS_ProjectileAnimation[udg_MPS_MasterNode[Node]])
call SetUnitFacing(udg_MPS_Slave[Node], GetUnitFacing(udg_MPS_Projectile[udg_MPS_MasterNode[Node]]))
if (udg_MPS_ProjectileStageID[udg_MPS_MasterNode[Node]] == MPS_ProjectileRecycleStageID() or z <= MPS_HeightLet()) then
//Slave killed
call MPS_FireEvent(udg_MPS_SLAVE_DEATH)
//Destroy effects on crash or Master death
call DestroyEffect(udg_MPS_SlaveCurrentEffect[Node])
call DestroyEffect(AddSpecialEffectTarget(udg_MPS_SlaveDeathModel[udg_MPS_SlaveType[Node]], udg_MPS_Slave[Node], MPS_ProjectileAttachmentPoint()))
//Select units to damage
call GroupEnumUnitsInRange(udg_MPS_TempGroup, x, y, udg_MPS_SlaveAOE[udg_MPS_SlaveType[Node]], null)
loop
set u = FirstOfGroup(udg_MPS_TempGroup)
exitwhen (u == null)
set x2 = GetUnitX(u)
set y2 = GetUnitY(u)
set dx = x - x2
set dy = y - y2
set dz = (MPS_GetZ(x, y) + z) - (MPS_GetZ(x2, y2) + GetUnitFlyHeight(u))
//Check they are within range and are a valid target
if (dx * dx + dy * dy + dz * dz <= udg_MPS_SlaveAOE[udg_MPS_SlaveType[Node]] * udg_MPS_SlaveAOE[udg_MPS_SlaveType[Node]]) and (MPS_ValidateTarget(u, udg_MPS_MasterNode[Node])) then
//Deal Damage
call UnitDamageTarget(udg_MPS_ProjectileLauncher[udg_MPS_MasterNode[Node]], u, udg_MPS_SlaveHealthDamage[udg_MPS_SlaveType[Node]], false, false, udg_MPS_SlaveAttackType[udg_MPS_SlaveType[Node]], udg_MPS_SlaveDamageType[udg_MPS_SlaveType[Node]], null)
call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) - udg_MPS_SlaveManaDamage[udg_MPS_SlaveType[Node]])
endif
call GroupRemoveUnit(udg_MPS_TempGroup, u)
endloop
//Start Recycling
call KillUnit(udg_MPS_Slave[Node])
call MPS_SlaveRecycle(Node)
else
call MPS_FireEvent(udg_MPS_SLAVE_UPDATE)
endif
endloop
endfunction
////////////////////////////////////////////////////////////////////
// The main function which is used to handle the control of all //
// of the projectiles handled by the system //
////////////////////////////////////////////////////////////////////
function MPS_ProjectileLoop takes nothing returns nothing
//Sets up locals
local integer Node = 0
loop
set Node = udg_MPS_ProjectileNextNode[Node]
exitwhen Node == 0
set udg_MPS_ACTIVE_NODE = Node
//Projectiles
if (udg_MPS_ProjectileStageID[Node] == MPS_ProjectileStageID()) then
//Move the projectile
if (MPS_Move(Node)) or (MPS_HitTarget(Node)) then
//Destroy on crash
call DestroyEffect(udg_MPS_ProjectileCurrentEffect[Node])
call DestroyEffect(AddSpecialEffectTarget(udg_MPS_ProjectileDeathModel[udg_MPS_AmmoType[Node]], udg_MPS_Projectile[Node], MPS_ProjectileAttachmentPoint()))
//Deal Damage
call MPS_ProjectileDamageArea(Node)
//Start Recycling
set udg_MPS_ProjectileStageID[Node] = MPS_ProjectileRecycleStageID()
endif
//Recycling
else
//Remove and Recycle Unit
call KillUnit(udg_MPS_Projectile[Node])
call MPS_ProjectileRecycle(Node)
endif
endloop
//If you're not using the slave projectile system remove these lines of Code
//************************************************************************
if (udg_MPS_SlaveNextNode[0] > 0) then
call MPS_UpdateSlaveProjectiles()
endif
//************************************************************************
endfunction
///////////////////////////////////////////////////////////////////
// Function used to add Slave Projectiles to their Masters //
// only needs to know the Node number of the Master, what //
// Slave Ammo to use and how much of it to make //
///////////////////////////////////////////////////////////////////
function MPS_AddSlaveProjectiles takes integer Master, integer Ammo, integer Amount returns nothing
//Set up locals
local integer iLoop = 0
local integer Node
local real Angle = 0
local real Increment
//Make sure there's at least one to put on before dividing
if (Amount > 0) then
set Increment = 2 * bj_PI / Amount
//Create projectiles
loop
set iLoop = iLoop + 1
exitwhen (iLoop > Amount)
//Set up Slave Data
set Angle = Angle + Increment
set Node = MPS_SlaveCreateNode()
set udg_MPS_SlaveAngle[Node] = Angle
set udg_MPS_SlaveCurrentOffset[Node] = udg_MPS_SlaveOffset[Ammo]
set udg_MPS_SlaveVel[Node] = 0
set udg_MPS_MasterNode[Node] = Master
set udg_MPS_SlaveType[Node] = Ammo
//Create Slave Projectile and Apply Aesthetics
set udg_MPS_Slave[Node] = CreateUnit(MPS_DummyPlayer(), MPS_ProjectileDummyID(), udg_MPS_ProjectileX[Master], udg_MPS_ProjectileY[Master], GetUnitFacing(udg_MPS_Projectile[Master]))
set udg_MPS_SlaveCurrentEffect[Node] = AddSpecialEffectTarget(udg_MPS_SlaveModel[Ammo], udg_MPS_Slave[Node], MPS_ProjectileAttachmentPoint())
if UnitAddAbility(udg_MPS_Slave[Node], 'Amrf') and UnitRemoveAbility(udg_MPS_Slave[Node], 'Amrf') then
endif
call SetUnitScale(udg_MPS_Slave[Node], udg_MPS_SlaveModelScale[Ammo], 0., 0.)
call SetUnitFlyHeight(udg_MPS_Slave[Node], GetUnitFlyHeight(udg_MPS_Projectile[Master]), 0.)
call PauseUnit(udg_MPS_Slave[Node], true)
//Fire create event
set udg_MPS_ACTIVE_NODE = Node
call MPS_FireEvent(udg_MPS_SLAVE_CREATE)
endloop
endif
endfunction
////////////////////////////////////////////////////////////////////
// Function used to create the projectiles in the system, it //
// must be given an Ammo type, strength of launch, the angles //
// for the launch (Pitch and Angle), starting co-ordinates //
// and a target (if a unit is passed as a target then target //
// X and Y need not be passed (pass a value of 0 for both) //
////////////////////////////////////////////////////////////////////
function MPS_CreateProjectile takes unit u, integer Ammo, real Power, real Pitch, real Angle, real x, real y, real z, unit Target, real TargetX, real TargetY returns integer
local integer Node = MPS_ProjectileCreateNode()
//Setup up Master Projectile Data
set udg_MPS_AmmoType[Node] = Ammo
set udg_MPS_ProjectileX[Node] = x
set udg_MPS_ProjectileY[Node] = y
set udg_MPS_TargetX[Node] = TargetX
set udg_MPS_TargetY[Node] = TargetY
set udg_MPS_ZVel[Node] = Power * Sin(Pitch)
set udg_MPS_XVel[Node] = Power * Cos(Angle) * Cos(Pitch)
set udg_MPS_YVel[Node] = Power * Sin(Angle) * Cos(Pitch)
set udg_MPS_ProjectileHeight[Node] = z + MPS_GetZ(x, y)
set udg_MPS_ProjectileStageID[Node] = MPS_ProjectileStageID()
set udg_MPS_ProjectileTargetUnit[Node] = Target
set udg_MPS_ProjectileLauncher[Node] = u
set udg_MPS_ProjectilePlayer[Node] = GetTriggerPlayer()
//Check if it has a target
if not(Target == null) then
set udg_MPS_TargetZ[Node] = GetUnitFlyHeight(udg_MPS_ProjectileTargetUnit[Node]) + udg_MPS_ProjectileTargetAimOffset[udg_MPS_AmmoType[Node]]
else
set udg_MPS_TargetZ[Node] = 0.
endif
//Create Projectile and Apply Aesthetics
set udg_MPS_Projectile[Node] = CreateUnit(MPS_DummyPlayer(), MPS_ProjectileDummyID(), x, y, Angle * bj_RADTODEG)
if UnitAddAbility(udg_MPS_Projectile[Node], 'Amrf') and UnitRemoveAbility(udg_MPS_Projectile[Node], 'Amrf') then
endif
set udg_MPS_ProjectileCurrentEffect[Node] = AddSpecialEffectTarget(udg_MPS_ProjectileModel[Ammo], udg_MPS_Projectile[Node], MPS_ProjectileAttachmentPoint())
call SetUnitScale(udg_MPS_Projectile[Node], udg_MPS_ProjectileModelScale[Ammo], 0., 0.)
call SetUnitFlyHeight(udg_MPS_Projectile[Node], z, 0.)
call PauseUnit(udg_MPS_Projectile[Node], true)
//Fire create event
set udg_MPS_ACTIVE_NODE = Node
call MPS_FireEvent(udg_MPS_MASTER_CREATE)
//Start Timer
if udg_MPS_ProjectilePrevNode[Node] == 0 then
call TimerStart(udg_MPS_ProjectileTimer, MPS_ProjectileTimerSpeed(), true, function MPS_ProjectileLoop)
endif
//Pass back node number for Slave projectiles
return Node
endfunction
////////////////////////////////////////////////////////////////////
// Initialisation trigger, applies the conditions to triggers //
// and sets up the global location used to get location Z's //
// as well as the map bounds and event variable values //
////////////////////////////////////////////////////////////////////
function InitTrig_Master_Projectile_System takes nothing returns nothing
//Set up the location used to find Z heights
set udg_MPS_Loc = Location(0,0)
//Set up the Timer used to run the loop
set udg_MPS_ProjectileTimer = CreateTimer()
//Set up event variables
set udg_MPS_NO_EVENT = 0.
set udg_MPS_MASTER_CREATE = 1.
set udg_MPS_MASTER_UPDATE = 2.
set udg_MPS_MASTER_CRASH = 3.
set udg_MPS_MASTER_HIT = 4.
set udg_MPS_SLAVE_CREATE = 5.
set udg_MPS_SLAVE_UPDATE = 6.
set udg_MPS_SLAVE_DEATH = 7.
//Set up Amount of active slaves and ammo
set udg_MPS_AmmoNumber = 0
set udg_MPS_SlaveNumber = 0
//Sets up the variables used to make sure a point is within the map area
set udg_MPS_MapMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
set udg_MPS_MapMinX = GetRectMinX(bj_mapInitialPlayableArea)
set udg_MPS_MapMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
set udg_MPS_MapMinY = GetRectMinY(bj_mapInitialPlayableArea)
endfunction
////////////////////////////////////////////////////////////////////
// End of the system //
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// MASTER SALVO SYSTEM V1.02 //
// Author: Tank-Commander //
// Requires: MASTER PROJECTILE SYSTEM //
// Purpose: Create Impressive and Dynamic Salvos of projectiles //
// //
// Notes: //
// - Read the readme before you try modifying the config //
// - Use the "Helpful files" to help you import the spell //
// //
// If you have used this system 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. //
// //
// This system does not have a configuration (as it does not //
// have any use of one) //
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function used for registering new salvo types into the system //
// call this function after setting up a new salvo type will //
// allow it to be used as a salvo type for spells/turrets/etc. //
////////////////////////////////////////////////////////////////////
function MPS_RegisterSalvo takes integer RegisterNumber returns nothing
local integer Id
if (RegisterNumber == 0) then
set udg_MPS_SalvoNumber = ( udg_MPS_SalvoNumber + 1 )
set Id = udg_MPS_SalvoNumber
else
set Id = RegisterNumber
endif
set udg_MPS_SalvoName[Id] = StringCase(udg_MPS_SalvoName[0], true)
set udg_MPS_SalvoMasterAmmoAmount[Id] = udg_MPS_SalvoMasterAmmoAmount[0]
set udg_MPS_SalvoSlaveAmmoAmount[Id] = udg_MPS_SalvoSlaveAmmoAmount[0]
set udg_MPS_SalvoAngles[Id] = udg_MPS_SalvoAngles[0]
set udg_MPS_SalvoPower[Id] = udg_MPS_SalvoPower[0]
set udg_MPS_SalvoHeightOffset[Id] = udg_MPS_SalvoHeightOffset[0]
endfunction
////////////////////////////////////////////////////////////////////
// This function returns the Id number of the salvo //
// which shares the name with the passed parameter, it can be //
// used to consistently refer to a salvo without knowing the //
// order by which it was declared (allowing dynamic introduction //
// of salvo types //
////////////////////////////////////////////////////////////////////
function MPS_GetSalvoByName takes string name returns integer
local integer iLoop = 0
set name = StringCase(name, true)
loop
set iLoop = iLoop + 1
exitwhen iLoop > udg_MPS_SalvoNumber
if (name == udg_MPS_SalvoName[iLoop]) then
return iLoop
endif
endloop
return 0
endfunction
////////////////////////////////////////////////////////////////////
// Function used for registering new attack types into the system//
// call this function after setting up a new attack type will //
// allow it to be used as an attack type for spells/turrets/etc. //
////////////////////////////////////////////////////////////////////
function MPS_RegisterAttack takes integer RegisterNumber returns nothing
local integer Id
if (RegisterNumber == 0) then
set udg_MPS_AttackNumber = ( udg_MPS_AttackNumber + 1 )
set Id = udg_MPS_AttackNumber
else
set Id = RegisterNumber
endif
set udg_MPS_AttackName[Id] = StringCase(udg_MPS_AttackName[0], true)
set udg_MPS_AttackSalvo[Id] = udg_MPS_AttackSalvo[0]
set udg_MPS_AttackMasterAmmo[Id] = udg_MPS_AttackMasterAmmo[0]
set udg_MPS_AttackSlaveAmmo[Id] = udg_MPS_AttackSlaveAmmo[0]
endfunction
////////////////////////////////////////////////////////////////////
// This function returns the Id number of the attack //
// which shares the name with the passed parameter, it can be //
// used to consistently refer to a attack without knowing the //
// order by which it was declared (allowing dynamic introduction //
// of attack types //
////////////////////////////////////////////////////////////////////
function MPS_GetAttackByName takes string name returns integer
local integer iLoop = 0
set name = StringCase(name, true)
loop
set iLoop = iLoop + 1
exitwhen iLoop > udg_MPS_AttackNumber
if (name == udg_MPS_AttackName[iLoop]) then
return iLoop
endif
endloop
return 0
endfunction
////////////////////////////////////////////////////////////////////
// Function used to Launch a Salvo from a specific unit to a //
// specific target in accordance to what it was passed //
////////////////////////////////////////////////////////////////////
function MPS_LaunchSalvo takes unit u, integer Salvo, integer MasterAmmo, integer SlaveAmmo, unit Target, real TargetX, real TargetY returns nothing
//Set up locals
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real z = GetUnitFlyHeight(u) + udg_MPS_SalvoHeightOffset[Salvo]
local real Facing = GetUnitFacing(u) * bj_DEGTORAD
local real Increment = 0
local real Offsetting = 0
local real Angle
local real Pitch
local integer iLoop = 0
//Make sure there's at least more than one master before dividing
if (udg_MPS_SalvoMasterAmmoAmount[Salvo] > 1) then
set Increment = udg_MPS_SalvoAngles[Salvo] / (udg_MPS_SalvoMasterAmmoAmount[Salvo] - 1)
set Offsetting = (udg_MPS_SalvoAngles[Salvo] / 2)
endif
//trigger event
set udg_MPS_ACTIVE_NODE = Salvo
call MPS_FireEvent(udg_MPS_SALVO_FIRED)
//Set up the Angle
set Angle = Facing - Offsetting
//Create Master and Slave Projectiles
loop
set iLoop = iLoop + 1
exitwhen iLoop > udg_MPS_SalvoMasterAmmoAmount[Salvo]
set Pitch = Cos((Facing - Angle))
if (SlaveAmmo > 0) then
call MPS_AddSlaveProjectiles(MPS_CreateProjectile(u, MasterAmmo, udg_MPS_SalvoPower[Salvo], Pitch, Angle, x, y, z, Target, TargetX, TargetY), SlaveAmmo, udg_MPS_SalvoSlaveAmmoAmount[Salvo])
else
call MPS_CreateProjectile(u, MasterAmmo, udg_MPS_SalvoPower[Salvo], Pitch, Angle, x, y, z, Target, TargetX, TargetY)
endif
set Angle = Angle + Increment
endloop
endfunction
////////////////////////////////////////////////////////////////////
// Function used to fire projectiles based on a defined Attack //
// using a specific Salvo, Master and Slave Ammo for ease of //
// use //
////////////////////////////////////////////////////////////////////
function MPS_ActivateAttack takes integer Attack, unit Launcher, unit Target, real x, real y returns nothing
set udg_MPS_ACTIVE_NODE = Attack
call MPS_FireEvent(udg_MPS_ATTACK_USED)
call MPS_LaunchSalvo(Launcher, udg_MPS_AttackSalvo[Attack], udg_MPS_AttackMasterAmmo[Attack], udg_MPS_AttackSlaveAmmo[Attack], Target, x, y)
endfunction
////////////////////////////////////////////////////////////////////
// Function which only exists to allow for compilation and set //
// up event variable values //
////////////////////////////////////////////////////////////////////
function InitTrig_Master_Salvo_System takes nothing returns nothing
set udg_MPS_SALVO_FIRED = 8.
set udg_MPS_ATTACK_USED = 9.
endfunction
////////////////////////////////////////////////////////////////////
// END OF THE SYSTEM //
////////////////////////////////////////////////////////////////////
The setup triggers are divided into categories (they do not need to be like this
but it is more practical this way), The names of the triggers are fairly
self-explanatory.
------------------------------------------------------------------------
The Master Ammo Setup
The fundamental setup - without this you have no projectiles to work with
and the other systems cannot do anything at all, it may be a bit difficult
to get the hang of but with a good understanding you can make really
beautiful looking projectiles and arcs.
- ProjectileName: This is the name given to the ammo so you can refer to it later
via the "MPS_GetAmmoByName(string Ammo)" function (pass this
value to get the index number of the ammo)
- ProjectileModel: This is the filepath of the model used for the projectile
while it is moving
- ProjectileDeathModel: This is the filepath of the model used for the projectile
while it is blowing up
- ProjectileScale: This is the size of the projectile and its death model,
1.00 = 100% (normal scale)
- ProjectileAOE: This is how close to its target the projectile needs to get
before it will detonate, it is also how large the area around
the target of the projectile which will also be affected is
- ProjectileHealthDamage: The damage dealt to the health of targets hit
- ProjectileManaDamage: The damage dealt to the mana of targets hit
- ProjectileAttackType: This is the Attack Type used by the projectile, it decides
what kind of damage buffs/reductions are applied to the
damage dealt by the projectile
- ProjectileWeaponType: This is the Weapon Type used by the projectile, it doesn't
mean much however unless you're using another external
system to make it do something useful
- ProjectileAcceleration: This is how much the net Speed of the projectile is
multiplied by every timer iteration (don't set it too
high or the projectile will fly out of control, too low
and it will barely move without a high turn rate
- ProjectileTurnRate: This is the amount of momentum the projectile gains each timer
iteration directly in the direction of the target (causing it
to turn)
- ProjectileTurnEfficiency: This is the portion of the net speed of the projectile
which is converted to be in the direction of the target
each step, note though that this results in a fractional
amount where 1.00 = 1/2, 0.5 = 2/3, 1.5 = 3/5.
(essetially TurnEfficiency / (1 + TurnEfficiency) with
this value being high, the projectile will always face
the target
- ProjectileTargetAimOffset: This is the extra Z height offset that the projectile
will aim at (this prevents the projectile from hitting
the ground too early)
------------------------------------------------------------------------
The Slave Ammo Setup
The Slave Projectile setup is a lot like the Master Ammo setup except with
some small differences: There's no physics controls (Accel/Turn/etc.)
instead you're given three different parameters.
- SlaveOffset: This is how far away from the Master projectile the Slave starts
- SlavePull: This is the strength at which the Slave projectile is pulled toward
The master projectile (this forms a sort of bouncing effect)
- SlaveSpin: This is how fast the projectile spins around the master projectile
The value is in radians so be weary when setting it
------------------------------------------------------------------------
The Salvos Setup
The Salvos Setup is where things start getting very fancy, this setup allows
you to launch multiple projectiles at once with different Arcs with each having
their own Slave projectiles.
- SalvoMasterAmmoAmount: This is how many Master projectiles that will be launched
by the salvo
- SalvoSlaveAmmoAmount: This is how many Slave projectiles each Master projectile
will ve given
- SalvoAngles: This is the area in which projectiles can be launched (a measure in
radians) a value of PI/2 results in an even spread in a |- shape
(where "-" is the facing angle of the launcher) Master projectiles
will be distributed evenly within the specified area of angles
- SalvoPower: This is the amount of force each Master Projectile is launched with
giving them an extra push as they are fired
- SalvoHeightOffset: This is the added amount of height given to Master Projectiles
fired by this salvo from the height of the launcher in order
to prevent them from hitting into the ground on launch
------------------------------------------------------------------------
The Attack Setup
By far the simplest setup, the Attack setup brings everything together in
an easy-to-use package once everything has been specified, as such it's
pretty simple to use.
- AttackSalvo: What Salvo Type this Attack shall be using
- AttackMasterAmmo: What Master Projectiles shall the Attack be using
- AttackSlaveAmmo: What Slave Projectiles shall the Attack by using
------------------------------------------------------------------------
Please use this document for referral if you are having difficulty or getting
confused by the setups for each area. Each Setup also has dividing markers
to show where one setup ends and each begins (and it's encouraged to
continue the structure in order to avoid confusion later on).
Events in this context are variables that reach a certain value when something occurs
you can respond to them via using the event of a Real becoming a certain value.
Event variable: udg_MPS_ACTIVE_EVENT
- this is the variable that is set to these other values listed below when an
event occurs so it is the one you need to be checking the value of
when any event occurs you can use the MPS_ACTIVE_NODE variable to get the index
value of the activating component (or in the case of the salvo system, the salvo
or attack type that was used, for projectiles you can access what type of ammo or
slave was used by using the following:
- udg_MPS_AmmoType[udg_MPS_ACTIVE_NODE] for master projectiles
- udg_MPS_SlaveType[udg_MPS_ACTIVE_NODE] for slave projectiles
- udg_MPS_AmmoType[udg_MPS_MasterNode[udg_MPS_ACTIVE_NODE]] to get the master projecile
type for any given slave projectile
WARNING: As slave projectiles and master projectiles are stored in seperate linked lists
it is possible and even likely that a master and slave projectile share an index number
when using this system to make abilities or other systems please use different variables
if you are performing actions on both slaves and masters
The values these systems has built-in for you to respond to:
------------------------------------------------------------------------
MASTER PROJECTILE SYSTEM:
MASTER RESPONCES
- udg_MPS_MASTER_CREATE: Fires when a master projectile is made
- udg_MPS_MASTER_UPDATE: Fires whenever a master projectile is updated (moved)
- udg_MPS_MASTER_CRASH: Fires when a master projectile hits the ground
- udg_MPS_MASTER_HIT: Fires when a master projectile hits a target
Depending on what kind of ability you are attempting to make/use you will either
be using CRASH or HIT but probably not both (unless you want something to happen
regardless of how the projectile meets its end)
If you want something to be happening all the time while the projectile is moving
UPDATE is the event for you
CREATE is probably the least used event of these unless you are making multiple
master projectiles and need them to behave independantly but you may still find
use for it in other scenarios
ADDITIONAL:
- Whenever you create a projectile using call MPS_CreateProjectile()
MPS_ACTIVE_NODE is set to that projectile index, so you can latch on to
the linked list of the projectiles instead of making your own structure for
your ability
SLAVE RESPONCES
- udg_MPS_SLAVE_CREATE: Fires when a slave projectile is made
- udg_MPS_MASTER_UPDATE: Fires whenever a slave projectile is updated (moved)
- udg_MPS_SLAVE_DEATH: Fires whenever a slave projectile is destroyed
Unlike master projectiles they do not distinguish between hitting their target
and crashing into the floor as they are tied to their master (although they
may still crash into the floor and they do not technically have a target)
this is why they only have the DEATH event
the other two are exactly the same as the Master projectile counterparts
------------------------------------------------------------------------
SALVO PROJECTILE SYSTEM
- udg_MPS_SALVO_FIRED: Fires whenever a salvo is fired
- udg_MPS_ATTACK_USED: Fires whenever an attack is used
both pretty self-explanatory
If you are not using the Master Salvo System (or the Attack system within it)
then you are going to need to know a few things in order to maximise the
usage out of the master projectile system
1) How do I create projectiles without Slave Projectiles?
function MPS_CreateProjectile takes unit u, integer Ammo, real Power, real Pitch, real Angle, real x, real y, real z, unit Target, real TargetX, real TargetY returns integer
the function returns an integer (which is the Node Number of the projectile created)
- MPS_CreateProjectile(u, Ammo, Power, Pitch, Angle, x, y, z, Target, TargetX, Target Y)
There are a lot of parameters that the function asks for
u - The unit creating the projectile
Ammo - What Master Projectile you're using
Power - How strong is the launch of the projectile
Pitch - What is the Angle upwards that you're firing with (Radians)
Angle - What is the Angle across the X/Y you're firing with (Radians)
x - x co-ordinate to place the projectile
y - y co-ordinate to place the projectile
z - z co-ordinate to place the projectile
Target - Target unit of the projectile (can be null)
TargetX - Only used if Target is null, Target X co-ordinate
TargetY - Only used if Target is null, Target Y co-ordinate
2) So now you know how to make a Master projectile, but how do you give a Master Projectile its slaves?
Well you directly nest the MPS_CreateProjectile function into the MPS_AddSlaveProjectiles function
function MPS_AddSlaveProjectiles takes integer Master, integer Ammo, integer Amount returns nothing
- MPS_AddSlaveProjectiles(Master, Ammo, Amount)
This one is significantly simpler than creating Master Projectiles
Master - The Node Number of the Master Projectile
Ammo - What Slave Projectile you're using
Amount - How many Slave projectiles do you want
So when combined they look like this:
call MPS_AddSlaveProjectiles(MPS_CreateProjectile(u, Ammo, Power, Pitch, Angle, x, y, z, Target, TargetX, Target Y), Ammo, Amount)
compact but it works
3)' By now it is looking like you ought to be using the Salvo system which makes doing this much easier
but you chose not to, so what else is important to remember?
Without having the attack system to help you either (inbuilt to the Salvo system) you are going to need to code up
each of the attacks as well as all the things the salvo does for you this includes:
- The Angle needs to take into account of the direction between launcher and caster (or the facing angle)
- Pitch should not be so high as to fire projectiles backwards (unless that's what you wanted)
- It becomes much harder to differentiate between different attacks without the inbuilt system
All things considered it shouldn't be too difficult to code up your own methods of utilising the system
While this example is rather crude is proves very effective at replacing all unit
attacks with projectiles generated through this system.
First you will need the Force Attack Trigger - this will order a unit to attack
whenever it attempts to attack. While this is weird at first it is essential
to the trick done to complete this task
Next, go into the object editor and create an ability based off channel
this will need as many levels as you have attacks.
change the Base order ID of every level to "attack", set duration to 0
set the disabling of other abilities to false and the follow through time
to 0 as well
the remaining parameters you have are Target Type, Cast Range and Cooldown
These are now your attacks, Target Type works best with "Unit Target"
as it still allows you to attack move successfully (this will however
also make your projectiles homing unless you make sure not to pass a unit
when activating the attack)
Cast Range now becomes your attack range (note: the unit the ability is
attached to will still need the correct/same attack range) and Cooldown
is now your attack cooldown (set the unit attack speed to be about twice
as fast as this cooldown, e.g. if the cooldown is 1.5 seconds set the
attack speed to 0.75, it's also recommended to change the animation
to "attack" if applicable, but in this case it may be a good idea to set
the attack speed to a value to at least match if not be more than the
cooldown
Now give all units with projectile attacks the ability (this can also be
achieved by having different channel abilities for each attack which may
be preferable to the next part if you're willing to do it)
You now need to make sure that all of your units has the right ability
which means setting all of them to have the right level (setting the level
of the ability to the right thing when they enter the map bounds and running
a trigger setting all exisiting units to the right level as well)
Finally you need a trigger to run the right attack for each level, the
example trigger here is rather crude and quite inefficient but serves the
purpose, a better method would be to cycle through each level and tie a
name or attack index number to each to then automatically run them
(some extra work if you wanted to occassionally pass null for a unit target
to avoid homing projectiles)
You can set the attack of units with the abilities to whatever you want
though it'd be helpful if it gave some indication of purpose
such as DPS - it may also be an idea to use gameplay constants to make
their attack type always deal 0 damage incase of any worries