Using a parabola.

NeuroToxin

New Member
Reaction score
46
Hey, I've never actually had to use a parabola for ANYTHING. Quite literally. Recently, I made this attack spell, which uses souls, and I wanted to know, how to make it arc like Parabola.png I have this, which works flawlessly, even at attack speeds such as 0.01, but I would like to know the parabola. I think I heard somewhere that you use the Sine function, but I personally have never needed to use it, so I've never learned.

EDIT: I believe its actually called a Bezier Curve.
JASS:
scope Steal initializer Init
    globals
//The spell id of the spell, "Life Steal"
        private constant integer SPELLID = 'A002'
//The dummy id of the dummy unit, attack dummy
        private constant integer DUMID = 'h001'
//The boolean to use souls, note that souls are gathered from Soul Portal
        private constant boolean USE_SOULS = true
//The interval for the timer
        private constant real TINTERVAL = 0.03125
//The offset per TINTERVAL seconds.
        private constant real OFFSET = 20
//The knockback when a bolt hits a unit.
        private constant real KNOCKBACK = 40
    endglobals
    
    private constant function HPSTEAL takes integer lvl returns real
        return .01 + (lvl * .01)
    endfunction
    
    globals
        private hashtable ht = InitHashtable()
    endglobals
    
    private function MoveDamage takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit attacker = LoadUnitHandle( ht, GetHandleId(t), 0)
    local unit target = LoadUnitHandle( ht, GetHandleId(t), 1)
    local unit dummy = LoadUnitHandle( ht, GetHandleId(t), 2)
    local boolean returning = LoadBoolean( ht, GetHandleId(t), 3)
    local real tx = GetUnitX(target)
    local real ty = GetUnitY(target)
    local real dx = GetUnitX(dummy)
    local real dy = GetUnitY(dummy)
    local real distx = tx - dx
    local real disty = dy - ty
    local real tempreal = SquareRoot(distx * distx + disty * disty)
    local real angle
    local real offsetx
    local real offsety
    local real damage = HPSTEAL(GetUnitAbilityLevel(attacker, SPELLID))
    local real cx = GetUnitX(attacker)
    local real cy = GetUnitY(attacker)
    local real dx1 = dx - cx
    local real dy1 = dy - cy
    local real casterdist = SquareRoot(dx1 * dx1 + dy1 * dy1)
    local real lifestolen
    if dummy == null then
        call KillUnit(dummy)
        call FlushChildHashtable( ht, GetHandleId(t))
        call PauseTimer(t)
        call DestroyTimer(t)
    elseif tempreal >= 50 and returning == false then
        set angle = Atan2(ty - dy, tx - dx)
        set offsetx = dx + OFFSET * Cos(angle)
        set offsety = dy + OFFSET * Sin(angle)
        call SetUnitX( dummy, offsetx)
        call SetUnitY( dummy, offsety)
        call SetUnitFacing( dummy, bj_RADTODEG * angle)
    elseif returning == false and tempreal <= 51 then
        set angle = Atan2(ty - dy, tx - dx)
        set offsetx = tx + KNOCKBACK * Cos(angle)
        set offsety = ty + KNOCKBACK * Sin(angle)
        call SetUnitX(target, offsetx)
        call SetUnitY(target, offsety)
        call SetWidgetLife( target, GetWidgetLife(target) - (GetWidgetLife(target) * damage))
        set lifestolen = GetWidgetLife(target) * damage
        call SaveReal( ht, GetHandleId(t), 4, lifestolen)
        set returning = true
        call SaveBoolean( ht, GetHandleId(t), 3, returning)
    elseif returning == true and casterdist >= 50 then
        set angle = Atan2(cy - dy, cx - dx)
        set offsetx = dx + OFFSET * Cos(angle)
        set offsety = dy + OFFSET * Sin(angle)
        call SetUnitX( dummy, offsetx)
        call SetUnitY( dummy, offsety)
        call SetUnitFacing( dummy, bj_RADTODEG * angle)
    else
        set lifestolen = LoadReal( ht, GetHandleId(t), 4)
        call SetWidgetLife( attacker, GetWidgetLife(attacker) + lifestolen)
        call KillUnit(dummy)
        call FlushChildHashtable( ht, GetHandleId(t))
        call PauseTimer(t)
        call DestroyTimer(t)
    endif
        set attacker = null
        set target = null
        set dummy = null
        set t = null
    endfunction
    
    private function OnCast takes nothing returns boolean
    local timer t
    local integer i = GetUnitAbilityLevel(GetAttacker(), SPELLID)
    local unit attacker
    local unit target
    local unit dummy
    local real x
    local real y
    local boolean returning
    if i > 0 then
        if Count[GetUnitId(GetAttacker())] > 0 or USE_SOULS == false then
            set t = CreateTimer()
            set attacker = GetAttacker()
            set target = GetTriggerUnit()
            set x = GetUnitX(attacker)
            set y = GetUnitY(attacker)
            set returning = false
            set dummy = CreateUnit( GetTriggerPlayer(), DUMID, x, y, GetRandomReal(0, 360))
            call SaveUnitHandle( ht, GetHandleId(t), 0, attacker)
            call SaveUnitHandle( ht, GetHandleId(t), 1, target)
            call SaveUnitHandle( ht, GetHandleId(t), 2, dummy)
            call SaveBoolean( ht, GetHandleId(t), 3, returning)
            call TimerStart( t, TINTERVAL, true, function MoveDamage)
            if USE_SOULS == true then
                set Count[GetUnitId(attacker)] = Count[GetUnitId(attacker)] - 1
            endif
            set attacker = null
            set t = null
            set dummy = null
            set target = null
        endif
    endif
    return true
    endfunction

    //===========================================================================
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger(  )
        call TriggerAddCondition( t, Condition( function OnCast) )
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ATTACKED)
    endfunction
