- Joined
- Sep 25, 2005
- Messages
- 71
EDIT: Resolved. Apparently it's bad to RemoveLocation something you're going to use.
I've sort of coded myself into a weird position here. I tried using a correctly-functioning spell projectile framework and added on functionality (calls an aoe-based struct at the location of the projectile.)
The projectile framework worked fine, but the moment I added in if's for whether it's an aoe, or set it to aoe on death, something changed. I literally can't figure how this crap would affect my instancing.
[youtube]
http://www.youtube.com/watch?v=qx5AF8A3wyM
[/youtube]
For the sake of thoroughness, here's the Fireblast:
Though considering the blasts work, not sure how it'd affect the fireballs unless something overlapped.
I've sort of coded myself into a weird position here. I tried using a correctly-functioning spell projectile framework and added on functionality (calls an aoe-based struct at the location of the projectile.)
The projectile framework worked fine, but the moment I added in if's for whether it's an aoe, or set it to aoe on death, something changed. I literally can't figure how this crap would affect my instancing.
[youtube]
http://www.youtube.com/watch?v=qx5AF8A3wyM
[/youtube]
JASS:
//==================================/ Projectile Framework /=================================================//
// //
// //
// //
//===========================================================================================================//
scope CorrectFireballs initializer init
globals
//Number of missiles; must be 1 or greater
private constant integer MISSILE_NUMBER = 3
//Which ability triggers the missiles
private constant integer ABIL_ID = 'A008'
//Which unit to use as a missile dummy
private constant integer DUMMY_ID = 'o003'
//Which unit to use as a caster dummy if casts spell on hit units
private constant integer CASTER_DUMMY_ID = 0
//Which ability do dummies cast
private constant integer CASTER_DUMMY_ABIL_ID = 0
//Which buff to use for expiration timer on dummies
private constant integer DUMMY_EX_BUFF = 'B001'
//If some missiles are hidden, sets interval for which are hidden; must be 1 or greater
private constant integer UNIT_HIDE_INTERVAL = 1
//Which FX to use on caster on cast
private constant string CAST_FX = null
//Which FX to use on dummy on cast
private constant string DUMMY_CAST_FX = null
//Which FX to use when a target is hit
private constant string HIT_FX = null
//Which FX to use on the missile when killed
private constant string DEATH_MISSILE_FX = null
//Which FX to use on the target when missile is killed
private constant string DEATH_ENEMY_FX = null
//Which FX to use as a trail trail is enabled
private constant string TRAIL_FX = "Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl"
//Which FX to use as lightning
private constant string LIGHTNING_FX = null
//Which attach point to use for FX on targets
private constant string FX_ATTACH_POINT = null
//Which attach point to use for FX on target when missile is killed
private constant string DEATH_FX_ATTACH_POINT = null
//Which order string to use for dummy casters
private constant string CASTER_DUMMY_ORDER = null
//Projectile collision size
private constant real COLLISION_SIZE = 128.
//Dummy expiration timer duration
private constant real DUMMY_EX_TIME = 1.
//Colors dummy/lightning
private constant real FX_RED = 1.
private constant real FX_GREEN = 1.
private constant real FX_BLUE = 1.
//Max distance
private constant real CAST_RANGE = 650.
//Gained max distance per level
private constant real RANGE_PER_LEVEL = 0.
//What distance percent necessary to start transparency
private constant real TRANSPARENCY_THRESH = 1.
//How much transparency is added after threshold per tick
private constant real TRANSPARENCY_INCREMENT = 0.1
//Angle between missiles
private constant real MISSILE_ANGLE = 30.
//Reduction of angle between missiles per level
private constant real MISSILE_ANGLE_LVL_REDUC = 0.
//How far to move missiles per tick
private constant real MISSILE_MOVESPEED = 33.
//Z height for missiles/lightning
private constant real FX_Z = 72.
//Damage dealt if damage is toggled
private constant real DAMAGE_AMOUNT = 0.
//Damage dealt increase per level
private constant real DAMAGE_PER_LEVEL = 0.
//Tick speed
private constant real TIMING_INTERVAL = 0.02
//Sets the angle of the "initial" missile based off of missile number and angle
private constant real CAST_ANGLE_OFFSET = ((MISSILE_NUMBER * MISSILE_ANGLE) * .5) + (.5 * MISSILE_ANGLE)
//Sets whether the missile kills trees
private constant boolean KILLS_TREES = true
//Sets whether missile causes an AOE effect on contact
private constant boolean AOE = false //TBI
//Sets whether missile causes an AOE effect when reaching max distance or dying
private constant boolean AOE_ON_DEATH = false //TBI
//Sets whether to use DUMMY_CAST_FX
private constant boolean HAS_DUMMY_CAST_FX = false
//Sets whether to use TRAIL_FX
private constant boolean HAS_TRAIL_FX = true
//Sets whether to use HIT_FX
private constant boolean FX_ON_COLLISION = false
//Sets whether to use DEATH_MISSILE_FX when missile is killed
private constant boolean FX_ON_DEATH_MISSILE = false
//Sets whether to use DEATH_ENEMY_FX on target when missile is killed
private constant boolean FX_ON_DEATH_ENEMY = false
//Sets whether all missiles die when one collides with a valid target
private constant boolean DIES_ON_COLLISION = false
//Sets whether individual missiles die when colliding with a valid target
private constant boolean INDIVID_DIE_ONHIT = true
//Sets whether missiles are killed or removed
private constant boolean MISSILES_EXPLODE = false
//Sets whether missiles deal damage on hit
private constant boolean DEALS_DAMAGE = true
//Sets whether missiles create dummies that cast a spell on the target unit
private constant boolean CASTS_SPELL_ON_TARGET = false
//Sets whether missiles create dummies that cast a spell on the point of the target unit
private constant boolean CASTS_SPELL_ON_POINT = false
//Sets whether missiles create dummies that cast a spell without a target on the point of the target unit
private constant boolean CASTS_SPELL_NOTARGET = false
//Sets whether the spell dummies cast levels with the triggering spell
private constant boolean SPELL_HAS_LEVELS = false
//Sets whether the spell is targeted via point
private constant boolean IS_TARGETED = true
//Sets whether the spell is targeted via unit
private constant boolean IS_UNIT_TARGETED = false
//Sets whether the missiles have homing; only useful with IS_UNIT_TARGETED = true
private constant boolean HAS_HOMING = false
//Sets whether some missiles are hidden
private constant boolean HIDING_UNITS = false
//Sets whether missiles are hidden those every UNIT_HIDE_INTERVAL
private constant boolean HIDING_UNITS_INTERVAL = false
//Checks whether the missiles create a valid circle, allowing lightning FX to seal up properly
private constant boolean IS_CIRCLE = MISSILE_ANGLE * MISSILE_NUMBER == 360.
private timer CorrectFireballsTimer = CreateTimer()
private integer CorrectFireballsInstances = 0
private group CorrectFireballsEnumGroup = CreateGroup()
private CorrectFireballsData array CorrectFireballsArray
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
private function ValidAliveTargetGroupedGround takes unit enemy, unit caster, group notInGroup returns boolean
if IsUnitEnemy(enemy, GetOwningPlayer(caster)) == true /*
*/and IsUnitType(enemy, UNIT_TYPE_GROUND) == true /*
*/and IsUnitType(enemy,UNIT_TYPE_MAGIC_IMMUNE) == false /*
*/and IsUnitType(enemy,UNIT_TYPE_STRUCTURE) == false /*
*/and IsUnitType(enemy, UNIT_TYPE_MECHANICAL) == false /*
*/and GetWidgetLife(enemy) > 0.405 == true /*
*/and IsUnitInGroup(enemy, notInGroup) == false then
return true
endif
return false
endfunction
struct CorrectFireballsData
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 CorrectFireballsTargets
private static method CorrectFireballsExecution 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 CorrectFireballsData localCorrectFireballs
loop
exitwhen i >= CorrectFireballsInstances
set localCorrectFireballs = CorrectFireballsArray[i]
if localCorrectFireballs.distance < localCorrectFireballs.maxDist then
if localCorrectFireballs.transparency > 0. and (localCorrectFireballs.distance / localCorrectFireballs.maxDist) > TRANSPARENCY_THRESH then
set localCorrectFireballs.transparency = localCorrectFireballs.transparency - TRANSPARENCY_INCREMENT
endif
loop
exitwhen Missilecount == MISSILE_NUMBER
call MoveUnit(localCorrectFireballs.Missiles[Missilecount], MISSILE_MOVESPEED, GetUnitFacing(localCorrectFireballs.Missiles[Missilecount]))
if HAS_HOMING == true and IS_UNIT_TARGETED == true then
call SetUnitFacing(localCorrectFireballs.Missiles[Missilecount], /*
*/bj_RADTODEG*Atan2( (GetUnitY(localCorrectFireballs.targetedunit) ) - (GetUnitY ( localCorrectFireballs.Missiles[Missilecount] ) ), /*
*/(GetUnitX(localCorrectFireballs.targetedunit)-(GetUnitX(localCorrectFireballs.Missiles[Missilecount])))))
endif
set x = GetUnitX(localCorrectFireballs.Missiles[Missilecount])
set y = GetUnitY(localCorrectFireballs.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(localCorrectFireballs.Missiles[Missilecount], FX_Z, 0.)
if HAS_TRAIL_FX == true then
set trailFX = AddSpecialEffect(TRAIL_FX, GetUnitX(localCorrectFireballs.Missiles[Missilecount]), GetUnitY(localCorrectFireballs.Missiles[Missilecount]))
call DestroyEffect(trailFX)
endif
if ModuloInteger(Missilecount, UNIT_HIDE_INTERVAL) == 0 and HIDING_UNITS == true then
call SetUnitVertexColor(localCorrectFireballs.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(localCorrectFireballs.Missiles[Missilecount], R2I(255. * FX_RED), R2I(255. * FX_GREEN), R2I(255. * FX_BLUE), 0)
else
call SetUnitVertexColor(localCorrectFireballs.Missiles[Missilecount], R2I(255. * FX_RED), R2I(255. * FX_GREEN), R2I(255. * FX_BLUE), R2I(localCorrectFireballs.transparency*255))
endif
endif
if Missilecount != MISSILE_NUMBER then
call MoveLightningEx(localCorrectFireballs.beams[Missilecount], true, GetUnitX(localCorrectFireballs.Missiles[Missilecount-1]), GetUnitY(localCorrectFireballs.Missiles[Missilecount-1]), z[Missilecount-1], GetUnitX(localCorrectFireballs.Missiles[Missilecount]), GetUnitY(localCorrectFireballs.Missiles[Missilecount]), z[Missilecount])
if localCorrectFireballs.beams[0] != null then
call MoveLightningEx(localCorrectFireballs.beams[0], true, GetUnitX(localCorrectFireballs.Missiles[0]), GetUnitY(localCorrectFireballs.Missiles[0]), z[0], GetUnitX(localCorrectFireballs.Missiles[Missilecount]), GetUnitY(localCorrectFireballs.Missiles[Missilecount]), z[Missilecount])
endif
endif
call SetLightningColor(localCorrectFireballs.beams[Missilecount],FX_RED,FX_GREEN,FX_BLUE,localCorrectFireballs.transparency)
if KILLS_TREES == true then
call EnumDestructablesInRect(area, TreeCheck, function KillTree)
endif
call GroupEnumUnitsInRange(CorrectFireballsEnumGroup, GetUnitX(localCorrectFireballs.Missiles[Missilecount]), GetUnitY(localCorrectFireballs.Missiles[Missilecount]), COLLISION_SIZE, null)
loop
set enemy = FirstOfGroup(CorrectFireballsEnumGroup)
exitwhen enemy == null
if ValidAliveTargetGroupedGround(enemy, localCorrectFireballs.caster, localCorrectFireballs.CorrectFireballsTargets) == true then
call GroupAddUnit(localCorrectFireballs.CorrectFireballsTargets, enemy)
if DEALS_DAMAGE == true then
call UnitDamageTarget(localCorrectFireballs.caster, enemy, DAMAGE_AMOUNT + (DAMAGE_PER_LEVEL * localCorrectFireballs.casterLevel), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, null)
endif
if CASTS_SPELL_ON_TARGET == true then
set casterDummy = CreateUnit(localCorrectFireballs.casterOwner,CASTER_DUMMY_ID,/*
*/GetUnitX(enemy),/*
*/GetUnitY(enemy),/*
*/bj_RADTODEG*Atan2(GetUnitY(enemy)-GetUnitY(localCorrectFireballs.Missiles[Missilecount]),GetUnitX(enemy)-GetUnitX(localCorrectFireballs.Missiles[Missilecount])))
call UnitAddAbility(casterDummy, CASTER_DUMMY_ABIL_ID)
if SPELL_HAS_LEVELS == true then
call SetUnitAbilityLevel(casterDummy, CASTER_DUMMY_ABIL_ID, localCorrectFireballs.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, DUMMY_EX_BUFF, DUMMY_EX_TIME)
endif
if CASTS_SPELL_ON_POINT == true then
set casterDummy = CreateUnit(localCorrectFireballs.casterOwner,CASTER_DUMMY_ID,/*
*/GetUnitX(enemy),/*
*/GetUnitY(enemy),/*
*/bj_RADTODEG*Atan2(GetUnitY(enemy)-GetUnitY(localCorrectFireballs.Missiles[Missilecount]),GetUnitX(enemy)-GetUnitX(localCorrectFireballs.Missiles[Missilecount])))
call UnitAddAbility(casterDummy, CASTER_DUMMY_ABIL_ID)
if SPELL_HAS_LEVELS == true then
call SetUnitAbilityLevel(casterDummy, CASTER_DUMMY_ABIL_ID, localCorrectFireballs.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, DUMMY_EX_BUFF, DUMMY_EX_TIME)
endif
if CASTS_SPELL_NOTARGET == true then
set casterDummy = CreateUnit(localCorrectFireballs.casterOwner,CASTER_DUMMY_ID,/*
*/GetUnitX(enemy),/*
*/GetUnitY(enemy),/*
*/bj_RADTODEG*Atan2(GetUnitY(enemy)-GetUnitY(localCorrectFireballs.Missiles[Missilecount]),GetUnitX(enemy)-GetUnitX(localCorrectFireballs.Missiles[Missilecount])))
call UnitAddAbility(casterDummy, CASTER_DUMMY_ABIL_ID)
if SPELL_HAS_LEVELS == true then
call SetUnitAbilityLevel(casterDummy, CASTER_DUMMY_ABIL_ID, localCorrectFireballs.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, 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(localCorrectFireballs.Missiles[Missilecount]),GetUnitY(localCorrectFireballs.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
set localCorrectFireballs.distance = localCorrectFireballs.maxDist
endif
if INDIVID_DIE_ONHIT == true then
if FX_ON_DEATH_MISSILE == true then
set collisionFX = AddSpecialEffect(DEATH_MISSILE_FX, GetUnitX(localCorrectFireballs.Missiles[Missilecount]),GetUnitY(localCorrectFireballs.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 localCorrectFireballs.beams[Missilecount] != null then
call DestroyLightning(localCorrectFireballs.beams[Missilecount])
set localCorrectFireballs.beams[Missilecount] = null
endif
if Missilecount + 1 <= MISSILE_NUMBER then
if localCorrectFireballs.beams[Missilecount+1] != null then
call DestroyLightning(localCorrectFireballs.beams[Missilecount+1])
set localCorrectFireballs.beams[Missilecount+1] = null
endif
endif
if localCorrectFireballs.Missilesexplode == false then
call RemoveUnit(localCorrectFireballs.Missiles[Missilecount])
endif
if localCorrectFireballs.Missilesexplode == true then
call KillUnit(localCorrectFireballs.Missiles[Missilecount])
endif
set localCorrectFireballs.Missiles[Missilecount] = null
endif
endif
call GroupRemoveUnit(CorrectFireballsEnumGroup, enemy)
endloop
set Missilecount = Missilecount + 1
endloop
set Missilecount = 0
set localCorrectFireballs.distance = localCorrectFireballs.distance + MISSILE_MOVESPEED
endif
if localCorrectFireballs.distance >= localCorrectFireballs.maxDist then
set CorrectFireballsInstances = CorrectFireballsInstances - 1
set CorrectFireballsArray[i] = CorrectFireballsArray[CorrectFireballsInstances]
call DestroyGroup(localCorrectFireballs.CorrectFireballsTargets)
call localCorrectFireballs.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 CorrectFireballsInstances == 0 then
call PauseTimer(CorrectFireballsTimer)
endif
endmethod
static method create takes unit whichUnit, unit whichTarget, location whichPoint, integer level returns CorrectFireballsData
local integer i = 0
local CorrectFireballsData data = CorrectFireballsData.allocate()
local effect casterFX
local location targetLoc
local real targetLocX
local real targetLocY
local real MissileFacing
if IS_TARGETED == true then
set targetLoc = whichPoint
set targetLocX = GetLocationX(targetLoc)
set targetLocY = GetLocationY(targetLoc)
set MissileFacing = (bj_RADTODEG*Atan2(targetLocY-GetUnitY(whichUnit),targetLocX-GetUnitX(whichUnit))) - CAST_ANGLE_OFFSET
endif
if IS_TARGETED == false then
set targetLoc = Location(GetUnitX(whichTarget), GetUnitY(whichTarget))
set targetLocX = GetLocationX(targetLoc)
set targetLocY = GetLocationY(targetLoc)
set MissileFacing = (bj_RADTODEG*Atan2(targetLocY-GetUnitY(whichUnit),targetLocX-GetUnitX(whichUnit))) - CAST_ANGLE_OFFSET
endif
if IS_UNIT_TARGETED == true then
set data.targetedunit = whichTarget
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.CorrectFireballsTargets = CreateGroup()
set data.caster = whichUnit
set data.casterLevel = level
if SPELL_HAS_LEVELS == true then
set data.dummySpellLevel = level
endif
set data.casterOwner = GetOwningPlayer(data.caster)
set data.distance = 0.
set data.maxDist = CAST_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
if data.beams[0] == null 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
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 CorrectFireballsArray[CorrectFireballsInstances] = data
if CorrectFireballsInstances <= 0 then
call TimerStart(CorrectFireballsTimer, TIMING_INTERVAL, true, function CorrectFireballsData.CorrectFireballsExecution)
endif
set CorrectFireballsInstances = CorrectFireballsInstances + 1
call RemoveLocation(targetLoc)
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
if .beams[i] != null then
call DestroyLightning(.beams[i])
set .beams[i] = null
endif
set i = i + 1
endloop
set .caster = null
set .targetedunit = null
set .casterOwner = null
set .CorrectFireballsTargets = null
endmethod
endstruct
//===================================/ Condition /===========================================================
private function checkCorrectFireballs takes nothing returns boolean
local CorrectFireballsData localCorrectFireballs
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local location targetpoint = GetSpellTargetLoc()
if GetSpellAbilityId() == ABIL_ID then
set localCorrectFireballs = CorrectFireballsData.create(caster, target, targetpoint, GetUnitAbilityLevel(caster, ABIL_ID))
endif
call RemoveLocation(targetpoint)
set caster = null
set target = null
set targetpoint = null
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 checkCorrectFireballs))
set localTrigVar = null
endfunction
endscope
JASS:
//==================================/ Projectile Framework /=================================================//
// //
// //
// //
//===========================================================================================================//
scope IncorrectFireballs initializer init
globals
//Number of missiles; must be 1 or greater
private constant integer MISSILE_NUMBER = 3
//Which ability triggers the missiles
private constant integer ABIL_ID = 'A008'
//Which unit to use as a missile dummy
private constant integer DUMMY_ID = 'o003'
//Which unit to use as a caster dummy if casts spell on hit units
private constant integer CASTER_DUMMY_ID = 0
//Which ability do dummies cast
private constant integer CASTER_DUMMY_ABIL_ID = 0
//Which buff to use for expiration timer on dummies
private constant integer DUMMY_EX_BUFF = 'B001'
//If some missiles are hidden, sets interval for which are hidden; must be 1 or greater
private constant integer UNIT_HIDE_INTERVAL = 1
//Which FX to use on caster on cast
private constant string CAST_FX = null
//Which FX to use on dummy on cast
private constant string DUMMY_CAST_FX = null
//Which FX to use when a target is hit
private constant string HIT_FX = null
//Which FX to use on the missile when killed
private constant string DEATH_MISSILE_FX = null
//Which FX to use on the target when missile is killed
private constant string DEATH_ENEMY_FX = null
//Which FX to use as a trail trail is enabled
private constant string TRAIL_FX = "Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl"
//Which FX to use as lightning
private constant string LIGHTNING_FX = null
//Which attach point to use for FX on targets
private constant string FX_ATTACH_POINT = null
//Which attach point to use for FX on target when missile is killed
private constant string DEATH_FX_ATTACH_POINT = null
//Which order string to use for dummy casters
private constant string CASTER_DUMMY_ORDER = null
//Projectile collision size
private constant real COLLISION_SIZE = 128.
//Dummy expiration timer duration
private constant real DUMMY_EX_TIME = 1.
//Colors dummy/lightning
private constant real FX_RED = 1.
private constant real FX_GREEN = 1.
private constant real FX_BLUE = 1.
//Max distance
private constant real CAST_RANGE = 650.
//Gained max distance per level
private constant real RANGE_PER_LEVEL = 0.
//What distance percent necessary to start transparency
private constant real TRANSPARENCY_THRESH = 1.
//How much transparency is added after threshold per tick
private constant real TRANSPARENCY_INCREMENT = 0.1
//Angle between missiles
private constant real MISSILE_ANGLE = 30.
//Reduction of angle between missiles per level
private constant real MISSILE_ANGLE_LVL_REDUC = 0.
//How far to move missiles per tick
private constant real MISSILE_MOVESPEED = 33.
//Z height for missiles/lightning
private constant real FX_Z = 72.
//Damage dealt if damage is toggled
private constant real DAMAGE_AMOUNT = 0.
//Damage dealt increase per level
private constant real DAMAGE_PER_LEVEL = 0.
//Tick speed
private constant real TIMING_INTERVAL = 0.02
//Sets the angle of the "initial" missile based off of missile number and angle
private constant real CAST_ANGLE_OFFSET = ((MISSILE_NUMBER * MISSILE_ANGLE) * .5) + (.5 * MISSILE_ANGLE)
//Sets whether the missile kills trees
private constant boolean KILLS_TREES = true
//Sets whether missile causes an AOE effect on contact
private constant boolean AOE = false //TBI
//Sets whether missile causes an AOE effect when reaching max distance or dying
private constant boolean AOE_ON_DEATH = true //TBI
//Sets whether to use DUMMY_CAST_FX
private constant boolean HAS_DUMMY_CAST_FX = false
//Sets whether to use TRAIL_FX
private constant boolean HAS_TRAIL_FX = true
//Sets whether to use HIT_FX
private constant boolean FX_ON_COLLISION = false
//Sets whether to use DEATH_MISSILE_FX when missile is killed
private constant boolean FX_ON_DEATH_MISSILE = false
//Sets whether to use DEATH_ENEMY_FX on target when missile is killed
private constant boolean FX_ON_DEATH_ENEMY = false
//Sets whether all missiles die when one collides with a valid target
private constant boolean DIES_ON_COLLISION = false
//Sets whether individual missiles die when colliding with a valid target
private constant boolean INDIVID_DIE_ONHIT = true
//Sets whether missiles are killed or removed
private constant boolean MISSILES_EXPLODE = false
//Sets whether missiles deal damage on hit
private constant boolean DEALS_DAMAGE = true
//Sets whether missiles create dummies that cast a spell on the target unit
private constant boolean CASTS_SPELL_ON_TARGET = false
//Sets whether missiles create dummies that cast a spell on the point of the target unit
private constant boolean CASTS_SPELL_ON_POINT = false
//Sets whether missiles create dummies that cast a spell without a target on the point of the target unit
private constant boolean CASTS_SPELL_NOTARGET = false
//Sets whether the spell dummies cast levels with the triggering spell
private constant boolean SPELL_HAS_LEVELS = false
//Sets whether the spell is targeted via point
private constant boolean IS_TARGETED = true
//Sets whether the spell is targeted via unit
private constant boolean IS_UNIT_TARGETED = false
//Sets whether the missiles have homing; only useful with IS_UNIT_TARGETED = true
private constant boolean HAS_HOMING = false
//Sets whether some missiles are hidden
private constant boolean HIDING_UNITS = false
//Sets whether missiles are hidden those every UNIT_HIDE_INTERVAL
private constant boolean HIDING_UNITS_INTERVAL = false
//Checks whether the missiles create a valid circle, allowing lightning FX to seal up properly
private constant boolean IS_CIRCLE = MISSILE_ANGLE * MISSILE_NUMBER == 360.
private timer IncorrectFireballsTimer = CreateTimer()
private integer IncorrectFireballsInstances = 0
private group IncorrectFireballsEnumGroup = CreateGroup()
private IncorrectFireballsData array IncorrectFireballsArray
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
private function ValidAliveTargetGroupedGround takes unit enemy, unit caster, group notInGroup returns boolean
if IsUnitEnemy(enemy, GetOwningPlayer(caster)) == true /*
*/and IsUnitType(enemy, UNIT_TYPE_GROUND) == true /*
*/and IsUnitType(enemy,UNIT_TYPE_MAGIC_IMMUNE) == false /*
*/and IsUnitType(enemy,UNIT_TYPE_STRUCTURE) == false /*
*/and IsUnitType(enemy, UNIT_TYPE_MECHANICAL) == false /*
*/and GetWidgetLife(enemy) > 0.405 == true /*
*/and IsUnitInGroup(enemy, notInGroup) == false then
return true
endif
return false
endfunction
struct IncorrectFireballsData
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 IncorrectFireballsTargets
private static method IncorrectFireballsExecution takes nothing returns nothing
local boolexpr TreeCheck
local integer i = 0
local integer d
local integer Missilecount = 0
local location zLoc
local location aoeloc
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 IncorrectFireballsData localIncorrectFireballs
local FireblastData localFireblast
loop
exitwhen i >= IncorrectFireballsInstances
set localIncorrectFireballs = IncorrectFireballsArray[i]
if localIncorrectFireballs.distance < localIncorrectFireballs.maxDist then
if localIncorrectFireballs.transparency > 0. and (localIncorrectFireballs.distance / localIncorrectFireballs.maxDist) > TRANSPARENCY_THRESH then
set localIncorrectFireballs.transparency = localIncorrectFireballs.transparency - TRANSPARENCY_INCREMENT
endif
loop
exitwhen Missilecount == MISSILE_NUMBER
call MoveUnit(localIncorrectFireballs.Missiles[Missilecount], MISSILE_MOVESPEED, GetUnitFacing(localIncorrectFireballs.Missiles[Missilecount]))
if HAS_HOMING == true and IS_UNIT_TARGETED == true then
call SetUnitFacing(localIncorrectFireballs.Missiles[Missilecount], /*
*/bj_RADTODEG*Atan2( (GetUnitY(localIncorrectFireballs.targetedunit) ) - (GetUnitY ( localIncorrectFireballs.Missiles[Missilecount] ) ), /*
*/(GetUnitX(localIncorrectFireballs.targetedunit)-(GetUnitX(localIncorrectFireballs.Missiles[Missilecount])))))
endif
set x = GetUnitX(localIncorrectFireballs.Missiles[Missilecount])
set y = GetUnitY(localIncorrectFireballs.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(localIncorrectFireballs.Missiles[Missilecount], FX_Z, 0.)
if HAS_TRAIL_FX == true then
set trailFX = AddSpecialEffect(TRAIL_FX, GetUnitX(localIncorrectFireballs.Missiles[Missilecount]), GetUnitY(localIncorrectFireballs.Missiles[Missilecount]))
call DestroyEffect(trailFX)
endif
if ModuloInteger(Missilecount, UNIT_HIDE_INTERVAL) == 0 and HIDING_UNITS == true then
call SetUnitVertexColor(localIncorrectFireballs.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(localIncorrectFireballs.Missiles[Missilecount], R2I(255. * FX_RED), R2I(255. * FX_GREEN), R2I(255. * FX_BLUE), 0)
else
call SetUnitVertexColor(localIncorrectFireballs.Missiles[Missilecount], R2I(255. * FX_RED), R2I(255. * FX_GREEN), R2I(255. * FX_BLUE), R2I(localIncorrectFireballs.transparency*255))
endif
endif
if Missilecount != MISSILE_NUMBER then
call MoveLightningEx(localIncorrectFireballs.beams[Missilecount], true, GetUnitX(localIncorrectFireballs.Missiles[Missilecount-1]), GetUnitY(localIncorrectFireballs.Missiles[Missilecount-1]), z[Missilecount-1], GetUnitX(localIncorrectFireballs.Missiles[Missilecount]), GetUnitY(localIncorrectFireballs.Missiles[Missilecount]), z[Missilecount])
if localIncorrectFireballs.beams[0] != null then
call MoveLightningEx(localIncorrectFireballs.beams[0], true, GetUnitX(localIncorrectFireballs.Missiles[0]), GetUnitY(localIncorrectFireballs.Missiles[0]), z[0], GetUnitX(localIncorrectFireballs.Missiles[Missilecount]), GetUnitY(localIncorrectFireballs.Missiles[Missilecount]), z[Missilecount])
endif
endif
call SetLightningColor(localIncorrectFireballs.beams[Missilecount],FX_RED,FX_GREEN,FX_BLUE,localIncorrectFireballs.transparency)
if KILLS_TREES == true then
call EnumDestructablesInRect(area, TreeCheck, function KillTree)
endif
call GroupEnumUnitsInRange(IncorrectFireballsEnumGroup, GetUnitX(localIncorrectFireballs.Missiles[Missilecount]), GetUnitY(localIncorrectFireballs.Missiles[Missilecount]), COLLISION_SIZE, null)
loop
set enemy = FirstOfGroup(IncorrectFireballsEnumGroup)
exitwhen enemy == null
if ValidGroundTarget(enemy, localIncorrectFireballs.caster) == true then
call GroupAddUnit(localIncorrectFireballs.IncorrectFireballsTargets, enemy)
if DEALS_DAMAGE == true then
call UnitDamageTarget(localIncorrectFireballs.caster, enemy, DAMAGE_AMOUNT + (DAMAGE_PER_LEVEL * localIncorrectFireballs.casterLevel), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, null)
endif
if AOE == true then
set aoeloc = Location(GetUnitX(localIncorrectFireballs.Missiles[Missilecount]), GetUnitY(localIncorrectFireballs.Missiles[Missilecount]))
set localFireblast = FireblastData.create(localIncorrectFireballs.caster, aoeloc, GetUnitAbilityLevel(localIncorrectFireballs.caster, ABIL_ID) )
endif
if CASTS_SPELL_ON_TARGET == true then
set casterDummy = CreateUnit(localIncorrectFireballs.casterOwner,CASTER_DUMMY_ID,/*
*/GetUnitX(enemy),/*
*/GetUnitY(enemy),/*
*/bj_RADTODEG*Atan2(GetUnitY(enemy)-GetUnitY(localIncorrectFireballs.Missiles[Missilecount]),GetUnitX(enemy)-GetUnitX(localIncorrectFireballs.Missiles[Missilecount])))
call UnitAddAbility(casterDummy, CASTER_DUMMY_ABIL_ID)
if SPELL_HAS_LEVELS == true then
call SetUnitAbilityLevel(casterDummy, CASTER_DUMMY_ABIL_ID, localIncorrectFireballs.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, DUMMY_EX_BUFF, DUMMY_EX_TIME)
endif
if CASTS_SPELL_ON_POINT == true then
set casterDummy = CreateUnit(localIncorrectFireballs.casterOwner,CASTER_DUMMY_ID,/*
*/GetUnitX(enemy),/*
*/GetUnitY(enemy),/*
*/bj_RADTODEG*Atan2(GetUnitY(enemy)-GetUnitY(localIncorrectFireballs.Missiles[Missilecount]),GetUnitX(enemy)-GetUnitX(localIncorrectFireballs.Missiles[Missilecount])))
call UnitAddAbility(casterDummy, CASTER_DUMMY_ABIL_ID)
if SPELL_HAS_LEVELS == true then
call SetUnitAbilityLevel(casterDummy, CASTER_DUMMY_ABIL_ID, localIncorrectFireballs.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, DUMMY_EX_BUFF, DUMMY_EX_TIME)
endif
if CASTS_SPELL_NOTARGET == true then
set casterDummy = CreateUnit(localIncorrectFireballs.casterOwner,CASTER_DUMMY_ID,/*
*/GetUnitX(enemy),/*
*/GetUnitY(enemy),/*
*/bj_RADTODEG*Atan2(GetUnitY(enemy)-GetUnitY(localIncorrectFireballs.Missiles[Missilecount]),GetUnitX(enemy)-GetUnitX(localIncorrectFireballs.Missiles[Missilecount])))
call UnitAddAbility(casterDummy, CASTER_DUMMY_ABIL_ID)
if SPELL_HAS_LEVELS == true then
call SetUnitAbilityLevel(casterDummy, CASTER_DUMMY_ABIL_ID, localIncorrectFireballs.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, 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(localIncorrectFireballs.Missiles[Missilecount]),GetUnitY(localIncorrectFireballs.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
set localIncorrectFireballs.distance = localIncorrectFireballs.maxDist
endif
if INDIVID_DIE_ONHIT == true then
if FX_ON_DEATH_MISSILE == true then
set collisionFX = AddSpecialEffect(DEATH_MISSILE_FX, GetUnitX(localIncorrectFireballs.Missiles[Missilecount]),GetUnitY(localIncorrectFireballs.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 localIncorrectFireballs.beams[Missilecount] != null then
call DestroyLightning(localIncorrectFireballs.beams[Missilecount])
set localIncorrectFireballs.beams[Missilecount] = null
endif
if Missilecount + 1 <= MISSILE_NUMBER then
if localIncorrectFireballs.beams[Missilecount+1] != null then
call DestroyLightning(localIncorrectFireballs.beams[Missilecount+1])
set localIncorrectFireballs.beams[Missilecount+1] = null
endif
endif
if localIncorrectFireballs.Missilesexplode == false then
if AOE_ON_DEATH == true then
set aoeloc = Location(GetUnitX(localIncorrectFireballs.Missiles[Missilecount]), GetUnitY(localIncorrectFireballs.Missiles[Missilecount]))
set localFireblast = FireblastData.create(localIncorrectFireballs.caster, aoeloc, GetUnitAbilityLevel(localIncorrectFireballs.caster, ABIL_ID) )
endif
call RemoveUnit(localIncorrectFireballs.Missiles[Missilecount])
endif
if localIncorrectFireballs.Missilesexplode == true then
if AOE_ON_DEATH == true then
set aoeloc = Location(GetUnitX(localIncorrectFireballs.Missiles[Missilecount]), GetUnitY(localIncorrectFireballs.Missiles[Missilecount]))
set localFireblast = FireblastData.create(localIncorrectFireballs.caster, aoeloc, GetUnitAbilityLevel(localIncorrectFireballs.caster, ABIL_ID) )
endif
call KillUnit(localIncorrectFireballs.Missiles[Missilecount])
endif
set localIncorrectFireballs.Missiles[Missilecount] = null
endif
endif
call GroupRemoveUnit(IncorrectFireballsEnumGroup, enemy)
endloop
set Missilecount = Missilecount + 1
endloop
set Missilecount = 0
set localIncorrectFireballs.distance = localIncorrectFireballs.distance + MISSILE_MOVESPEED
endif
if localIncorrectFireballs.distance >= localIncorrectFireballs.maxDist then
set IncorrectFireballsInstances = IncorrectFireballsInstances - 1
set IncorrectFireballsArray[i] = IncorrectFireballsArray[IncorrectFireballsInstances]
call DestroyGroup(localIncorrectFireballs.IncorrectFireballsTargets)
call localIncorrectFireballs.destroy()
endif
call RemoveRect(area)
call DestroyBoolExpr(TreeCheck)
call RemoveLocation(aoeloc)
set area = null
set casterDummy = null
set zLoc = null
set aoeloc = null
set trailFX = null
set collisionFX = null
set dummycastFX = null
set i = i + 1
endloop
if IncorrectFireballsInstances == 0 then
call PauseTimer(IncorrectFireballsTimer)
endif
endmethod
static method create takes unit whichUnit, unit whichTarget, location whichPoint, integer level returns IncorrectFireballsData
local integer i = 0
local IncorrectFireballsData data = IncorrectFireballsData.allocate()
local effect casterFX
local location targetLoc
local real targetLocX
local real targetLocY
local real MissileFacing
if IS_TARGETED == true then
set targetLoc = whichPoint
set targetLocX = GetLocationX(targetLoc)
set targetLocY = GetLocationY(targetLoc)
set MissileFacing = (bj_RADTODEG*Atan2(targetLocY-GetUnitY(whichUnit),targetLocX-GetUnitX(whichUnit))) - CAST_ANGLE_OFFSET
endif
if IS_TARGETED == false then
set targetLoc = Location(GetUnitX(whichTarget), GetUnitY(whichTarget))
set targetLocX = GetLocationX(targetLoc)
set targetLocY = GetLocationY(targetLoc)
set MissileFacing = (bj_RADTODEG*Atan2(targetLocY-GetUnitY(whichUnit),targetLocX-GetUnitX(whichUnit))) - CAST_ANGLE_OFFSET
endif
if IS_UNIT_TARGETED == true then
set data.targetedunit = whichTarget
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.IncorrectFireballsTargets = CreateGroup()
set data.caster = whichUnit
set data.casterLevel = level
if SPELL_HAS_LEVELS == true then
set data.dummySpellLevel = level
endif
set data.casterOwner = GetOwningPlayer(data.caster)
set data.distance = 0.
set data.maxDist = CAST_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
if data.beams[0] == null 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
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 IncorrectFireballsArray[IncorrectFireballsInstances] = data
if IncorrectFireballsInstances <= 0 then
call TimerStart(IncorrectFireballsTimer, TIMING_INTERVAL, true, function IncorrectFireballsData.IncorrectFireballsExecution)
endif
set IncorrectFireballsInstances = IncorrectFireballsInstances + 1
call RemoveLocation(targetLoc)
set targetLoc = null
return data
endmethod
method onDestroy takes nothing returns nothing
local integer i = 0
local location aoeloc
local FireblastData localFireblast
if .Missilesexplode == false then
loop
exitwhen i == MISSILE_NUMBER
if AOE_ON_DEATH == true then
set aoeloc = Location(GetUnitX(.Missiles[i]), GetUnitY(.Missiles[i]))
set localFireblast = FireblastData.create(.caster, aoeloc, GetUnitAbilityLevel(.caster, ABIL_ID) )
endif
call RemoveUnit(.Missiles[i])
set .Missiles[i] = null
set i = i + 1
endloop
endif
if .Missilesexplode == true then
loop
exitwhen i == MISSILE_NUMBER
if AOE_ON_DEATH == true then
set aoeloc = Location(GetUnitX(.Missiles[i]), GetUnitY(.Missiles[i]))
set localFireblast = FireblastData.create(.caster, aoeloc, GetUnitAbilityLevel(.caster, ABIL_ID) )
endif
call KillUnit(.Missiles[i])
set .Missiles[i] = null
set i = i + 1
endloop
endif
set i = 0
loop
exitwhen i == MISSILE_NUMBER
if .beams[i] != null then
call DestroyLightning(.beams[i])
set .beams[i] = null
endif
set i = i + 1
endloop
call RemoveLocation(aoeloc)
set aoeloc = null
set .caster = null
set .targetedunit = null
set .casterOwner = null
set .IncorrectFireballsTargets = null
endmethod
endstruct
//===================================/ Condition /===========================================================
private function checkIncorrectFireballs takes nothing returns boolean
local IncorrectFireballsData localIncorrectFireballs
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local location targetpoint = GetSpellTargetLoc()
if GetSpellAbilityId() == ABIL_ID then
set localIncorrectFireballs = IncorrectFireballsData.create(caster, target, targetpoint, GetUnitAbilityLevel(caster, ABIL_ID))
endif
call RemoveLocation(targetpoint)
set caster = null
set target = null
set targetpoint = null
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 checkIncorrectFireballs))
set localTrigVar = null
endfunction
endscope
For the sake of thoroughness, here's the Fireblast:
JASS:
//==================================/ AOE /==================================================================
scope Fireblast initializer init
globals
private constant integer ABIL_ID = 'A00L'
private constant string FX_UNITS_AFFECTED = null
private constant string FX_UNITS_ATTACH = null
private constant string FX_POINT = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
private constant real AOE_SIZE = 200.
private constant real AOE_PER_LVL = 0.
private constant real DAMAGE_AMOUNT = 45.
private constant real DAMAGE_PER_LVL = 20.
private constant boolean DEALS_DAMAGE = true
private constant boolean HAS_UNIT_FX = false
private constant boolean HAS_POINT_FX = true
private group FireblastEnumGroup = CreateGroup()
endglobals
struct FireblastData
integer level
unit caster
group Fireblasttargets
real aoe
static method create takes unit caster, location spelltarget, integer spelllevel returns FireblastData
local FireblastData this = FireblastData.allocate()
local unit enemy
local effect pointFX
local effect unitFX
set .level = spelllevel
set .caster = caster
set .Fireblasttargets = CreateGroup()
set .aoe = AOE_SIZE + (AOE_PER_LVL*.level)
if HAS_POINT_FX == true then
set pointFX = AddSpecialEffectLoc(FX_POINT, spelltarget)
call DestroyEffect(pointFX)
endif
call GroupEnumUnitsInRangeOfLoc(FireblastEnumGroup, spelltarget, .aoe, null)
loop
set enemy = FirstOfGroup(FireblastEnumGroup)
exitwhen enemy == null
if ValidAliveTargetGroupedGround(enemy, .caster, .Fireblasttargets) == true then
call GroupAddUnit(.Fireblasttargets, enemy)
/* if HAS_UNIT_FX == true then
set unitFX = AddSpecialEffectTarget(FX_UNITS_AFFECTED, enemy, FX_UNITS_ATTACH)
call DestroyEffect(unitFX)
endif */
if DEALS_DAMAGE == true then
call UnitDamageTarget(.caster, enemy, DAMAGE_AMOUNT + (DAMAGE_PER_LVL*.level), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, null)
endif
endif
call GroupRemoveUnit(FireblastEnumGroup, enemy)
call DestroyGroup(.Fireblasttargets)
endloop
set pointFX = null
set unitFX =null
set enemy = null
call .destroy()
return this
endmethod
method onDestroy takes nothing returns nothing
set .Fireblasttargets = null
set .caster = null
endmethod
endstruct
private function checkFireblast takes nothing returns boolean
local FireblastData localFireblast
local unit casterunit = GetTriggerUnit()
local location targetloc = GetSpellTargetLoc()
if GetSpellAbilityId() == ABIL_ID then
set localFireblast = FireblastData.create(casterunit, targetloc, GetUnitAbilityLevel(casterunit, ABIL_ID))
endif
call RemoveLocation(targetloc)
set casterunit = null
set targetloc = null
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 checkFireblast))
set localTrigVar = null
endfunction
endscope
Though considering the blasts work, not sure how it'd affect the fireballs unless something overlapped.
Last edited: