- Reaction score
- 663
Finally got this b*st*rd of a spell working 
Technical Details:
Requires NewGen WE
Made in vJASS
Fully* MUI
Leakless*
Lagless (for me, even with a significant number of instances)
System-independent
*I think
Description:
Launches a barrage of spirits towards the target point - upon contact with the ground, the spirits will release a burst of unholy energy, damaging enemy units nearby.
At longer distances, the targeting accuracy is reduced.
Deals 25/35/45 damage, damage is reduced with increased distance from epicenter
Screenshots:
Projectiles coming down from a ~2000-distance cast
Projectiles at ~800 distance cast
Importing:
Code:
Thanks to:
Shadow1500 - Parabola function
Artificial - helping me figure out what I was doing wrong, then commencing to break stuff
Vexorian - dummy.mdx [del](can someone tell me who the author of dummy.mdx is so I can properly credit them)[/del]
Gwypaas - giving me the idea to enclose everything within a struct
Credits:
If you use this spell, please credit me for making it because it took quite some time to complete
Technical Details:
Requires NewGen WE
Made in vJASS
Fully* MUI
Leakless*
Lagless (for me, even with a significant number of instances)
System-independent
*I think
Description:
Launches a barrage of spirits towards the target point - upon contact with the ground, the spirits will release a burst of unholy energy, damaging enemy units nearby.
At longer distances, the targeting accuracy is reduced.
Deals 25/35/45 damage, damage is reduced with increased distance from epicenter
Screenshots:
Projectiles coming down from a ~2000-distance cast