endscope
 

emjlr3

Change can be a good thing
Reaction score
395
this doesn't actually create any sort of parabola, just moves the projectile to and from the target in a straight line (depending on path)

Acehart made a pretty good one

even explains how to use it...

this could theoretically be extrapolated horizontally instead of vertically, otherwise you can go with a bezier curve - the easiest of which to construct is a quadratic (parabolic).

2d5e5d58562d8ec2c35f16df98d2b974.png


where P0 is where you started, P2 is where you are going, and P1 is ideally a polar projection of some point in between P0 and P2, a distance outward (wideness of your arc), say a point projected outward perpendicular from the center of a line connecting P0 and P2 at a distance of 500.

per the equation, t is 0-1, which denotes the path traveled along your function. .5 would be in the center, 0 at the beginning, 1 at the other end.
 

NeuroToxin

New Member
Reaction score
46
This may be incredibly wrong, but heres what i have, and it starts the curve, then it gets screwed up. Man I hate math. :p.
cx = attacker X, cy = attacker Y, dist is the distance between target and attacker.
JASS:
        elseif tempreal >= 50 and returning == false then
        set angle = Atan2(ty - dy, tx - dx)
        set p1y = cy + (dist / 2) * Sin(bj_DEGTORAD * (GetUnitFacing(attacker)+80))
        set p1x = cx + (dist / 2) * Cos(bj_DEGTORAD * (GetUnitFacing(attacker)+80))
        set p2x = cx + dist * Cos(angle)
        set p2y = cy + dist * Sin(angle)
        set X = (((1-r)*(1-r))*cx)+(2*(1-r)*r*p1x)+(r*r*p2x)
        set Y = (((1-r)*(1-r))*cy)+(2*(1-r)*r*p1y)+(r*r*p2y)
        call SetUnitX( dummy, X)
        call SetUnitY( dummy, Y)
        call SetUnitFacing( dummy, bj_RADTODEG * angle)
        set r = r + 0.05
        call SaveReal( ht, GetHandleId(t), 5, r)
 

emjlr3

Change can be a good thing
Reaction score
395
p1 needs to be out from your line, so say

JASS:
set p1x = (cx+dist/2.*Cos(angle))+width*Cos(angle+bj_PI/2.)
set p1y = (cy+dist/2.*Sin(angle))+width*Sin(angle+bj_PI/2.)

a perpendicular distance "width" from the center of your line connecting the target and caster - use a textag to make sure its where you want it

your x/y looks about right, but you need to update it dynamically during the curve

JASS:
local real b = 1.-a // where 'a' goes up and down from 0. to 1. during the spell

call SetUnitX(dummy,p0x*a*a+p1x*2*a*b+p2x*b*b)
call SetUnitY(dummy,p0y*a*a+p1y*2*a*b+p2y*b*b)


