library MissileLibrary initializer Init requires GroupRecycler
///////////////////////////////////////
// Missile Spells/System //
// by: Adiktuz //
// Version 1.5 //
///////////////////////////////////////
//Description: //
//A simple system made for the custom//
//missile spells included in the map //
//but can also be used as a base for //
//your own custom missile system //
///////////////////////////////////////
// Credits //
// Vexorian for the dummy.mdx //
// vJASS //
//Element of Water, Deaod, Itachi009 //
//The_Reborn_Devil, hell_gate //
//CBX_Entertainment //
///////////////////////////////////////
//List of Included Actions
//Arrow_Create
//Arrow_Storm_Create (A massive version of Arrow_Create)
//Arrow_Create_Custom (Enables creation of missiles that do not use the predefined variables of this system)
//Missile_Movement
//Knockback
//Don't forget to import dummy.mdx
//You can configure this things
globals
constant real RANGE_BASE = 1000 //This is the base range of the missile spells
constant real RANGE_INCREMENT = 100 //This is the range increment for each level of the spells
constant integer MISSILE = 'h001' //This is the raw code for the missile dummy
constant integer DUMMY = 'h003' //This is the raw code for the dummy caster used by Prismatic Missile
constant real RADIUS = 300 //This is the damage radius for the explosive missile
constant real RADIUS_FILTER = 100 //This is the radius when a missile will hit a unit, this is also the activation radius for the explosion of an explosive missile
constant real DISTANCE = 20 //This is the distance travelled by the missile every .03 seconds
constant real KNOCK_BACK_RANGE = 200 //This is the distance of the knockback
constant real KB_DIST = 10 //This is the distance at which the unit is knockbacked every .03 seconds
constant integer TIME = 20 //This is the time on how long the status of Prismatic Missile will lasts per hit (10 = 1 second)
constant integer FREEZE = 'A00H' //This is the raw code of the freeze spell
constant integer STUN = 'A00G' //This is the raw code of the stun spell
constant boolean CUSTOM_STATUS = true //When this is true, Prismatic Missile will use the custom status effect system
//If you will use the Status Effect System, the buffs from Prismatic Missile will stack for every hit
//If you set CUSTOM_STATUS to false be sure to modify the duration for the status effects in the object editor
group DAMAGE_GROUP = CreateGroup() //No need to configure this
group TEMP_GROUP = CreateGroup() //No need to configure this
group TEMP_GROUP_E = CreateGroup() //No need to configure this
//The following are paths for the model files to be used by the missiles.
//If you are going to change the paths here be sure to change all \ in the path to \\
constant string EXPLOSION_EFFECT = "Objects\\Spawnmodels\\Human\\FragmentationShards\\FragBoomSpawn.mdl" //The path for the effect for the explosion
constant string PIERCING_MISSILE = "Abilities\\Weapons\\Arrow\\ArrowMissile.mdl" //This is the path for the model for the piercing missile
constant string EXPLOSION_MISSILE = "Abilities\\Weapons\\SearingArrow\\SearingArrowMissile.mdl" //This is the path for the model for the explosion missile
constant string PRISMATIC_MISSILE = "Abilities\\Weapons\\ProcMissile\\ProcMissile.mdl" //This is the path for the model for the prismatic missile
constant string SH_MISSILE = "Abilities\\Weapons\\ColdArrow\\ColdArrowMissile.mdl" //This is the path for the model for the single hit missile
constant string KNOCKBACK_PIERCING_MISSILE = "Abilities\\Spells\\Other\\BlackArrow\\BlackArrowMissile.mdl" //This is the path for the model for the piercing missile if knockback is enabled
constant string KNOCKBACK_EXPLOSION_MISSILE = null//This is the path for the model for the explosion missile if knockback is enabled
constant string KNOCKBACK_PRISMATIC_MISSILE = null//This is the path for the model for the prismatic missile if knockback is enabled
constant string KNOCKBACK_SH_MISSILE = null//This is the path for the model for the single hit missile if knockback is enabled
constant string KB_EFFECT = null //This is the path of the knockback effect
//End of configurables
real MAP_X_MAX
real MAP_X_MIN
real MAP_Y_MIN
real MAP_Y_MAX
private timer T = CreateTimer()
private integer array DATA
private integer TOTAL = 0
private timer TK = CreateTimer()
private integer array DATAK
private integer TOTALK = 0
endglobals
//Do not edit anything from here unless you are sure what you are doing
private function missilecheck takes nothing returns boolean
return (GetUnitTypeId(GetFilterUnit()) != MISSILE and GetWidgetLife(GetFilterUnit()) >= .405)
endfunction
private struct Missile
real x
real y
real dx
real dy
real dxk
real dyk
unit u
player owner
real angle
real distance
real damage
boolean kb = false
effect Effect
real kb_distance = KNOCK_BACK_RANGE
group Group //= CreateGroup()
real speed
string behavior
boolean height
private method onDestroy takes nothing returns nothing
call KillUnit(this.u)
set this.u = null
set this.owner = null
set this.Group = null
set this.Effect = null
endmethod
static method Knockback takes nothing returns nothing
local Missile dat
local integer i = 0
loop
exitwhen i == TOTALK
set dat = DATAK[i]
set dat.x = dat.x + dat.dxk
set dat.y = dat.y + dat.dyk
if (dat.x >= MAP_X_MIN and dat.x <= MAP_X_MAX and dat.y >= MAP_Y_MIN and dat.y <= MAP_Y_MAX) then
call SetUnitPosition(dat.u, dat.x, dat.y)
call DestroyEffect(AddSpecialEffect(KB_EFFECT, dat.x, dat.y))
endif
set dat.kb_distance = dat.kb_distance - KB_DIST
if dat.kb_distance <= 0 then
set dat.u = null //We set it to null because the onDestroy method has the action KillUnit and so this line would prevent the unit to be killed
call dat.destroy()
set TOTALK = TOTALK - 1
set DATAK[i] = DATAK[TOTALK]
set i = i - 1
endif
set i = i + 1
endloop
if TOTALK == 0 then
call PauseTimer(TK)
endif
endmethod
static method KB_Init takes unit a, real angle returns nothing
local Missile dat = Missile.allocate()
set DATAK[TOTALK] = dat
set dat.u = a
set dat.owner = GetOwningPlayer(dat.u)
set dat.angle = angle
set dat.x = GetUnitX(dat.u)
set dat.y = GetUnitY(dat.u)
set dat.dxk = KB_DIST*Cos(angle)
set dat.dyk = KB_DIST*Sin(angle)
if TOTALK == 0 then
call TimerStart(TK, .03, true, function Missile.Knockback)
endif
set TOTALK = TOTALK + 1
endmethod
static method move takes nothing returns nothing
local unit c
local unit e
local real dist = 0
local location mpoint
local location tpoint
local integer i = 0
local real dista
local Missile dat
loop
exitwhen i == TOTAL
set dat = DATA[i]
set tpoint = Location(dat.x, dat.y)
set dat.x = dat.x + dat.dx
set dat.y = dat.y + dat.dy
set DAMAGE_GROUP = dat.Group
if (dat.x >= MAP_X_MIN and dat.x <= MAP_X_MAX and dat.y >= MAP_Y_MIN and dat.y <= MAP_Y_MAX) then
call SetUnitPosition(dat.u, dat.x, dat.y)
else
set dat.distance = 0
endif
set mpoint = Location(dat.x,dat.y)
if dat.height then
call SetUnitFlyHeight(dat.u, GetUnitFlyHeight(dat.u) + GetLocationZ(tpoint) - GetLocationZ(mpoint) ,0)
if GetUnitFlyHeight(dat.u) <= 1 then
set dat.distance = 0
endif
endif
call RemoveLocation(tpoint)
call GroupEnumUnitsInRange(TEMP_GROUP, dat.x, dat.y, RADIUS_FILTER, Condition(function missilecheck))
if FirstOfGroup(TEMP_GROUP) != null then
if dat.behavior == "PIERCE" then
loop
set c = FirstOfGroup(TEMP_GROUP)
exitwhen c == null
if (IsPlayerEnemy(GetOwningPlayer(c), dat.owner) and IsUnitInGroup(c, DAMAGE_GROUP) != true) then
call GroupAddUnit(DAMAGE_GROUP, c)
if dat.kb == true then
call Missile.KB_Init(c,dat.angle)
endif
call UnitDamageTarget(dat.u, c, dat.damage , false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_DIVINE, WEAPON_TYPE_WHOKNOWS)
endif
call GroupRemoveUnit(TEMP_GROUP, c)
endloop
endif
if dat.behavior == "SINGLE" then
loop
set c = FirstOfGroup(TEMP_GROUP)
exitwhen c == null
if IsPlayerEnemy(GetOwningPlayer(c), dat.owner) then
set dista = SquareRoot((dat.x - GetUnitX(c))*(dat.x - GetUnitX(c)) + (dat.y - GetUnitY(c))*(dat.y - GetUnitY(c)))
if dist != 0 then
if dist > dista then
set e = c
set dist = dista
endif
else
set e = c
set dist = dista
endif
endif
call GroupRemoveUnit(TEMP_GROUP, c)
endloop
if e != null then
call UnitDamageTarget(dat.u, e, dat.damage , false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_DIVINE, WEAPON_TYPE_WHOKNOWS)
set dat.distance = 0
if dat.kb == true then
call Missile.KB_Init(c,dat.angle)
endif
endif
endif
if dat.behavior == "EXPLODE" then
call GroupEnumUnitsInRange(TEMP_GROUP_E, dat.x, dat.y, RADIUS, Condition(function missilecheck))
loop
set c = FirstOfGroup(TEMP_GROUP_E)
exitwhen c == null
if (IsPlayerEnemy(GetOwningPlayer(c), dat.owner) and IsUnitInGroup(c, DAMAGE_GROUP) != true) then
if dat.kb == true then
call Missile.KB_Init(c,dat.angle)
endif
call GroupAddUnit(DAMAGE_GROUP, c)
call UnitDamageTarget(dat.u, c, dat.damage , false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_DIVINE, WEAPON_TYPE_WHOKNOWS)
set dat.distance = 0
call DestroyEffect(AddSpecialEffect(EXPLOSION_EFFECT, GetUnitX(c), GetUnitY(c)))
endif
call GroupRemoveUnit(TEMP_GROUP_E, c)
endloop
endif
if dat.behavior == "PRISM" then
loop
set c = FirstOfGroup(TEMP_GROUP)
exitwhen c == null
if (IsPlayerEnemy(GetOwningPlayer(c), dat.owner) and IsUnitInGroup(c, DAMAGE_GROUP) != true) then
if dat.kb == true then
call Missile.KB_Init(c,dat.angle)
endif
call GroupAddUnit(DAMAGE_GROUP, c)
if GetRandomInt(1,100) >= 50 then
set e = CreateUnit(dat.owner, DUMMY, dat.x, dat.y, 0.00)
call UnitApplyTimedLife(e, 'BTLF', 1.00)
call UnitAddAbility(e, FREEZE)
call IssueTargetOrder(e, "slow", c)
if CUSTOM_STATUS == true then
call SaveInteger(udg_StatusHash, GetHandleId(c), StringHash("FreezeTime"), LoadInteger(udg_StatusHash, GetHandleId(c), StringHash("FreezeTime"))+ TIME)
call GroupAddUnit(udg_StatusGroup, c)
call EnableTrigger(gg_trg_Status_Effect_Removal)
endif
else
set e = CreateUnit(dat.owner, DUMMY, dat.x, dat.y, 0.00)
call UnitApplyTimedLife(e, 'BTLF', 1.00)
call UnitAddAbility(e, STUN)
call IssueTargetOrder(e, "firebolt", c)
if CUSTOM_STATUS == true then
call SaveInteger(udg_StatusHash, GetHandleId(c), StringHash("StunTime"), LoadInteger(udg_StatusHash, GetHandleId(c), StringHash("FreezeTime")) + TIME)
call GroupAddUnit(udg_StatusGroup, c)
call EnableTrigger(gg_trg_Status_Effect_Removal)
endif
endif
call UnitDamageTarget(dat.u, c, dat.damage , false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_DIVINE, WEAPON_TYPE_WHOKNOWS)
endif
call GroupRemoveUnit(TEMP_GROUP, c)
endloop
endif
endif
set dat.distance = dat.distance - DISTANCE
if dat.distance <= 0 then
call DestroyEffect(dat.Effect)
call ReleaseGroup(dat.Group)
call dat.destroy()
set TOTAL = TOTAL - 1
set DATA[i] = DATA[TOTAL]
set i = i - 1
endif
set i = i + 1
endloop
if TOTAL == 0 then
call PauseTimer(T)
endif
call RemoveLocation(mpoint)
set c = null
set e = null
set mpoint = null
set tpoint = null
endmethod
endstruct
//The following actions are for the missile creation
function Arrow_Create takes unit a, real b, real c, boolean d, string e, real angle, real height, boolean constantfly returns nothing
local Missile dat = Missile.create()
local group grup = NewGroup()
local real x = GetUnitX(a)
local real y = GetUnitY(a)
set DATA[TOTAL] = dat
set dat.owner = GetOwningPlayer(a)
set dat.u = CreateUnit(dat.owner, MISSILE, x, y, bj_RADTODEG*angle)
set dat.x = GetUnitX(dat.u)
set dat.y = GetUnitY(dat.u)
if e == "PIERCE" then
if d == false then
set dat.Effect = AddSpecialEffectTarget(PIERCING_MISSILE, dat.u, "origin")
else
set dat.Effect = AddSpecialEffectTarget(KNOCKBACK_PIERCING_MISSILE, dat.u, "origin")
endif
endif
if e == "EXPLODE" then
if d == false then
set dat.Effect = AddSpecialEffectTarget(EXPLOSION_MISSILE, dat.u, "origin")
else
set dat.Effect = AddSpecialEffectTarget(KNOCKBACK_EXPLOSION_MISSILE, dat.u, "origin")
endif
endif
if e == "PRISM"then
if d == false then
set dat.Effect = AddSpecialEffectTarget(PRISMATIC_MISSILE, dat.u, "origin")
else
set dat.Effect = AddSpecialEffectTarget(KNOCKBACK_PRISMATIC_MISSILE, dat.u, "origin")
endif
endif
if e == "SINGLE"then
if d == false then
set dat.Effect = AddSpecialEffectTarget(SH_MISSILE, dat.u, "origin")
else
set dat.Effect = AddSpecialEffectTarget(KNOCKBACK_SH_MISSILE, dat.u, "origin")
endif
endif
set dat.behavior = e
set dat.distance = RANGE_BASE + RANGE_INCREMENT*c
set dat.damage = b
set dat.angle = angle
set dat.Group = grup
set dat.kb = d
set dat.dx = DISTANCE*Cos(angle)
set dat.dy = DISTANCE*Sin(angle)
set grup = null
call UnitAddAbility(dat.u, 'Amrf')
call UnitRemoveAbility(dat.u, 'Amrf')
call SetUnitFlyHeight(dat.u, height, 0)
set dat.height = constantfly
set TOTAL = TOTAL + 1
if TOTAL == 1 then
call TimerStart(T, .03, true, function Missile.move)
endif
endfunction
function Arrow_Create_Custom takes unit a, real b, real c, boolean d, string e, real angle, string f, string g, real h, real i, real height ,boolean constantfly returns nothing
local Missile dat = Missile.create()
local group grup = CreateGroup()
local real x = GetUnitX(a)
local real y = GetUnitY(a)
set dat.owner = GetOwningPlayer(a)
set dat.u = CreateUnit(dat.owner, MISSILE, x, y, bj_RADTODEG*angle)
set dat.x = GetUnitX(dat.u)
set dat.y = GetUnitY(dat.u)
if e == "PIERCE" then
if d == false then
set dat.Effect = AddSpecialEffectTarget(PIERCING_MISSILE, dat.u, "origin")
else
set dat.Effect = AddSpecialEffectTarget(KNOCKBACK_PIERCING_MISSILE, dat.u, "origin")
endif
endif
if e == "EXPLODE" then
if d == false then
set dat.Effect = AddSpecialEffectTarget(EXPLOSION_MISSILE, dat.u, "origin")
else
set dat.Effect = AddSpecialEffectTarget(KNOCKBACK_EXPLOSION_MISSILE, dat.u, "origin")
endif
endif
if e == "PRISM"then
if d == false then
set dat.Effect = AddSpecialEffectTarget(PRISMATIC_MISSILE, dat.u, "origin")
else
set dat.Effect = AddSpecialEffectTarget(KNOCKBACK_PRISMATIC_MISSILE, dat.u, "origin")
endif
endif
if e == "SINGLE"then
if d == false then
set dat.Effect = AddSpecialEffectTarget(SH_MISSILE, dat.u, "origin")
else
set dat.Effect = AddSpecialEffectTarget(KNOCKBACK_SH_MISSILE, dat.u, "origin")
endif
endif
set dat.behavior = e
set dat.distance = h
set dat.damage = b
set dat.angle = angle
set dat.Group = grup
set dat.kb = d
set dat.speed = i
set dat.dx = dat.speed*Cos(angle)
set dat.dy = dat.speed*Sin(angle)
set grup = null
call UnitAddAbility(dat.u, 'Amrf')
call UnitRemoveAbility(dat.u, 'Amrf')
call SetUnitFlyHeight(dat.u, height, 0)
set dat.height = constantfly
if TOTAL == 0 then
call TimerStart(T, .03, true, function Missile.move)
endif
set TOTAL = TOTAL + 1
endfunction
private function Init takes nothing returns nothing
set MAP_X_MAX = GetRectMaxX(bj_mapInitialPlayableArea)
set MAP_X_MIN = GetRectMinX(bj_mapInitialPlayableArea)
set MAP_Y_MAX = GetRectMaxY(bj_mapInitialPlayableArea)
set MAP_Y_MIN = GetRectMinY(bj_mapInitialPlayableArea)
endfunction
endlibrary