//==================================/ Projectile Framework /=================================================
scope LightningBlast initializer init
globals
private constant integer MISSILE_NUMBER = 6
private constant integer ABIL_ID = 'A006'
private constant integer DUMMY_ID = 'o002'
private constant integer CASTER_DUMMY_ID = 0
private constant integer CASTER_DUMMY_EX_BUFF = 0
private constant integer CASTER_DUMMY_ABIL_ID = 0
private constant integer UNIT_HIDE_INTERVAL = 1
private constant string CAST_FX = "Abilities\\Spells\\Orc\\LightningBolt\\LightningBoltMissile.mdl"
private constant string DUMMY_CAST_FX = null
private constant string HIT_FX = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
private constant string DEATH_MISSILE_FX = null
private constant string DEATH_ENEMY_FX = null
private constant string TRAIL_FX = null
private constant string LIGHTNING_FX = "FORK"
private constant string FX_ATTACH_POINT = "chest"
private constant string DEATH_FX_ATTACH_POINT = null
private constant string CASTER_DUMMY_ORDER = null
private constant real COLLISION_SIZE = 128.
private constant real DUMMY_EX_TIME = 1.
private constant real FX_RED = 0.35
private constant real FX_GREEN = 0.65
private constant real FX_BLUE = 1.
private constant real CAST_RANGE = 350.
private constant real RANGE_PER_LEVEL = 0.
private constant real TRANSPARENCY_THRESH = 0.5
private constant real TRANSPARENCY_INCREMENT = 0.1
private constant real MISSILE_ANGLE = 20.
private constant real MISSILE_ANGLE_LVL_REDUC = 0.
private constant real MISSILE_MOVESPEED = 20.
private constant real FX_Z = 36.
private constant real DAMAGE_AMOUNT = 85.
private constant real DAMAGE_PER_LEVEL = 0.
private constant real TIMING_INTERVAL = 0.5// Slowed down from 0.02 temporarily to make it visible
private constant real CAST_ANGLE_OFFSET = ((MISSILE_NUMBER * MISSILE_ANGLE) * .5) + (.5 * MISSILE_ANGLE)
private constant boolean KILLS_TREES = false
private constant boolean HAS_DUMMY_CAST_FX = false
private constant boolean HAS_TRAIL_FX = false
private constant boolean FX_ON_COLLISION = true
private constant boolean FX_ON_DEATH_MISSILE = false
private constant boolean FX_ON_DEATH_ENEMY = false
private constant boolean DIES_ON_COLLISION = false
private constant boolean INDIVID_DIE_ONHIT = true
private constant boolean MISSILES_EXPLODE = false
private constant boolean MISSILES_EXPLODE_ONHIT = false
private constant boolean DEALS_DAMAGE = true
private constant boolean CASTS_SPELL_ON_TARGET = false
private constant boolean CASTS_SPELL_ON_POINT = false
private constant boolean CASTS_SPELL_NOTARGET = false
private constant boolean SPELL_HAS_LEVELS = false
private constant boolean IS_TARGETED = true
private constant boolean IS_UNIT_TARGETED = false
private constant boolean HAS_HOMING = false
private constant boolean HIDING_UNITS = false
private constant boolean HIDING_UNITS_INTERVAL = false
private constant boolean IS_CIRCLE = MISSILE_ANGLE * MISSILE_NUMBER == 360.
private timer LightningBlastTimer = CreateTimer()
private integer LightningBlastInstances = 0
private group LightningBlastEnumGroup = CreateGroup()
private LightningBlastData array LightningBlastArray
endglobals
private function TreeFilter takes nothing returns boolean
local integer d = GetDestructableTypeId(GetFilterDestructable())
return d == 'ATtr' or d == 'BTtw' or d == 'KTtw' or d == 'YTft' or d == 'JTct' or d == 'YTst' or d == 'YTct' or d == 'YTwt' or d == 'JTwt' or d == 'JTwt' or d == 'FTtw' or d == 'CTtr' or d == 'ITtw' or d == 'NTtw' or d == 'OTtw' or d == 'ZTtw' or d == 'WTst' or d == 'LTlt' or d == 'GTsh' or d == 'Xtlt' or d == 'WTtw' or d == 'Attc' or d == 'BTtc' or d == 'CTtc' or d == 'ITtc' or d == 'NTtc' or d == 'ZTtc' /*
*/and GetDestructableLife(GetFilterDestructable()) > 0.405
endfunction
private function KillTree takes nothing returns nothing
call KillDestructable(GetEnumDestructable())
endfunction
private function MoveUnit takes unit movingUnit, real speed, real angle returns nothing
local real x
local real y
set x = GetUnitX(movingUnit) + speed * Cos(angle * bj_DEGTORAD)
set y = GetUnitY(movingUnit) + speed * Sin(angle * bj_DEGTORAD)
call SetUnitBoundedX(movingUnit, x)
call SetUnitBoundedY(movingUnit, y)
endfunction
struct LightningBlastData
boolean Missilesexplode
unit caster
unit targetedunit
unit array Missiles[MISSILE_NUMBER]
player casterOwner
lightning array beams[MISSILE_NUMBER]
integer casterLevel
integer dummySpellLevel
real distance
real maxDist
real transparency
group LightningBlastTargets
private static method LightningBlastExecution takes nothing returns nothing
local boolexpr TreeCheck
local integer i = 0
local integer d
local integer Missilecount = 0
local location zLoc
local unit enemy
local unit casterDummy
local effect collisionFX
local effect trailFX
local effect dummycastFX
local real x
local real y
local real array z
local rect area
local LightningBlastData localLightningBlast
loop
exitwhen i >= LightningBlastInstances
set localLightningBlast = LightningBlastArray[i]
if localLightningBlast.distance < localLightningBlast.maxDist then
if localLightningBlast.transparency > 0. and (localLightningBlast.distance / localLightningBlast.maxDist) > TRANSPARENCY_THRESH then
set localLightningBlast.transparency = localLightningBlast.transparency - TRANSPARENCY_INCREMENT
endif
loop
exitwhen Missilecount == MISSILE_NUMBER
call MoveUnit(localLightningBlast.Missiles[Missilecount], MISSILE_MOVESPEED, GetUnitFacing(localLightningBlast.Missiles[Missilecount]))
if HAS_HOMING == true and IS_UNIT_TARGETED == true then
call SetUnitFacing(localLightningBlast.Missiles[Missilecount], /*
*/bj_RADTODEG*Atan2((GetUnitY(localLightningBlast.targetedunit)-GetUnitY(localLightningBlast.Missiles[Missilecount])), /*
*/(GetUnitX(localLightningBlast.targetedunit)-GetUnitX(localLightningBlast.Missiles[Missilecount]))))
endif
set x = GetUnitX(localLightningBlast.Missiles[Missilecount])
set y = GetUnitY(localLightningBlast.Missiles[Missilecount])
set TreeCheck = Filter(function TreeFilter)
set area = Rect(x - COLLISION_SIZE, y - COLLISION_SIZE, x + COLLISION_SIZE, y + COLLISION_SIZE)
set zLoc = Location(x, y)
set z[Missilecount] = FX_Z + GetLocationZ(zLoc)
call SetUnitFlyHeight(localLightningBlast.Missiles[Missilecount], FX_Z, 0.)
if HAS_TRAIL_FX == true then
set trailFX = AddSpecialEffect(TRAIL_FX, GetUnitX(localLightningBlast.Missiles[Missilecount]), GetUnitY(localLightningBlast.Missiles[Missilecount]))
call DestroyEffect(trailFX)
endif
if ModuloInteger(Missilecount, UNIT_HIDE_INTERVAL) == 0 and HIDING_UNITS == true then
call SetUnitVertexColor(localLightningBlast.Missiles[Missilecount], R2I(255. * FX_RED), R2I(255. * FX_GREEN), R2I(255. * FX_BLUE), 0)
else
if ModuloInteger(Missilecount, UNIT_HIDE_INTERVAL) != 0 and HIDING_UNITS_INTERVAL == true then
call SetUnitVertexColor(localLightningBlast.Missiles[Missilecount], R2I(255. * FX_RED), R2I(255. * FX_GREEN), R2I(255. * FX_BLUE), 0)
else
call SetUnitVertexColor(localLightningBlast.Missiles[Missilecount], R2I(255. * FX_RED), R2I(255. * FX_GREEN), R2I(255. * FX_BLUE), R2I(localLightningBlast.transparency*255))
endif
endif
if Missilecount != MISSILE_NUMBER then
call MoveLightningEx(localLightningBlast.beams[Missilecount], true, GetUnitX(localLightningBlast.Missiles[Missilecount-1]), GetUnitY(localLightningBlast.Missiles[Missilecount-1]), z[Missilecount-1], GetUnitX(localLightningBlast.Missiles[Missilecount]), GetUnitY(localLightningBlast.Missiles[Missilecount]), z[Missilecount])
if localLightningBlast.beams[0] != null then
call MoveLightningEx(localLightningBlast.beams[0], true, GetUnitX(localLightningBlast.Missiles[0]), GetUnitY(localLightningBlast.Missiles[0]), z[0], GetUnitX(localLightningBlast.Missiles[Missilecount]), GetUnitY(localLightningBlast.Missiles[Missilecount]), z[Missilecount])
endif
endif
call SetLightningColor(localLightningBlast.beams[Missilecount],FX_RED,FX_GREEN,FX_BLUE,localLightningBlast.transparency)
if KILLS_TREES == true then
call EnumDestructablesInRect(area, TreeCheck, function KillTree)
endif
call GroupEnumUnitsInRange(LightningBlastEnumGroup, GetUnitX(localLightningBlast.Missiles[Missilecount]), GetUnitY(localLightningBlast.Missiles[Missilecount]), COLLISION_SIZE, null)
loop
set enemy = FirstOfGroup(LightningBlastEnumGroup)
exitwhen enemy == null
if ValidAliveTargetGroupedGround(enemy, localLightningBlast.caster, localLightningBlast.LightningBlastTargets) == true then
call GroupAddUnit(localLightningBlast.LightningBlastTargets, enemy)
if DEALS_DAMAGE == true then
call UnitDamageTarget(localLightningBlast.caster, enemy, DAMAGE_AMOUNT + (DAMAGE_PER_LEVEL * localLightningBlast.casterLevel), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, null)
endif
if CASTS_SPELL_ON_TARGET == true then
set casterDummy = CreateUnit(localLightningBlast.casterOwner,CASTER_DUMMY_ID,/*
*/GetUnitX(enemy),/*
*/GetUnitY(enemy),/*
*/bj_RADTODEG*Atan2(GetUnitY(enemy)-GetUnitY(localLightningBlast.Missiles[Missilecount]),GetUnitX(enemy)-GetUnitX(localLightningBlast.Missiles[Missilecount])))
call UnitAddAbility(casterDummy, CASTER_DUMMY_ABIL_ID)
if SPELL_HAS_LEVELS == true then
call SetUnitAbilityLevel(casterDummy, CASTER_DUMMY_ABIL_ID, localLightningBlast.dummySpellLevel)
endif
call IssueTargetOrder(casterDummy, CASTER_DUMMY_ORDER, enemy)
if HAS_DUMMY_CAST_FX == true then
set dummycastFX = AddSpecialEffect(DUMMY_CAST_FX, GetUnitX(casterDummy),GetUnitY(casterDummy) )
call DestroyEffect(dummycastFX)
endif
call UnitApplyTimedLife(casterDummy, CASTER_DUMMY_EX_BUFF, DUMMY_EX_TIME)
endif
if CASTS_SPELL_ON_POINT == true then
set casterDummy = CreateUnit(localLightningBlast.casterOwner,CASTER_DUMMY_ID,/*
*/GetUnitX(enemy),/*
*/GetUnitY(enemy),/*
*/bj_RADTODEG*Atan2(GetUnitY(enemy)-GetUnitY(localLightningBlast.Missiles[Missilecount]),GetUnitX(enemy)-GetUnitX(localLightningBlast.Missiles[Missilecount])))
call UnitAddAbility(casterDummy, CASTER_DUMMY_ABIL_ID)
if SPELL_HAS_LEVELS == true then
call SetUnitAbilityLevel(casterDummy, CASTER_DUMMY_ABIL_ID, localLightningBlast.dummySpellLevel)
endif
call IssuePointOrder(casterDummy, CASTER_DUMMY_ORDER, GetUnitX(enemy), GetUnitY(enemy))
if HAS_DUMMY_CAST_FX == true then
set dummycastFX = AddSpecialEffect(DUMMY_CAST_FX, GetUnitX(casterDummy),GetUnitY(casterDummy) )
call DestroyEffect(dummycastFX)
endif
call UnitApplyTimedLife(casterDummy, CASTER_DUMMY_EX_BUFF, DUMMY_EX_TIME)
endif
if CASTS_SPELL_NOTARGET == true then
set casterDummy = CreateUnit(localLightningBlast.casterOwner,CASTER_DUMMY_ID,/*
*/GetUnitX(enemy),/*
*/GetUnitY(enemy),/*
*/bj_RADTODEG*Atan2(GetUnitY(enemy)-GetUnitY(localLightningBlast.Missiles[Missilecount]),GetUnitX(enemy)-GetUnitX(localLightningBlast.Missiles[Missilecount])))
call UnitAddAbility(casterDummy, CASTER_DUMMY_ABIL_ID)
if SPELL_HAS_LEVELS == true then
call SetUnitAbilityLevel(casterDummy, CASTER_DUMMY_ABIL_ID, localLightningBlast.dummySpellLevel)
endif
call IssueImmediateOrder(casterDummy, CASTER_DUMMY_ORDER)
if HAS_DUMMY_CAST_FX == true then
set dummycastFX = AddSpecialEffect(DUMMY_CAST_FX, GetUnitX(casterDummy),GetUnitY(casterDummy) )
call DestroyEffect(dummycastFX)
endif
call UnitApplyTimedLife(casterDummy, CASTER_DUMMY_EX_BUFF, DUMMY_EX_TIME)
endif
if FX_ON_COLLISION == true then
set collisionFX = AddSpecialEffectTarget(HIT_FX, enemy, FX_ATTACH_POINT)
call DestroyEffect(collisionFX)
endif
if DIES_ON_COLLISION == true then
if FX_ON_DEATH_MISSILE == true then
set collisionFX = AddSpecialEffect(DEATH_MISSILE_FX, GetUnitX(localLightningBlast.Missiles[Missilecount]),GetUnitY(localLightningBlast.Missiles[Missilecount]) )
call DestroyEffect(collisionFX)
endif
if FX_ON_DEATH_ENEMY == true then
set collisionFX = AddSpecialEffectTarget(DEATH_ENEMY_FX, enemy, DEATH_FX_ATTACH_POINT)
call DestroyEffect(collisionFX)
endif
if MISSILES_EXPLODE_ONHIT == true then
set localLightningBlast.Missilesexplode = true
endif
set localLightningBlast.distance = localLightningBlast.maxDist
endif
if INDIVID_DIE_ONHIT == true then
if FX_ON_DEATH_MISSILE == true then
set collisionFX = AddSpecialEffect(DEATH_MISSILE_FX, GetUnitX(localLightningBlast.Missiles[Missilecount]),GetUnitY(localLightningBlast.Missiles[Missilecount]) )
call DestroyEffect(collisionFX)
endif
if FX_ON_DEATH_ENEMY == true then
set collisionFX = AddSpecialEffectTarget(DEATH_ENEMY_FX, enemy, DEATH_FX_ATTACH_POINT)
call DestroyEffect(collisionFX)
endif
if MISSILES_EXPLODE_ONHIT == true then
set localLightningBlast.Missilesexplode = true
endif
// Here's where I'm trying to implement the good stuff
call DestroyLightning(localLightningBlast.beams[Missilecount])
set localLightningBlast.beams[Missilecount] = null
//
if localLightningBlast.Missilesexplode == false then
call RemoveUnit(localLightningBlast.Missiles[Missilecount])
endif
if localLightningBlast.Missilesexplode == true then
call KillUnit(localLightningBlast.Missiles[Missilecount])
endif
set localLightningBlast.Missiles[Missilecount] = null
endif
endif
call GroupRemoveUnit(LightningBlastEnumGroup, enemy)
endloop
set Missilecount = Missilecount + 1
endloop
set Missilecount = 0
set localLightningBlast.distance = localLightningBlast.distance + MISSILE_MOVESPEED
endif
if localLightningBlast.distance >= localLightningBlast.maxDist then
set LightningBlastInstances = LightningBlastInstances - 1
set LightningBlastArray[i] = LightningBlastArray[LightningBlastInstances]
call DestroyGroup(localLightningBlast.LightningBlastTargets)
call localLightningBlast.destroy()
endif
call RemoveRect(area)
call DestroyBoolExpr(TreeCheck)
set area = null
set casterDummy = null
set zLoc = null
set trailFX = null
set collisionFX = null
set dummycastFX = null
set i = i + 1
endloop
if LightningBlastInstances == 0 then
call PauseTimer(LightningBlastTimer)
endif
endmethod
static method create takes unit whichUnit, real range returns LightningBlastData
local integer i = 0
local LightningBlastData data = LightningBlastData.allocate()
local effect casterFX
local location targetLoc = GetSpellTargetLoc()
local real targetLocX = GetLocationX(targetLoc)
local real targetLocY = GetLocationY(targetLoc)
local real MissileFacing = (bj_RADTODEG*Atan2(targetLocY-GetUnitY(whichUnit),targetLocX-GetUnitX(whichUnit))) - CAST_ANGLE_OFFSET
if IS_TARGETED == false then
set targetLoc = Location(GetUnitX(whichUnit), GetUnitY(whichUnit))
set targetLocX = GetLocationX(targetLoc)
set targetLocY = GetLocationY(targetLoc)
set MissileFacing = GetUnitFacing(whichUnit) - CAST_ANGLE_OFFSET
endif
if IS_UNIT_TARGETED == true then
set data.targetedunit = GetSpellTargetUnit()
set targetLoc = Location(GetUnitX(data.targetedunit), GetUnitY(data.targetedunit))
set targetLocX = GetLocationX(targetLoc)
set targetLocY = GetLocationY(targetLoc)
set MissileFacing = (bj_RADTODEG*Atan2(targetLocY-GetUnitY(whichUnit),targetLocX-GetUnitX(whichUnit))) - CAST_ANGLE_OFFSET
endif
set data.LightningBlastTargets = CreateGroup()
set data.caster = whichUnit
set data.casterLevel = GetUnitAbilityLevel(data.caster, ABIL_ID)
if SPELL_HAS_LEVELS == true then
set data.dummySpellLevel = GetUnitAbilityLevel(data.caster, ABIL_ID)
endif
set data.casterOwner = GetOwningPlayer(data.caster)
set data.distance = 0.
set data.maxDist = range + (RANGE_PER_LEVEL * data.casterLevel)
set data.transparency = 1.
set data.Missilesexplode = MISSILES_EXPLODE
set casterFX = AddSpecialEffect(CAST_FX, GetUnitX(whichUnit), GetUnitY(whichUnit))
call DestroyEffect(casterFX)
set casterFX = null
loop
exitwhen i == MISSILE_NUMBER
call SetUnitPathing(data.caster, false)
set data.Missiles[i] = CreateUnit(data.casterOwner, DUMMY_ID, GetUnitX(data.caster), GetUnitY(data.caster), (MissileFacing))
call SetUnitPathing(data.Missiles[i], false)
call SetUnitX(data.Missiles[i], GetUnitX(data.caster))
call SetUnitY(data.Missiles[i], GetUnitY(data.caster))
call SetUnitPathing(data.caster, true)
call UnitAddAbility(data.Missiles[i], 'Amrf')
call UnitRemoveAbility(data.Missiles[i], 'Amrf')
call SetUnitVertexColor(data.Missiles[i], 255, 255, 255, 255)
set MissileFacing = MissileFacing+(MISSILE_ANGLE - (MISSILE_ANGLE_LVL_REDUC * data.casterLevel))
call SetUnitFacing(data.Missiles[i], MissileFacing)
if IS_CIRCLE == true then
set data.beams[0] = AddLightningEx(LIGHTNING_FX, true, GetUnitX(data.Missiles[0]), GetUnitY(data.Missiles[0]), FX_Z, GetUnitX(data.Missiles[i]), GetUnitY(data.Missiles[i]), FX_Z)
endif
if i != 0 then
set data.beams[i] = AddLightningEx(LIGHTNING_FX, true, GetUnitX(data.Missiles[i-1]), GetUnitY(data.Missiles[i-1]), FX_Z, GetUnitX(data.Missiles[i]), GetUnitY(data.Missiles[i]), FX_Z)
endif
set i = i + 1
endloop
set LightningBlastArray[LightningBlastInstances] = data
if LightningBlastInstances <= 0 then
call TimerStart(LightningBlastTimer, TIMING_INTERVAL, true, function LightningBlastData.LightningBlastExecution)
endif
set LightningBlastInstances = LightningBlastInstances + 1
set targetLoc = null
return data
endmethod
method onDestroy takes nothing returns nothing
local integer i = 0
if .Missilesexplode == false then
loop
exitwhen i == MISSILE_NUMBER
call RemoveUnit(.Missiles[i])
set .Missiles[i] = null
set i = i + 1
endloop
endif
if .Missilesexplode == true then
loop
exitwhen i == MISSILE_NUMBER
call KillUnit(.Missiles[i])
set .Missiles[i] = null
set i = i + 1
endloop
endif
set i = 0
loop
exitwhen i == MISSILE_NUMBER
call DestroyLightning(.beams[i])
set .beams[i] = null
set i = i + 1
endloop
set .caster = null
set .targetedunit = null
set .casterOwner = null
set .LightningBlastTargets = null
endmethod
endstruct
//===================================/ Condition /===========================================================
private function checkLightningBlast takes nothing returns boolean
local LightningBlastData localLightningBlast
if GetSpellAbilityId() == ABIL_ID then
set localLightningBlast = LightningBlastData.create(GetTriggerUnit(), CAST_RANGE)
endif
return false
endfunction
//===================================/ Init /================================================================
private function init takes nothing returns nothing
local trigger localTrigVar = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(localTrigVar, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition( localTrigVar, Condition(function checkLightningBlast))
set localTrigVar = null
endfunction
endscope