use an a and a b to save some arithmetic. a should start at 1. where b should start at 0. to get there, and vice versa if you want to return

*untested
 

emjlr3

Change can be a good thing
Reaction score
395
no its an ellipse

Wild Axes uses a bezier curve

I you have ever played Age of Myths, the spell Kiss of the Dragon (which I made) uses a bezier curve

p0, p1 and p2 shouldn't need to be updated, unless you want a projectile to eventually travel to a target (say the returning of the wild axes from DotA), you could update p2 with the targets x/y dynamically

the only thing you should need to continually solve for is the x/y coordinates of the missile, dependent upon b (0-1)
 

NeuroToxin

New Member
Reaction score
46
no its an ellipse

Wild Axes uses a bezier curve

I you have ever played Age of Myths, the spell Kiss of the Dragon (which I made) uses a bezier curve

p0, p1 and p2 shouldn't need to be updated, unless you want a projectile to eventually travel to a target (say the returning of the wild axes from DotA), you could update p2 with the targets x/y dynamically

the only thing you should need to continually solve for is the x/y coordinates of the missile, dependent upon b (0-1)

And that would be why its screwing up for me.
 

emjlr3

Change can be a good thing
Reaction score
395
incase you are still having some issues
JASS:
scope BezierCurve

globals
    private constant integer        ABIL        = 'XXXX'
    private constant integer        DUM         = 'XXXX'
    
    private constant real           TIMEOUT     = .04
    
    private timer T=CreateTimer()
    private integer C=0
    private integer array D
endglobals

