Playing around with the WE today, and this is what happened:
Import Difficulty: Low
Units Affected: Enemy, ground, non-structure, non-magic immune
Target Type: Single unit - Enemy, ground, organic
Spell Info:
Magical energy consumes the targeted unit, throwing its equilibrium out of orbit. Enemies in its rotation path are thrust away, and both units take minor damage.
Level 1 - 2 second rotation, 20 collision damage.
Level 2 - 3 second rotation, 30 collision damage.
Level 3 - 4 second rotation, 40 collision damage.
Requirements:
Timer Utils
Group Utils
UnitAlive native
Would also be a good idea to make use of Bound Sentinel
Code
Import Difficulty: Low
Units Affected: Enemy, ground, non-structure, non-magic immune
Target Type: AoE - Enemy, ground, organic
Spell Info:
Summons a torrent of electrical activity at the target area. Pillars of energy attract all nearby enemies, then eject them in a powerful blast.
Level 1 - Small area, 100 damage.
Level 2 - Medium area, 100 damage.
Level 3 - Large area, 300 damage.
Requirements:
Timer Utils
Group Utils
Would also be a good idea to make use of Bound Sentinel
A capped limit of 100 units affected per instance of this spell is in effect. If this is a problem for someone, use less units ;Þ.
Code
Updates:
Twisteroo v1.01
Import Difficulty: Low
Units Affected: Enemy, ground, non-structure, non-magic immune
Target Type: Single unit - Enemy, ground, organic
Spell Info:
Magical energy consumes the targeted unit, throwing its equilibrium out of orbit. Enemies in its rotation path are thrust away, and both units take minor damage.
Level 1 - 2 second rotation, 20 collision damage.
Level 2 - 3 second rotation, 30 collision damage.
Level 3 - 4 second rotation, 40 collision damage.
Requirements:
Timer Utils
Group Utils
UnitAlive native
Would also be a good idea to make use of Bound Sentinel
Code
JASS:
scope Twisteroo initializer Init
// The main target is stunned via the storm bolt active for the duration of the spell. The stun duration can be changed via this ability
// in the object editor. You could also added effects to the main target, along with initial cast damage, etc. via the object editor.
// This spell requires the "UnitAlive" native. If this is not already in your map, declare it as seen in this map's custom script section.
// This spell also requires the GroupUtils and TimerUtils systems. Similarly, as with all movement based spells, it is a good idea to have
// the BoundSentinel system installed and operational in your map as well.
globals
// Rawcode for caster ability
private constant integer AbilId = 'A000'
// Effect to create at the feet of sliding units
private constant string SlideFX = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl"
// Attachment point for SlideFX
private constant string SlideFXAttachmentPoint = "origin"
// Effect to create at the feet of the main target on collision
private constant string CollisionFX = "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl"
// Attachment point for CollisionFX
private constant string CollisionFXAttachmentPoint = "origin"
// Interval for periodic timers. This doesn't really need to be changed, but its here nonetheless...
private constant real TimerInterval = .04
// Attack type for collision damage
private constant attacktype AttackType = ATTACK_TYPE_MAGIC
// Damage type for collision damage
private constant damagetype DamageType = DAMAGE_TYPE_MAGIC
// Whether you want the main unit to fade to black, then back again during the effects
private constant boolean DoVertexColorChange = true
endglobals
// Duration of the rotation effect
private constant function Duration takes integer lvl returns real
return 1.+lvl
endfunction
// Damage to main target and collidee
private constant function CollisionDamage takes integer lvl returns real
return 10.*lvl
endfunction
// Area of effect within which collision take place
private constant function CollisionAoE takes integer lvl returns real
return 125.
endfunction
// Angle between the main target and its initial location will increase at this rate per TimerInterval
private constant function RotationAngleIncrease takes integer lvl returns real
return 10.
endfunction
// Distance between the main target and its initial location will increase at this rate per TimerInterval
private constant function RotationDistanceIncrease takes integer lvl returns real
return 4.
endfunction
// Main targets post rotation slide speed will begin at this rate per TimerInterval
private constant function TargetSlideSpeedStart takes integer lvl returns real
return 35.
endfunction
// Main targets post rotation slide speed will decrease at this rate per TimerInterval
private constant function TargetSlideSpeedDeceleration takes integer lvl returns real
return 1.
endfunction
// Collidee targets collision slide speed will begin at this rate per TimerInterval
private constant function CollisionSlideSpeedStart takes integer lvl returns real
return 13.
endfunction
// Collidee targets collision slide speed will decrease at this rate per TimerInterval
private constant function CollisionSlideSpeedDeceleration takes integer lvl returns real
return .4
endfunction
//== No touching past this point ==\\
globals
private boolexpr B
private integer Temp_data
private unit Targ
endglobals
private struct data
unit cast
unit targ
player owner
timer t
integer lvl
real duration
real angle
real angle_change
real distance = 0.
real distance_change
real startx
real starty
real damage
real slide_distance
real collision
real decel
integer vertex = 255
integer vertex_interval
boolean switch = false
group g
method onDestroy takes nothing returns nothing
call ReleaseTimer(.t)
call ReleaseGroup(.g)
call SetUnitPathing(.targ,true)
if DoVertexColorChange then
call SetUnitVertexColor(.targ,255,255,255,255)
endif
endmethod
endstruct
private struct slide
unit targ
real distance
real cos
real sin
real decel
timer t
integer i
method onDestroy takes nothing returns nothing
local data d = .i
call ReleaseTimer(.t)
call GroupRemoveUnit(d.g,d.targ)
endmethod
endstruct
private function Slide takes nothing returns nothing
local slide s = GetTimerData(GetExpiredTimer())
call SetUnitPosition(s.targ,GetUnitX(s.targ)+s.distance*s.cos,GetUnitY(s.targ)+s.distance*s.sin)
call DestroyEffect(AddSpecialEffectTarget(SlideFX,s.targ,SlideFXAttachmentPoint))
set s.distance = s.distance - s.decel
if s.distance<s.decel then
call s.destroy()
endif
endfunction
private function Filt takes nothing returns boolean
local data d = Temp_data
local slide s
local real ang
set Targ = GetFilterUnit()
if Targ!=d.targ and not IsUnitInGroup(Targ,d.g) and IsUnitEnemy(Targ,d.owner) and UnitAlive(Targ) and IsUnitType(Targ,UNIT_TYPE_STRUCTURE)==false and IsUnitType(Targ,UNIT_TYPE_MAGIC_IMMUNE)==false then
call UnitDamageTarget(d.cast,d.targ,d.damage,false,false,AttackType,DamageType,null)
call UnitDamageTarget(d.cast,Targ,d.damage,false,false,AttackType,DamageType,null)
call DestroyEffect(AddSpecialEffectTarget(CollisionFX,d.targ,CollisionFXAttachmentPoint))
call GroupAddUnit(d.g,Targ)
set s = slide.create()
set s.targ = Targ
set ang = Atan2(GetUnitY(s.targ) - GetUnitY(d.targ), GetUnitX(s.targ) - GetUnitX(d.targ))
set s.cos = Cos(ang)
set s.sin = Sin(ang)
set s.distance = CollisionSlideSpeedStart(d.lvl)
set s.decel = CollisionSlideSpeedDeceleration(d.lvl)
set s.t = NewTimer()
set s.i = d
call SetTimerData(s.t,s)
call TimerStart(s.t,TimerInterval,true,function Slide)
endif
return false
endfunction
private function Effects takes nothing returns nothing
local data d = GetTimerData(GetExpiredTimer())
local real x
local real y
if d.duration<TimerInterval then
if not d.switch then
set d.switch = not d.switch
set d.angle = (d.angle+90.)*bj_DEGTORAD
if DoVertexColorChange then
set d.vertex_interval = R2I(255./(d.slide_distance/d.decel))
endif
endif
set x = GetUnitX(d.targ)+d.slide_distance*Cos(d.angle)
set y = GetUnitY(d.targ)+d.slide_distance*Sin(d.angle)
call SetUnitPosition(d.targ,x,y)
call DestroyEffect(AddSpecialEffectTarget(SlideFX,d.targ,SlideFXAttachmentPoint))
if DoVertexColorChange then
set d.vertex = d.vertex + d.vertex_interval
call SetUnitVertexColor(d.targ,d.vertex,d.vertex,d.vertex,255)
endif
set d.slide_distance = d.slide_distance - d.decel
if d.slide_distance<d.decel then
call d.destroy()
endif
else
set x = d.startx+d.distance*Cos(d.angle*bj_DEGTORAD)
set y = d.starty+d.distance*Sin(d.angle*bj_DEGTORAD)
call SetUnitX(d.targ,x)
call SetUnitY(d.targ,y)
if DoVertexColorChange then
set d.vertex = d.vertex - d.vertex_interval
call SetUnitVertexColor(d.targ,d.vertex,d.vertex,d.vertex,255)
endif
call GroupClear(ENUM_GROUP)
set Temp_data = d
call GroupEnumUnitsInArea(ENUM_GROUP, x, y, d.collision, B)
set d.angle = d.angle + d.angle_change
set d.distance = d.distance + d.distance_change
set d.duration = d.duration - TimerInterval
endif
endfunction
private function Actions takes nothing returns nothing
local data d = data.create()
set d.cast = GetTriggerUnit()
set d.targ = GetSpellTargetUnit()
set d.owner = GetOwningPlayer(d.cast)
set d.lvl = GetUnitAbilityLevel(d.cast,AbilId)
set d.duration = Duration(d.lvl)
set d.g = NewGroup()
set d.angle = GetRandomReal(0.,360.)
set d.angle_change = RotationAngleIncrease(d.lvl)
set d.distance_change = RotationDistanceIncrease(d.lvl)
set d.startx = GetUnitX(d.targ)
set d.starty = GetUnitY(d.targ)
set d.damage = CollisionDamage(d.lvl)
set d.collision = CollisionAoE(d.lvl)
set d.slide_distance = TargetSlideSpeedStart(d.lvl)
set d.decel = TargetSlideSpeedDeceleration(d.lvl)
if DoVertexColorChange then
set d.vertex_interval = R2I(255./(d.duration/TimerInterval))
endif
set d.t = NewTimer()
call SetUnitPathing(d.targ,false)
call SetTimerData(d.t,d)
call TimerStart(d.t, TimerInterval, true, function Effects)
endfunction
private function SpellConditions takes nothing returns boolean
if GetSpellAbilityId() == AbilId then
call Actions()
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
loop
exitwhen i>bj_MAX_PLAYERS
call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
set i = i + 1
endloop
call TriggerAddCondition(t, Condition( function SpellConditions ))
set B = Condition(function Filt)
endfunction
endscope
Electric Explosion v1.01
Import Difficulty: Low
Units Affected: Enemy, ground, non-structure, non-magic immune
Target Type: AoE - Enemy, ground, organic
Spell Info:
Summons a torrent of electrical activity at the target area. Pillars of energy attract all nearby enemies, then eject them in a powerful blast.
Level 1 - Small area, 100 damage.
Level 2 - Medium area, 100 damage.
Level 3 - Large area, 300 damage.
Requirements:
Timer Utils
Group Utils
Would also be a good idea to make use of Bound Sentinel
A capped limit of 100 units affected per instance of this spell is in effect. If this is a problem for someone, use less units ;Þ.
Code
JASS:
scope ElectricExplosion initializer Init
// This spell requires the GroupUtils and TimerUtils systems. Similarly, as with all movement based spells, it is a good idea to have
// the BoundSentinel system installed and operational in your map as well.
// I inacted a limit of 100 units affected per instance of this spell. If this becomes a problem, use less units :Þ
// As such, given the internal limitations of structs, this also imposes an 81 spell instance limit as well.
globals
// Rawcode for caster ability
private constant integer AbilId = 'A001'
// Effect to create at the feet of sliding units
private constant string SlideFX = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl"
// Attachment point for SlideFX
private constant string SlideFXAttachmentPoint = "origin"
// Effect to create when units are initially trapped by the lightning beams
private constant string InitialSFX = "Abilities\\Weapons\\ChimaeraLightningMissile\\ChimaeraLightningMissile.mdl"
// Attachment point for InitialFX
private constant string InitialFXAttachmentPoint = "chest"
// Effect to create for the explosion
private constant string ExplosionSFX = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
// Type of lightning effect to create on affected units
private constant string LightningSFX = "FORK"
// Z axis height for lightning
private constant real LightningZ = 35.
// Interval for periodic timer. This doesn't really need to be changed, but its here nonetheless...
private constant real TimerInterval = .04
// Attack type for explosion damage
private constant attacktype AttackType = ATTACK_TYPE_MAGIC
// Damage type for explosion damage
private constant damagetype DamageType = DAMAGE_TYPE_MAGIC
endglobals
// Damage to targets after explosion
private constant function ExplosionDamage takes integer lvl returns real
return 100.*lvl
endfunction
// Area of effect within which collision take place
private constant function EffectArea takes integer lvl returns real
return 125.+lvl*75.
endfunction
// Duration for lightning pull effect
private constant function InitialSlideDuration takes integer lvl returns real
return 1.
endfunction
// Initial slide speed for lightning pull effect per TimerInterval
private constant function InitialSlideSpeed takes integer lvl returns real
return 10.
endfunction
// Initial slide speed after explosion per TimerInterval
private constant function ExplosionInitialSlideSpeed takes integer lvl returns real
return 30.
endfunction
// Deceleration slide speed after explosion per TimerInterval
private constant function ExplosionDecelerationSlideSpeed takes integer lvl returns real
return .6
endfunction
//== No touching past this point ==\\
globals
private boolexpr B
private location L = Location(0.,0.)
private integer Temp_data
private unit Targ
endglobals
private struct data
unit cast
player owner
timer t
integer lvl
integer count = 0
lightning array light[100]
unit array targ[100]
real duration
real array cos[100]
real array sin[100]
real array distance[100]
real z
real x
real y
real speed
real decel
real damage
boolean in = false
method onDestroy takes nothing returns nothing
call ReleaseTimer(.t)
endmethod
endstruct
private function Filt takes nothing returns boolean
local data d = Temp_data
local real x
local real y
local real dx
local real dy
local real ang
set Targ = GetFilterUnit()
if IsUnitEnemy(Targ,d.owner) and UnitAlive(Targ) and IsUnitType(Targ,UNIT_TYPE_STRUCTURE)==false and IsUnitType(Targ,UNIT_TYPE_MAGIC_IMMUNE)==false then
set d.count = d.count + 1
set x = GetUnitX(Targ)
set y = GetUnitY(Targ)
call MoveLocation(L,x,y)
set d.light[d.count] = AddLightningEx(LightningSFX,true,d.x,d.y,d.z,x,y,GetLocationZ(L)+LightningZ)
set d.targ[d.count] = Targ
set ang = Atan2(y - d.y, x - d.x)
set d.cos[d.count] = Cos(ang)
set d.sin[d.count] = Sin(ang)
set dx = x-d.x
set dy = y-d.y
set d.distance[d.count] = SquareRoot(dx*dx+dy*dy)
call DestroyEffect(AddSpecialEffectTarget(InitialSFX,d.targ[d.count],InitialFXAttachmentPoint))
call SetUnitPathing(d.targ[d.count],false)
return true
endif
return false
endfunction
private function Effects takes nothing returns nothing
local data d = GetTimerData(GetExpiredTimer())
local integer i = d.count
local real x
local real y
if not d.in then
loop
exitwhen i==0
set d.distance<i> = d.distance<i> - d.speed
if d.distance<i>>d.speed then
set x = GetUnitX(d.targ<i>)-d.speed*d.cos<i>
set y = GetUnitY(d.targ<i>)-d.speed*d.sin<i>
call SetUnitPosition(d.targ<i>,x,y)
else
set x = d.x
set y = d.y
call SetUnitPosition(d.targ<i>,d.x,d.y)
endif
call MoveLocation(L,x,y)
call MoveLightningEx(d.light<i>,true,d.x,d.y,d.z,x,y,GetLocationZ(L)+LightningZ)
set i = i - 1
endloop
set d.duration = d.duration - TimerInterval
if d.duration<TimerInterval then
set d.in = not d.in
set d.speed = ExplosionInitialSlideSpeed(d.lvl)
set d.decel = ExplosionDecelerationSlideSpeed(d.lvl)
call DestroyEffect(AddSpecialEffect(ExplosionSFX,d.x,d.y))
set i = d.count
loop
exitwhen i==0
call DestroyLightning(d.light<i>)
call SetUnitPathing(d.targ<i>,true)
call UnitDamageTarget(d.cast,d.targ<i>,d.damage,false,false,AttackType,DamageType,null)
set i = i - 1
endloop
endif
else
loop
exitwhen i==0
set x = GetUnitX(d.targ<i>)+d.speed*d.cos<i>
set y = GetUnitY(d.targ<i>)+d.speed*d.sin<i>
call SetUnitPosition(d.targ<i>,x,y)
call DestroyEffect(AddSpecialEffectTarget(SlideFX,d.targ<i>,SlideFXAttachmentPoint))
set i = i - 1
endloop
set d.speed = d.speed - d.decel
if d.speed<d.decel then
call d.destroy()
endif
endif
endfunction
private function Actions takes nothing returns nothing
local data d = data.create()
set d.cast = GetTriggerUnit()
set d.owner = GetOwningPlayer(d.cast)
set d.lvl = GetUnitAbilityLevel(d.cast,AbilId)
set d.x = GetSpellTargetX()
set d.y = GetSpellTargetY()
call MoveLocation(L,d.x,d.y)
set d.z = GetLocationZ(L)+LightningZ
set d.damage = ExplosionDamage(d.lvl)
set d.speed = InitialSlideSpeed(d.lvl)
set d.duration = InitialSlideDuration(d.lvl)
set d.t = NewTimer()
call GroupClear(ENUM_GROUP)
set Temp_data = d
call GroupEnumUnitsInArea(ENUM_GROUP, d.x, d.y, EffectArea(d.lvl), B)
if d.count>0 then
call SetTimerData(d.t,d)
call TimerStart(d.t, TimerInterval, true, function Effects)
else
call d.destroy()
endif
endfunction
private function SpellConditions takes nothing returns boolean
if GetSpellAbilityId() == AbilId then
call Actions()
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
loop
exitwhen i>bj_MAX_PLAYERS
call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
set i = i + 1
endloop
call TriggerAddCondition(t, Condition( function SpellConditions ))
set B = Condition(function Filt)
endfunction
endscope</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>
Updates:
1.01
- Added some extra configurables
- Updated distance check in Electric Explosion to a. actually work, and b. be far more efficient
- Added a vertex fade to Twisteroo, which is toggleable (forgot to incorporate before initial release
1.00
- Initial release
Attachments
-
53.8 KB Views: 571
-
59.8 KB Views: 555
-
103.2 KB Views: 499