I made my first spell and was wondering if anyone could give me some advice on things I did wrong/right here. I know this spell isn't MUI atm, and I'm assuming instantiating a new copy of targetLocs for each spell cast would achieve that so casts that occur while another is happening won't share targetLocs. A question I have is what's the difference between nulling a location and removing is using RemoveLocation() and such for other types as well, do they both stop leaks or no? Also for whatever reason I can't get the AncestralSpiritCaster model to appear, although as I type this I'm thinking I should've tried scaling up the model to see if it was just simply too small to be seen. Anyways I'm just looking for some general advice on how well this is coded, if I should've put something in a struct, or if I don't need this here, etc. Especially if I could've used XE better somewhere. I'll paste the spell below, thanks for looking. =)

JASS:
library ImpetuousHail initializer init requires xebasic, xepreload, xedamage, xecollider
globals
private constant integer SPELLID = 'A004'
private constant string SPELL_WARNING = "Abilities\\Spells\\Human\\FlameStrike\\FlameStrikeTarget.mdl"
private constant string SPELL_MISSILE = "Units\\Demon\\Infernal\\InfernalBirth.mdl"
private constant string SPELL_IMPACT = "Abilities\\Weapons\\SteamTank\\SteamTankImpact.mdl"
private constant real MISSILE_LIFETIME = .85
endglobals
private function setMaxTargets takes integer level returns integer
if (level == 1) then
return 3
else
return (level + 2)
endif
endfunction
private function getDamage takes integer level returns integer
return 80 * level
endfunction
private function getDamageFactor takes integer level returns real
if (level == 1) then
return 2
else
return 2 + (level * 1.0)
endif
endfunction
globals
private unit spellCaster
private integer targetCount = 0
private integer maxTargetCount = 0
private location array targetLocs[4]
private boolexpr getTargets
private xedamage damageOptions
endglobals
function onSpellEnd takes nothing returns nothing
local integer i = 0
loop
exitwhen i >= targetCount
call RemoveLocation(targetLocs[i])
set i = i + 1
endloop
set targetCount = 0
set spellCaster = null
endfunction
private function setDamageOptions takes xedamage xd returns nothing
set xd.dtype = DAMAGE_TYPE_FIRE
set xd.atype = ATTACK_TYPE_NORMAL
set xd.damageEnemies = true
set xd.damageAllies = true
set xd.damageNeutral = false
set xd.damageSelf = true
endfunction
private struct Missile extends xecollider
location target
integer level
method onDestroy takes nothing returns nothing
call SetUnitAnimation(spellCaster, "stand 2")
call DestroyEffect(AddSpecialEffectLoc(SPELL_IMPACT, this.target))
call damageOptions.damageAOE(spellCaster, GetLocationX(this.target), GetLocationY(this.target), 100, getDamage(.level))
set targetCount = targetCount - 1
if (targetCount <= 0) then
call onSpellEnd()
endif
endmethod
endstruct
private function isTarget takes nothing returns boolean
if ((damageOptions.allowedTarget(spellCaster, GetFilterUnit())) and (targetCount < maxTargetCount)) then
set targetLocs[targetCount] = GetUnitLoc(GetFilterUnit())
set targetCount = targetCount + 1
endif
return false
endfunction
private function onSpellEffect takes nothing returns nothing
local Missile m
local integer i = 0
local integer level
local group possibleTargets = CreateGroup()
local location spellTarget = GetSpellTargetLoc()
set spellCaster = GetTriggerUnit()
set level = GetUnitAbilityLevel(spellCaster, SPELLID)
set maxTargetCount = setMaxTargets(level)
call damageOptions.factor(UNIT_TYPE_STRUCTURE, getDamageFactor(level))
call GroupEnumUnitsInRange(possibleTargets, GetLocationX(spellTarget), GetLocationY(spellTarget), 400, getTargets)
call SetUnitAnimation(spellCaster, "attack 2")
call GroupClear(possibleTargets)
call DestroyEffect(AddSpecialEffectLoc(SPELL_WARNING, spellTarget))
loop
exitwhen i >= targetCount
set m = Missile.create(GetLocationX(targetLocs[i]), GetLocationY(targetLocs[i]), 0)
set m.level = level
set m.fxpath = SPELL_MISSILE
set m.expirationTime = MISSILE_LIFETIME
set m.target = targetLocs[i]
set i = i + 1
endloop
set spellTarget = null
endfunction
private function spellIDMatch takes nothing returns boolean
return (GetSpellAbilityId() == SPELLID)
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
set getTargets = Condition(function isTarget)
set damageOptions = xedamage.create()
call setDamageOptions(damageOptions)
call XE_PreloadAbility(SPELLID)
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function spellIDMatch))
call TriggerAddAction(t, function onSpellEffect)
set t = null
endfunction
endlibrary