private struct data
    unit cast
    unit targ
    unit dum
    real p0x
    real p0y
    real p1x
    real p1y
    real p2x
    real p2y
    real a=1.
    real ang
    real dist
    real speed=.05
    real width=500.
    real sigma
    real delta

    method destroy takes nothing returns nothing
        // deal damage to .targ and such
        call RemoveUnit(.dum)
        call .deallocate()
    endmethod
    
    static method onPeriodic takes nothing returns nothing
        local data this
        local integer i=1
        local real b
        
        loop
            exitwhen i>C
            set this=D<i>
            
            // .05 is the &quot;speed&quot;, a little arbitrary, the value in wc3 units really depends on .dist
            // a constant .speed yields a constant projectile duration
            // make it depend on .dist to manipulate the projectile&#039;s real speed to keep it constant
            // i.e. set .speed=25/.dist, @ 500 distance, speed=.05, @ 1000 distance, speed=.025
            set .a=.a-.speed
            if .a&lt;=0. then
                call .destroy()
                set D<i>=D[C]
                set C=C-1
                set i=i-1
            else            
                set b=1.-a
                // update .dum facing
                set .sigma=.sigma-.delta
                call SetUnitFacing(.dum,(.ang+.sigma)*bj_RADTODEG)
                // this will eventually take us to .targ
                set .p2x=GetUnitX(.targ)
                set .p2y=GetUnitY(.targ)
                // could also update .p1x/y in the same way to make it look extra special good
                // but that is a lot of extra arithmetic
                call SetUnitX(dum,.p0x*.a*.a+.p1x*2*.a*b+.p2x*b*b)
                call SetUnitX(dum,.p0y*.a*.a+.p1y*2*.a*b+.p2y*b*b)
            endif
            
            set i=i+1
        endloop
        
        if C==0 then
            call PauseTimer(T)
        endif
    endmethod
    
    static method actions takes nothing returns nothing
        local data this=data.allocate()
        
        set .cast=GetTriggerUnit()
        set .targ=GetSpellTargetUnit()
        set .p0x=GetUnitX(.cast)
        set .p0y=GetUnitY(.cast)
        set .p2x=GetUnitX(.targ)
        set .p2y=GetUnitY(.targ)
        set .ang=Atan2(.p2y-.p0y,.p2x-.p0x)
        set .dist=SquareRoot(Pow(.p0x-.p2x,2)+Pow(.p0y-.p2y,2))
        // + for to the right, - for to the left, 500 is the width
        // generally the larger the .width, the wider the arc (also depends on the magnitude of .dist)
        // could create a projectile at both the right and left to have one on either side...
        set .p1x=(.p0x+.dist/2.*Cos(.ang))+.width*Cos(.ang+bj_PI/2.)
        set .p1y=(.p0y+.dist/2.*Sin(.ang))+.width*Sin(.ang+bj_PI/2.)     
        set .dum=CreateUnit(GetTriggerPlayer(),DUM,.p0x,.p0y,0.)
        // updating this guys facing will be tough
        // really depends on the ratio of .dist/.width, and its effect on the accident angle from the caster (Pythagorean Theorem)
        // once you have that, go from +.accident to -.accident smoothly from b=0.-b=1. (or vice versa for to the left)
        set .sigma=Atan2(.p1y-.p0y,.p1x-.p0x)-.ang // working in the clockwise direction
        // we need to go from +.sigma to -.sigma
        // solving for the total number of timer function callbacks allows us to divvy this total into equal parts
        set .delta=(.sigma*2.)/(1./.speed)    
        // starting at .sigma
        call SetUnitFacing(.dum,(.ang+.sigma)*bj_RADTODEG)
        
        set C=C=1
        set D[C]=d
        if C==1 then
            call TimerStart(T,TIMEOUT,true,function data.onPeriodic)
        endif
    endmethod
    static method conditions takes nothing returns nothing
        if GetSpellAbilityId()==ABIL then
            call data.actions()
        endif
    endmethod
    
    static method onInit takes nothing returns nothing
        local trigger trig=CreateTrigger()
        local integer i=0
        loop
            call TriggerRegisterPlayerUnitEvent(trig,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
            set i=i+1
            exitwhen i==bj_MAX_PLAYERS
        endloop
        call TriggerAddCondition(trig,Condition(function data.conditions))
    endmethod
endstruct

endscope
</i></i>


untested and uncompiled - in theory it should work
 

emjlr3

Change can be a good thing
Reaction score
395
this one actually does work, tested

JASS:
scope BezierCurve

globals
    private constant integer        ABIL        = &#039;AHbn&#039;
    private constant integer        DUM         = &#039;n000&#039;
    
    private constant real           TIMEOUT     = .04
    
    private constant string         SFX         = &quot;Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl&quot;
    
    private timer T=CreateTimer()
    private integer C=0
    private integer array D
endglobals

private struct data
    unit cast
    unit targ
    unit dum
    effect sfx
    real p0x
    real p0y
    real p1x
    real p1y
    real p2x
    real p2y
    real a=1.
    real ang
    real dist
    real speed=.025
    real sigma
    real delta
    real defaultheight=40.
    real height=300.

    method destroy takes nothing returns nothing
        call DestroyEffect(.sfx)
        call KillUnit(.dum)
        call .deallocate()
    endmethod
    
    // Credits to Acehart for this parabola function
    static method getParabolaZ takes real x, real dist, real height returns real
        return 4.*height*x*(dist-x)/(dist*dist)
    endmethod
    static method onPeriodic takes nothing returns nothing
        local data this
        local integer i=1
        local real b
        
        loop
            exitwhen i&gt;C
            set this=D<i>
            
            // update the position along the curve path
            set .a=.a-.speed            
            if .a&lt;=0. then
                call .destroy()
                set D<i>=D[C]
                set C=C-1
                set i=i-1
            else                        
                set b=1.-a
                // update dum facing
                set .sigma=.sigma-.delta
                call SetUnitFacing(.dum,(.ang+.sigma)*bj_RADTODEG)
                // set parabolic fly height
                call SetUnitFlyHeight(.dum,data.getParabolaZ(b,1.,.height)+.defaultheight,0.)
                // update dummy model roll pitch
                call SetUnitAnimationByIndex(.dum,R2I((b/1.)*181))
                
                // if targ is still alive, keep moving toward it
                if UnitAlive(.targ) then
                    set .p2x=GetUnitX(.targ)
                    set .p2y=GetUnitY(.targ)
                endif
                // interpolate dum to its next position along the curve path
                call SetUnitX(.dum,.p0x*.a*.a+.p1x*2*.a*b+.p2x*b*b)
                call SetUnitY(.dum,.p0y*.a*.a+.p1y*2*.a*b+.p2y*b*b)
            endif
            
            set i=i+1
        endloop
        
        if C==0 then
            call PauseTimer(T)
        endif
    endmethod
    
    static method actions takes real angle, real width returns nothing
        local data this=data.allocate()
        
        set .cast=GetTriggerUnit()
        set .targ=GetSpellTargetUnit()
        set .p0x=GetUnitX(.cast)
        set .p0y=GetUnitY(.cast)
        set .p2x=GetUnitX(.targ)
        set .p2y=GetUnitY(.targ)
        set .ang=Atan2(.p2y-.p0y,.p2x-.p0x)
        set .dist=SquareRoot(Pow(.p0x-.p2x,2)+Pow(.p0y-.p2y,2))
        // coords at the end of a perpendicular line extending out from the midpoint of the line connecting p0 and p1
        set .p1x=(.p0x+.dist/2.*Cos(.ang))+width*Cos(.ang+angle)
        set .p1y=(.p0y+.dist/2.*Sin(.ang))+width*Sin(.ang+angle)   
            
        // angular distance between the aforementioned line and the line connecting p0 and p2
        set .sigma=Atan2(.p1y-.p0y,.p1x-.p0x)-.ang 
        // change in angle required/TIMEOUT to rotate dums facing from .ang+.sigma to .ang-.sigma
        set .delta=(.sigma*2.)/(1./.speed)    
        set .dum=CreateUnit(GetTriggerPlayer(),DUM,.p0x,.p0y,(.ang+.sigma)*bj_RADTODEG) 
        set .sfx=AddSpecialEffectTarget(SFX,.dum,&quot;origin&quot;)
        call UnitAddAbility(.dum,&#039;Amrf&#039;)  
        call SetUnitFlyHeight(.dum,.defaultheight,0.)
        
        set C=C+1
        set D[C]=this
        if C==1 then
            call TimerStart(T,TIMEOUT,true,function data.onPeriodic)
        endif
    endmethod
    static method conditions takes nothing returns boolean
        local integer i=100
        if GetSpellAbilityId()==ABIL then
            loop
                exitwhen i&gt;2000
                if i==1000 then
                    call data.actions(0.,0.)
                else
                    call data.actions(bj_PI/2.,1000.-i)
                endif
                set i=i+100
            endloop
        endif
        return false
    endmethod
    
    static method onInit takes nothing returns nothing
        local trigger trig=CreateTrigger()
        local integer i=0
        loop
            call TriggerRegisterPlayerUnitEvent(trig,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
            set i=i+1
            exitwhen i==bj_MAX_PLAYERS
        endloop
        call TriggerAddCondition(trig,Condition(function data.conditions))
    endmethod
endstruct

endscope</i></i>


really shows off the power - added in a parabola for good measure
 

NeuroToxin

New Member
Reaction score
46
@emjlr3 So, now that i have it working, I have a question, It loops back the same way it came, is there a way to fix that? Like, heres my code and map.
JASS:

scope Steal initializer Init
    globals
//The spell id of the spell, &quot;Life Steal&quot;
        private constant integer SPELLID = &#039;A002&#039;
//The dummy id of the dummy unit, attack dummy
        private constant integer DUMID = &#039;h001&#039;
//The boolean to use souls, note that souls are gathered from Soul Portal
        private constant boolean USE_SOULS = true
//The interval for the timer
        private constant real TINTERVAL = 0.03125
//The offset per TINTERVAL seconds.
        private constant real OFFSET = 20
//The knockback when a bolt hits a unit.
        private constant real KNOCKBACK = 40
//The width of the angle for the Bezier Curve, Wider means more curves, and vice versa
        private constant real WIDTH = 300
    endglobals
    
    private constant function HPSTEAL takes integer lvl returns real
        return .01 + (lvl * .01)
    endfunction
    
    globals
        private hashtable ht = InitHashtable()
    endglobals
    
    private function GetParabolaZ takes real x, real dist, real height returns real
        return 4.*height*x*(dist-x)/(dist*dist)
    endfunction
    
    private function MoveDamage takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit attacker = LoadUnitHandle( ht, GetHandleId(t), 0)
    local unit target = LoadUnitHandle( ht, GetHandleId(t), 1)
    local unit dummy = LoadUnitHandle( ht, GetHandleId(t), 2)
    local boolean returning = LoadBoolean( ht, GetHandleId(t), 3)
    local real a = LoadReal( ht, GetHandleId(t), 5)
    local real r = LoadReal( ht, GetHandleId(t), 8)
    local real tx = GetUnitX(target)
    local real ty = GetUnitY(target)
    local real dx = GetUnitX(dummy)
    local real dy = GetUnitY(dummy)
    local real p1x = LoadReal( ht, GetHandleId(t), 6)
    local real p1y = LoadReal( ht, GetHandleId(t), 7)
    local real distx = tx - dx
    local real disty = ty - dy
    local real tempreal = SquareRoot(distx * distx + disty * disty)
    local real angle
    local real offsetx
    local real offsety
    local real damage = HPSTEAL(GetUnitAbilityLevel(attacker, SPELLID))
    local real cx = GetUnitX(attacker)
    local real cy = GetUnitY(attacker)
    local real dx1 = dx - cx
    local real dy1 = dy - cy
    local real casterdist = SquareRoot(dx1 * dx1 + dy1 * dy1)
    local real lifestolen
    local real xx = tx - cx
    local real yy = ty - cy
    local real dist = SquareRoot(Pow(tx - cx, 2) + Pow(ty - cy, 2))
    local real b = 1.-r
    if dummy == null or attacker == null or target == null then
        call KillUnit(dummy)
        call FlushChildHashtable( ht, GetHandleId(t))
        call PauseTimer(t)
        call DestroyTimer(t)
    elseif tempreal &gt;= 50 and returning == false then
        set angle = Atan2(ty - dy, tx - dx)
        call SetUnitX(dummy, cx*r*r+p1x*2*r*b+tx*b*b)
        call SetUnitY(dummy, cy*r*r+p1y*2*r*b+ty*b*b)
        call SetUnitFlyHeight( dummy, GetParabolaZ(a, dist, 300) + 40, 0)
        set a = a + OFFSET
        call SaveReal( ht, GetHandleId(t), 5, a)
        call SetUnitFacing( dummy, bj_RADTODEG * angle)
        set r = r - .05
        call SaveReal( ht, GetHandleId(t), 8, r)
    elseif returning == false and tempreal &lt;= 51 then
        set angle = Atan2(ty - cy, tx - cx)
        set offsetx = tx + KNOCKBACK * Cos(angle)
        set offsety = ty + KNOCKBACK * Sin(angle)
        call SetUnitX(target, offsetx)
        call SetUnitY(target, offsety)
        set p1x = (cx + dist/2. * Cos(angle)) + WIDTH * Cos(angle + bj_PI/2.)
        set p1y = (cy + dist/2 * Sin(angle)) + WIDTH * Sin(angle + bj_PI/2.)
        call SaveReal( ht, GetHandleId(t), 6, p1x)
        call SaveReal( ht, GetHandleId(t), 7, p1y)
        call SetWidgetLife( target, GetWidgetLife(target) - (GetWidgetLife(target) * damage))
        set lifestolen = GetWidgetLife(target) * damage
        call SaveReal( ht, GetHandleId(t), 4, lifestolen)
        set returning = true
        call SaveBoolean( ht, GetHandleId(t), 3, returning)
    elseif returning == true and casterdist &gt;= 50 then
        set angle = Atan2(cy - dy, cx - dx)
        call SetUnitFlyHeight( dummy, GetParabolaZ(a, dist, 300) + 40, 0)
        set a = a - OFFSET
        call SaveReal( ht, GetHandleId(t), 5, a)
        call SetUnitX(dummy, cx*r*r+p1x*2*r*b+tx*b*b)
        call SetUnitY(dummy, cy*r*r+p1y*2*r*b+ty*b*b)
        call SetUnitFacing( dummy, bj_RADTODEG * angle)
        set r = r + 0.05
        call SaveReal( ht, GetHandleId(t), 8, r)
    else
        set lifestolen = LoadReal( ht, GetHandleId(t), 4)
        call SetWidgetLife( attacker, GetWidgetLife(attacker) + lifestolen)
        call KillUnit(dummy)
        call FlushChildHashtable( ht, GetHandleId(t))
        call PauseTimer(t)
        call DestroyTimer(t)
    endif
        set attacker = null
        set target = null
        set dummy = null
        set t = null
    endfunction
    
    private function OnCast takes nothing returns boolean
    local timer t
    local integer i = GetUnitAbilityLevel(GetAttacker(), SPELLID)
    local unit attacker
    local unit target
    local unit dummy
    local real x
    local real y
    local real a
    local boolean returning
    local real p1x
    local real p1y
    local real r
    local real tx
    local real ty
    local real dist
    local real angle
    local real dx
    local real dy
    if i &gt; 0 then
        if Count[GetUnitId(GetAttacker())] &gt; 0 or USE_SOULS == false then
            set t = CreateTimer()
            set attacker = GetAttacker()
            set target = GetTriggerUnit()
            set x = GetUnitX(attacker)
            set y = GetUnitY(attacker)
            set tx = GetUnitX(target)
            set ty = GetUnitY(target)
            set dx = tx - x
            set dy = ty - y
            set dist = SquareRoot(Pow(x -tx, 2) + Pow(y - ty, 2))
            set angle = Atan2( ty - y, tx - x)
            set returning = false
            set r = 0
            set a = 1
            set p1x = (x+ dist/2. * Cos(angle)) + WIDTH * Cos(angle + bj_PI/2.)
            set p1y = (y + dist/2 * Sin(angle)) + WIDTH * Sin(angle + bj_PI/2.)
            set dummy = CreateUnit( GetTriggerPlayer(), DUMID, x, y, GetRandomReal(0, 360))
            call SaveUnitHandle( ht, GetHandleId(t), 0, attacker)
            call SaveUnitHandle( ht, GetHandleId(t), 1, target)
            call SaveUnitHandle( ht, GetHandleId(t), 2, dummy)
            call SaveReal( ht, GetHandleId(t), 5, r)
            call SaveReal( ht, GetHandleId(t), 6, p1x)
            call SaveReal( ht, GetHandleId(t), 7, p1y)
            call SaveReal( ht, GetHandleId(t), 8, a)
            call SaveBoolean( ht, GetHandleId(t), 3, returning)
            call TimerStart( t, TINTERVAL, true, function MoveDamage)
            if USE_SOULS == true then
                set Count[GetUnitId(attacker)] = Count[GetUnitId(attacker)] - 1
            endif
            set attacker = null
            set t = null
            set dummy = null
            set target = null
        endif
    endif
    return true
    endfunction

    //===========================================================================
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger(  )
        call TriggerAddCondition( t, Condition( function OnCast) )
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ATTACKED)
    endfunction
endscope

View attachment Soul Gathering.w3x
 

emjlr3

Change can be a good thing
Reaction score
395
w/o going into some of the weird stuff you are doing, you can either change p2 to -width or -bj_PI/2., your choice

my example shows how its done
 

emjlr3

Change can be a good thing
Reaction score
395
for starters. one hashtable/spell is a bad idea - you could just as easily use one for your entire map

beyond that, using hashtables for attachment isn't terrible, when passing a struct, but using it for every variable is lame

what is up with this?

JASS:
local real xx = tx - cx
    local real yy = ty - cy
    local real dist = SquareRoot(Pow(tx - cx, 2) + Pow(ty - cy, 2))


there are several other instances where you already have done some arithmetic and stored it to a variable, but then do it again instead of using the variable.

then something like this

JASS:
set r = r + 0.05
        call SaveReal( ht, GetHandleId(t), 8, r)


could just as easily be
JASS:
call SaveReal( ht, GetHandleId(t), 8, r+.05)


and for god sakes, store GetHandleId(t) instead of calling the native a hundred times, this is like Local Handle Vars all over again

I understand that you took the other spell and just added some content, but at least try to make it your own
 

NeuroToxin

New Member
Reaction score
46
I took like 2 things from your spell. I took the parabola and the bezier, other than that I took nothing...
Also,
emjlr3 said:
what is up with this?
JASS:
local real xx = tx - cx
    local real yy = ty - cy
    local real dist = SquareRoot(Pow(tx - cx, 2) + Pow(ty - cy, 2))
The distance between the unit and the caster has most likely changed since the unit went towards the targeted unit, therefore, we need the dist between the caster and the target, for the Bezier Curve.
 

emjlr3

Change can be a good thing
Reaction score
395
I took like 2 things from your spell. I took the parabola and the bezier, other than that I took nothing...

i meant the first spell posted at the top of the page
Also,
The distance between the unit and the caster has most likely changed since the unit went towards the targeted unit, therefore, we need the dist between the caster and the target, for the Bezier Curve.

yes but you store xx and yy, but don't use them, and run the math again
 

NeuroToxin

New Member
Reaction score
46
Oh. I see. I thought you were talking about the bezier function. My bad. Thank you, I also moved the math Pow math line to the part where I reset the coords.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top