- Joined
- Jul 26, 2008
- Messages
- 1,009
I'm using XE Colliders for this because my math for making visual effects is useless. I grabbed the example spell and did some editing with my best understanding of vJASS. Of course the spell has some major issues, as it became a bigger undertaking than I expected any help would be appreciated and hopefully lead me to greater knowledge of vJASS
For Earth I used Rising Dusk's knockback, with slight mod, and for Frost I used EarthFury's bonusmod. Wind uses XEcast.
The tooltip:
Fire 5/7/9 arrows randomly imbued with elemental attributes.
Fire - Splash damage.
Earth - Knockback
Wind - Cyclone
Frost - Slow
Damage is determined by current damage bonus and main attribute.
For Earth I used Rising Dusk's knockback, with slight mod, and for Frost I used EarthFury's bonusmod. Wind uses XEcast.
The tooltip:
Fire 5/7/9 arrows randomly imbued with elemental attributes.
Fire - Splash damage.
Earth - Knockback
Wind - Cyclone
Frost - Slow
Damage is determined by current damage bonus and main attribute.
JASS:
scope elementalVolley initializer init
globals
private constant real DISTANCE = 50. //distance between each two fire balls.
private unit TEMP
endglobals
private struct elementalEarth extends xecollider
unit owner
method onUnitHit takes unit target returns nothing
local real a = 57.29582 * Atan2(GetUnitY(target) - GetUnitY(this.owner), GetUnitX(target) - GetUnitX(this.owner))
local real dam = SpellStat(this.owner,true)/2 + GetUnitBonus(this.owner, BONUS_DAMAGE)
// Don't explode if the owner is "hit", often when creating the missile
// it would think it hit the unit that created it...
if (this.owner != target) and IsUnitType(target, UNIT_TYPE_STRUCTURE) == false and IsUnitEnemy(target, GetOwningPlayer(this.owner)) then
// perform the damage stuff, credit the owner.
call UnitDamageTarget(this.owner, target, dam , true, false, ATTACK_TYPE_PIERCE, DAMAGE_TYPE_NORMAL, wtype)
call KnockbackTarget(this.owner, target, a, 250., 400, true, true, false, "MDX//KnockbackDust.mdx")
call this.terminate()
endif
endmethod
endstruct
private struct elementalWind extends xecollider
unit owner
method onUnitHit takes unit target returns nothing
local real a = 57.29582 * Atan2(GetUnitY(target) - GetUnitY(this.owner), GetUnitX(target) - GetUnitX(this.owner))
local real dam = SpellStat(this.owner,true)/2 + GetUnitBonus(this.owner, BONUS_DAMAGE)
if (this.owner != target) and IsUnitType(target, UNIT_TYPE_STRUCTURE) == false and IsUnitEnemy(target, GetOwningPlayer(this.owner)) then
call UnitDamageTarget( this.owner, target, dam , true, false, ATTACK_TYPE_PIERCE, DAMAGE_TYPE_NORMAL, wtype)
call CasterCastAbility(GetOwningPlayer(this.owner), 'A009', "cyclone", target, true)
call this.terminate()
endif
endmethod
endstruct
private struct elementalFire extends xecollider
unit owner
static method explosion takes nothing returns boolean
local real dam = SpellStat(TEMP,true)/4 + GetUnitBonus(TEMP, BONUS_DAMAGE)/2
if IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TEMP)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false then
call UnitDamageTarget( TEMP, GetFilterUnit(), dam , true, false, ATTACK_TYPE_PIERCE, DAMAGE_TYPE_MAGIC, wtype)
return true
endif
return false
endmethod
method onUnitHit takes unit target returns nothing
local real dam = SpellStat(this.owner,true)/2 + GetUnitBonus(this.owner, BONUS_DAMAGE)
local group g = NewGroup()
if (this.owner != target) and IsUnitType(target, UNIT_TYPE_STRUCTURE) == false and IsUnitEnemy(target, GetOwningPlayer(this.owner)) then
call UnitDamageTarget( this.owner, target, dam , true, false, ATTACK_TYPE_PIERCE, DAMAGE_TYPE_NORMAL, wtype)
set TEMP = this.owner
call GroupEnumUnitsInRange(g, GetUnitX(this.owner), GetUnitY(this.owner), 300, Filter(function elementalFire.explosion))
call ReleaseGroup(g)
call this.terminate()
endif
endmethod
endstruct
private struct elementalFrost extends xecollider
unit owner
unit t
static method onLoop takes nothing returns nothing
local timer tim = GetExpiredTimer()
local elementalFrost d = elementalFrost(GetTimerData(tim))
call AddUnitBonus(d.t, BONUS_ATTACK_SPEED, 25)
call AddUnitBonus(d.t, BONUS_MOVEMENT_SPEED, 50)
call d.terminate()
call ReleaseTimer(tim)
endmethod
static method freeze takes unit target returns elementalFrost
local elementalFrost d = elementalFrost.allocate(GetUnitX(target),GetUnitY(target),300)
local timer tim = NewTimer()
set d.t = target
call AddUnitBonus(target, BONUS_ATTACK_SPEED, -25)
call AddUnitBonus(target, BONUS_MOVEMENT_SPEED, -50)
call SetTimerData(tim, d)
call TimerStart(tim, 1.5, false, function elementalFrost.onLoop)
return d
endmethod
method onUnitHit takes unit target returns nothing
local real dam = SpellStat(this.owner,true)/2 + GetUnitBonus(this.owner, BONUS_DAMAGE)
if (this.owner != target) and IsUnitType(target, UNIT_TYPE_STRUCTURE) == false and IsUnitEnemy(target, GetOwningPlayer(this.owner)) then
call UnitDamageTarget( this.owner, target, dam , true, false, ATTACK_TYPE_PIERCE, DAMAGE_TYPE_NORMAL, wtype)
call elementalFrost.freeze(target)
call this.terminate()
endif
endmethod
endstruct
private function onSpellCast takes nothing returns nothing
local unit u = GetTriggerUnit()
local location loc = GetSpellTargetLoc()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real ang = Atan2( GetLocationY(loc) - y, GetLocationX(loc) - x)
local real ang2
local integer i
local integer i2
local real mx
local real my
local integer FIRE_BALL_COUNT = 3 + 2 * GetUnitAbilityLevel(u, 'ElVo')
local elementalEarth xc //notice our variable uses the greenMissile type
// ang2 would be perpendicular to the other angle, we are using radians so to get
// ang2 we add PI/2 which is equivalent to 90 degrees.
set ang2 = ang + bj_PI/2
set i=0 //
loop // we are creating two missiles but that's too much hassle and repetitive
exitwhen (i==FIRE_BALL_COUNT) // code, just use a loop and one trick for the angle you shall see bellow
if ( (i==FIRE_BALL_COUNT-1) and ( ModuloInteger(FIRE_BALL_COUNT,2)==1) ) then
set mx = x // When FIRE_BALL_COUNT is odd, create the last fire ball
set my = y // right in the middle.
else
set mx = x + (DISTANCE*(i/2+1))*Cos(ang2) //Getting the position for the missile using a polar
set my = y + (DISTANCE*(i/2+1))*Sin(ang2) //projections. Increase distance by 75 every 2 steps.
endif
set i2 = GetRandomInt(1,4)
if i2 == 1 then
set xc = elementalEarth.create(mx, my, ang)
call xc.flash( "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl" )
elseif i2 == 2 then
set xc = elementalFrost.create(mx, my, ang)
call xc.flash( "Abilities\\Weapons\\LichMissile\\LichMissile.mdl" )
elseif i2 == 3 then
set xc = elementalFire.create(mx, my, ang)
call xc.flash( "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl" )
elseif i2 == 4 then
set xc = elementalWind.create(mx, my, ang)
call xc.flash( "Abilities\\Spells\\Human\\Thunderclap\\ThunderclapTarget.mdl" )
endif
set xc.fxpath = "Abilities\\Weapons\\Arrow\\ArrowMissile.mdl"
set xc.speed = 1000.0 // The missile starts with a speed of 0, acceleration of 1000
set xc.expirationTime = 1.25 //20000.0 // it expires after 1 seconds.
// So, try visualizing those things
set xc.z = 50.0 // some height, since missiles often fly...
set xc.owner = u // important stuff, see how our collider is using the greenMissile struct
// so we can assign the owner, as you saw above the owner determines
// what can hit the missile and who to credit the damage for.
set ang2= bj_PI + ang2 //this is the trick, the second angle will be the opposite to the
// current one.
set i=i+1
endloop
call RemoveLocation(loc) //We need to clean the point.
set u=null
set loc=null // good idea to null handle local variables.
endfunction
private function spellIdMatch takes nothing returns boolean
return (GetSpellAbilityId()=='ElVo')
endfunction
private function init takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerAddCondition(t, Condition( function spellIdMatch) )
call TriggerAddAction(t, function onSpellCast)
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
set t=null
endfunction
endscope