Importing:
Code:
1) Copy the PB trigger into your map
2) Copy the dummy.mdx model (if you do not already have it) into your map
3) Copy the Projectile dummy (h000) and the Launcher dummy (h001) into your map
4) Copy the Phantasmal Barrage (A000)
5) Modify the SID (Ability ID), LID (Launcher dummy ID) and PID (Projectile dummy ID) to match the rawcodes of the dummies and ability you copied
6) Add the spell to a hero
7) Enjoy!
JASS:
//Phantasmal barrage
//By Flare
//Requires: Projectile dummy (h000)
// Launcher Dummy (h001)
// Phantasmal Barrage ability (A000)
// NewGen WE
// dummy.mdx
//Thanks to:
//Shadow1500 - Parabola function
//Vexorian - dummy.mdx
//Please let me know who the creator of the dummy.mdx model is, so I can credit them <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile :)" data-shortname=":)" />
struct PB
//Configurables
//Ability rawcode
private static constant integer SID = 'A000'
//Launcher Dummy rawcode
private static constant integer LID = 'h001'
//Projectile Dummy rawcode
private static constant integer PID = 'h000'
//Timer interval
private static constant real TIMER_INT = 0.03125
//Number of projectiles used
private static constant integer PROJCOUNT = 5
//Projectile effect
private static constant string PROJ_FX = "Abilities\\Spells\\Other\\BlackArrow\\BlackArrowMissile.mdl"
//Explosion effect
private static constant string END_FX = "Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl"
//Number of effects spawned per ring
private static constant integer FX_COUNT = 1
//Number of rings spawned
private static constant integer RING_COUNT = 1
//Ring offset
private static constant real RING_OFFSET = 0.
//Spawn offset
private static constant real SPAWN_OFFSET = 300.
//Attachment point for projectile
private static constant string ATTACH_PT = "origin"
//Low bound for projectile speed - projectile speed will be somewhere between SPEED_LOW and SPEED_HIGH
private static constant real SPEED_LOW = 300
//High bound for projectile speed
private static constant real SPEED_HIGH = 1000
//Projectile's target offset - base value for how far from the target point the projectile will land
private static constant real TARGET_OFFSET = 0.
//Intensity of curve - lower values result in a "sharper" curve, higher values result in a bow-shaped curve
private static constant real CURVE = 2.5
//Accurate range
private static constant real ACC_RANGE = 1000.
//Accuracy discrepancy - higher values result in a less accurate barrage at longer distance
//Discrepancy only takes effect if the target point if more than ACC_RANGE units away from the caster
private static constant real ACC_DISCREP_MULTI = 200.
//For every ACC_DISCREP_MULTI units beyond ACC_RANGE, the projectiles' targeting will be off by up to ACC_DISCREP units
//e.g. (using current values) if you cast the spell 1400 units away
//the projectiles will land up to ((1400-1000)/200)*125 (250) units away from their original target
//NOTE: Projectile's original target is the target point offset by TARGET_OFFSET towards a random angle
private static constant real ACC_DISCREP = 125.
//Minimum damage percentage - percentage of whole damage that is dealt at the edge of damage radius
private static constant real MIN_DMG_PERCENT = 0.25
//Attack type, damage type, weapon type
private static constant attacktype ATYPE = ATTACK_TYPE_MAGIC
private static constant damagetype DTYPE = DAMAGE_TYPE_NORMAL
private static constant weapontype WTYPE = WEAPON_TYPE_WHOKNOWS
//Animation played by the launcher dummies
private static constant string ANIM = "attack"
//Lifespan of launcher dummies
private static constant real LIFESPAN = 1.
//Determines whether the projectile dummies should be set back to normal before being killed
private static constant boolean RESET_DUMMY = true
//Minimum projectile targeting range (thanks to Artificial for pointing out this issue)
private static constant real MIN_RANGE = 250.
//RGBA and scaling values
//Launcher RGBA and scaling
private static constant integer LR = 255
private static constant integer LG = 255
private static constant integer LB = 255
private static constant integer LA = 127
private static constant real LS = 1.
//Projectile RGBA and scaling
private static constant integer PR = 255
private static constant integer PG = 255
private static constant integer PB = 255
private static constant integer PA = 255
private static constant real PS = 1.25
//Damage radius calculation
private static method DamageRadius takes unit u returns real
local real base = 150.
local real m1 = I2R (GetUnitAbilityLevel (u, .SID))
local real m2 = 50.
return base + m1*m2
endmethod
//Damage calculation
private static method Damage takes unit u returns real
local real base = 15
local real m1 = GetUnitAbilityLevel (u, .SID)
local real m2 = 5
return base + m1*m2
endmethod
//Required globals
private static location tempLoc = Location (0,0)
private static group genGroup = CreateGroup ()
private static timer spellTimer = CreateTimer ()
private static PB array data
private static PB gdata = 0
private static integer N = 0
private static real groupX = 0
private static real groupY = 0
private static real doublePi = bj_PI * 2
private static boolean timerStarted = false
//Required struct members
unit caster
unit proj
real speed
real pcos
real psin
real curdist
real maxdist
effect pfx
//Required helper functions
private static method RingEffect takes string fxString, real ringCount, real effectCount, real offset, real x, real y returns nothing
local integer i1 = 1
local integer i2 = 1
local real angle = GetRandomReal (0, .doublePi)
local real x2
local real y2
loop
exitwhen i1 > ringCount
loop
exitwhen i2 > effectCount
set angle = angle + .doublePi/effectCount
set x2 = x + Cos (angle) * (i1 * offset)
set y2 = y + Sin (angle) * (i1 * offset)
call DestroyEffect(AddSpecialEffect (fxString, x2, y2))
set i2 = i2 + 1
endloop
set i1 = i1 + 1
endloop
endmethod
private static constant method JumpParabola takes real dist, real maxdist,real curve returns real
local real t = (dist*2)/maxdist-1
return (-t*t+1)*(maxdist/curve)
endmethod
private static method SetUnitXY takes unit u,real x,real y returns nothing
if x<GetRectMaxX(bj_mapInitialPlayableArea) and x>GetRectMinX(bj_mapInitialPlayableArea) and y<GetRectMaxY(bj_mapInitialPlayableArea) and y>GetRectMinY(bj_mapInitialPlayableArea) then
call SetUnitX(u,x)
call SetUnitY(u,y)
endif
endmethod
private static method GetCoordZ takes real x, real y returns real
call MoveLocation (.tempLoc, x, y)
return GetLocationZ (.tempLoc)
endmethod
private static method SetUnitZ takes unit u, real h returns boolean
local real z = h - .GetCoordZ (GetUnitX (u), GetUnitY (u))
if z > 0 then
call SetUnitFlyHeight (u, z, 0)
return false
else
return true
endif
endmethod
private static method GetUnitZ takes unit u returns real
return .GetCoordZ (GetUnitX (u), GetUnitY (u)) + GetUnitFlyHeight (u)
endmethod
private static method GetRandOffsetX takes real x, real dist returns real
local real discrepdist = ((dist - .ACC_RANGE)/.ACC_DISCREP_MULTI) * .ACC_DISCREP
local real angle = Cos (GetRandomReal (0, .doublePi))
return x + angle * discrepdist
endmethod
private static method GetRandOffsetY takes real y, real dist returns real
local real discrepdist = ((dist - .ACC_RANGE)/.ACC_DISCREP_MULTI) * .ACC_DISCREP
local real angle = Sin (GetRandomReal (0, .doublePi))
return y + angle * discrepdist
endmethod
//Spell functions
private static method GroupFunc takes nothing returns boolean
local PB a = .gdata
local unit u = GetFilterUnit ()
local real ux = GetUnitX (u)
local real uy = GetUnitY (u)
local real x = .groupX - ux
local real y = .groupY - uy
local real dist = SquareRoot (x*x + y*y)
local real percent = (.DamageRadius (a.caster) - dist)/.DamageRadius (a.caster)
local real dmg
if percent < .MIN_DMG_PERCENT then
set percent = .MIN_DMG_PERCENT
endif
set dmg = .Damage (a.caster) * percent
if IsUnitEnemy (u, GetOwningPlayer (a.caster)) then
call UnitDamageTarget (a.caster, u, dmg, false, true, .ATYPE, .DTYPE, .WTYPE)
endif
set u = null
return false
endmethod
private static method Move takes nothing returns nothing
local integer i = 1
local integer i2 = 0
local real fxAngle = 0
local real x = 0
local real y = 0
local real x2 = 0
local real y2 = 0
local real z = 0
local real uz = 0
local real ex = 0
local real ey = 0
local boolean b = false
local real hDist = 0
local real zDif = 0
local integer animindex = 0
local PB a
loop
exitwhen i > .N
set a = .data<i>
set x = GetUnitX (a.proj)
set y = GetUnitY (a.proj)
set x2 = x + a.pcos*a.speed
set y2 = y + a.psin*a.speed
set z = .JumpParabola (a.curdist, a.maxdist, .CURVE)
set uz = .GetUnitZ (a.proj)
set hDist = SquareRoot ((x2-x)*(x2-x) + (y2-y)*(y2-y))
set zDif = z - uz
set animindex = R2I (Atan2 (zDif, hDist) * bj_RADTODEG + 90)
if animindex >= 0 and animindex <= 180 then
call SetUnitAnimationByIndex (a.proj, animindex)
endif
call .SetUnitXY (a.proj, x2, y2)
set b = .SetUnitZ (a.proj, z)
if b and a.curdist > 0 then
if .RESET_DUMMY then
call SetUnitAnimationByIndex (a.proj, 0)
endif
call DestroyEffect (a.pfx)
set a.curdist = 0
call KillUnit (a.proj)
set .gdata = a
call GroupEnumUnitsInRange (.genGroup, x, y, .DamageRadius (a.caster), Condition (function PB.GroupFunc))
call .RingEffect (.END_FX, .RING_COUNT, .FX_COUNT, .RING_OFFSET, x, y)
call a.destroy ()
set .data<i> = .data[.N]
set .N = .N - 1
set i = i - 1
if .N == 0 and .timerStarted then
call PauseTimer (.spellTimer)
set .timerStarted = false
endif
endif
set a.curdist = a.curdist + a.speed
set i = i + 1
endloop
endmethod
private static method SpellCond takes nothing returns boolean
return GetSpellAbilityId () == .SID
endmethod
private static method SpellActions takes nothing returns nothing
local PB a
local integer i = 0
local unit u
local unit c = GetTriggerUnit ()
local real cx = GetUnitX (c)
local real cy = GetUnitY (c)
local real sx
local real sy
local location l = GetSpellTargetLoc ()
local real tx = GetLocationX (l)
local real ty = GetLocationY (l)
local real x = tx - cx
local real y = ty - cy
local real rx
local real ry
local real angle = Atan2 (y, x)
local real dist = SquareRoot (x*x + y*y)
set rx = cx + Cos (angle) * dist
set ry = cy + Sin (angle) * dist
call RemoveLocation (l)
set l = null
if .timerStarted == false then
call TimerStart (.spellTimer, .TIMER_INT, true, function PB.Move)
endif
loop
exitwhen i == .PROJCOUNT
if dist < .MIN_RANGE then
set angle = GetRandomReal (0, .doublePi)
endif
set a = PB.create ()
set a.caster = c
set sx = cx + Cos (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .SPAWN_OFFSET)
set sy = cy + Sin (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .SPAWN_OFFSET)
set u = CreateUnit (GetOwningPlayer (a.caster), .LID, sx, sy, angle * bj_RADTODEG)
call SetUnitVertexColor (u, .LR, .LG, .LB, .LA)
call SetUnitScale (u, .LS, .LS, .LS)
call SetUnitAnimation (u, .ANIM)
call QueueUnitAnimation (u, "stand")
call UnitApplyTimedLife (u, 'BTLF', .LIFESPAN)
if dist <= .ACC_RANGE then
set rx = rx + Cos (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .TARGET_OFFSET)
set ry = ry + Sin(GetRandomReal (0, .doublePi)) * GetRandomReal (0, .TARGET_OFFSET)
else
set rx = .GetRandOffsetX (rx, dist)
set ry = .GetRandOffsetY (ry, dist)
endif
set x = rx-cx
set y = ry-cy
set angle = Atan2 (y, x)
set a.pcos = Cos (angle)
set a.psin = Sin (angle)
set a.maxdist = SquareRoot (x*x + y*y)
if a.maxdist < .MIN_RANGE then
set angle = GetUnitFacing (u) * bj_DEGTORAD
set a.maxdist = a.maxdist + .MIN_RANGE
set a.pcos = Cos (angle)
set a.psin = Sin (angle)
endif
set a.proj = CreateUnit (GetOwningPlayer (a.caster), .PID, sx, sy, angle * bj_RADTODEG)
call SetUnitVertexColor (a.proj, .PR, .PG, .PB, .PA)
call UnitAddAbility (a.proj, 'Arav')
call UnitRemoveAbility (a.proj, 'Arav')
set a.curdist = 0
set a.speed = GetRandomReal (.SPEED_LOW, .SPEED_HIGH) * .TIMER_INT
set a.pfx = AddSpecialEffectTarget (.PROJ_FX, a.proj, .ATTACH_PT)
set i = i + 1
set .N = .N + 1
set .data[.N] = a
endloop
set c = null
set u = null
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger ()
call TriggerRegisterAnyUnitEventBJ (t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition (t, Condition (function PB.SpellCond))
call TriggerAddAction (t, function PB.SpellActions)
endmethod
endstruct</i></i>
Thanks to:
Shadow1500 - Parabola function
Artificial - helping me figure out what I was doing wrong, then commencing to break stuff
Vexorian - dummy.mdx [del](can someone tell me who the author of dummy.mdx is so I can properly credit them)[/del]
Gwypaas - giving me the idea to enclose everything within a struct
Credits:
If you use this spell, please credit me for making it because it took quite some time to complete
Attachments
-
34.3 KB Views: 489