• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[JASS] Help with XE and doing missile math. (Grenade)

Status
Not open for further replies.
Level 13
Joined
Jul 26, 2008
Messages
1,009
Hi, I've been trying to get this missile do it's actions properly but haven't had much luck. For whatever reason the missile effect won't move, and since it won't move it won't destroy itself when it gets close to the destination.

I need someone to check the trigger and tell me what the problem is, as I can't tell if it's the math, which I'm terrible at and have copied from a few example grenade spells, and therefore not all the equations may be added, or of it's the actual code and something I've forgotten.

JASS:
private function JumpParabola takes real dist, real maxdist,real maxheight returns real
    local real t = (dist*2)/maxdist-1    
    return (-t*t+1)*maxheight
endfunction
   //

private function MissileMove takes nothing returns nothing
 local timer tim = GetExpiredTimer()
 local Data D = Data(GetTimerData(tim))
 local real maxHeight = 500.
 local real cur_dist = SquareRoot((D.fx.x - D.X2)*(D.fx.x - D.X2)+(D.fx.y - D.Y2)*(D.fx.y-D.Y2))
 local real fly = JumpParabola(cur_dist, D.maxDist, maxHeight)//Shadow1500 jump parabola 
 local real angle = Atan2(D.X2 - GetUnitY(D.c), D.Y2 - GetUnitX(D.c))
 local real moveX              = D.fx.x + 21. * Cos(angle)
 local real moveY              = D.fx.y + 21. * Sin(angle)
 local timer tim2
    if cur_dist <= 21.0 then
        //Do Damage Stuff Here
    
        //End Damage Stuff
        call D.fx.destroy()
        call ReleaseTimer(tim)
        
    else
        set D.fx.x = moveX
        set D.fx.y = moveY 
        set D.fx.z = fly  
    endif
    
endfunction
//

private function Actions takes nothing returns nothing
 local timer tim = NewTimer()
 local timer tim2 = NewTimer()
 local real X1 = GetUnitX(GetTriggerUnit())
 local real Y1 = GetUnitY(GetTriggerUnit())
 local real a = Atan2(GetSpellTargetX() - GetUnitY(GetTriggerUnit()), GetSpellTargetY() - GetUnitX(GetTriggerUnit()))
 local xefx fx = xefx.create(GetUnitX(GetTriggerUnit())+20.0*Cos(a), GetUnitY(GetTriggerUnit())+20.0*Sin(a), a)
 local Data D = Data.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY(), CreateGroup(), fx.x, fx.y)
 
 local real til = SquareRoot(Pow(X1 - D.X2, 2) + Pow(Y1 - D.Y2, 2)) / 700.
    
    
    set fx.fxpath = "Abilities\\Weapons\\BatTrollMissile\\BatTrollMissile.mdl"
    
    call SetTimerData(tim, D)
    call TimerStart(tim, 0.03, true, function MissileMove)
    call TriggerSleepAction(til)
    set caster = D.c
    call DestroyEffect(AddSpecialEffect("Abilities\\Weapons\\DemolisherFireMissile\\DemolisherFireMissile.mdl", D.X2, D.Y2))
    set damage = 25 + 100 * GetUnitAbilityLevel(D.c, SpellID)
    call GroupEnumUnitsInRange(D.g, D.X2, D.Y2, 150 + 45. * (GetUnitAbilityLevel(D.c, SpellID) - 1), function GroupEnum)
    call SetTimerData(tim2, D)
    call TimerStart(tim2, 1., true, function Timer)
        call D.fx.destroy()
 set tim = null
endfunction

JASS:
//globals

globals
    private constant integer    SpellID = 'bomb'
    private constant damagetype dmg     = DAMAGE_TYPE_MAGIC
    private constant weapontype wpn     = WEAPON_TYPE_WHOKNOWS
    private constant attacktype atk     = ATTACK_TYPE_PIERCE
    private constant string  MODEL_PATH_MISSILE       = "Abilities\\Weapons\\BatTrollMissile\\BatTrollMissile.mdl"
    private constant string  MODEL_PATH_FLASH         = null
    private unit caster
    private real damage
endglobals
//Struct

private struct Data
    unit c
    real X2
    real Y2
    real til
    real dur
    group g
    real maxDist
    xefx fx
    
    static method create takes unit caster, real targX, real targY, group gro, real fxx, real fxy returns Data
     local Data D = Data.allocate()
        set D.c = caster
        set D.X2 = targX
        set D.Y2 = targY
        set D.dur = 0.
        set D.g = gro
        set D.fx.x = fxx
        set D.fx.y = fxy
        set D.fx.z = 60
        set D.maxDist = SquareRoot((D.X2 - D.fx.x)*(D.X2 - D.fx.x)+(D.Y2 - D.fx.y)*(D.Y2 - D.fx.y))
     return D
    endmethod
    
endstruct

JASS:
scope AlcheBomb initializer Init

globals
    private constant integer    SpellID = 'bomb'
    private constant damagetype dmg     = DAMAGE_TYPE_MAGIC
    private constant weapontype wpn     = WEAPON_TYPE_WHOKNOWS
    private constant attacktype atk     = ATTACK_TYPE_PIERCE
    private constant string  MODEL_PATH_MISSILE       = "Abilities\\Weapons\\BatTrollMissile\\BatTrollMissile.mdl"
    private constant string  MODEL_PATH_FLASH         = null
    private unit caster
    private real damage
endglobals

private function GroupEnum takes nothing returns boolean
    if IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(caster)) then
        call UnitDamageTarget(caster, GetFilterUnit(), damage, true, false, atk, dmg, wpn)
        call DestroyEffect(AddSpecialEffectTarget("Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl", GetFilterUnit(), "origin"))
    endif
 return false
endfunction

private function JumpParabola takes real dist, real maxdist,real maxheight returns real
    local real t = (dist*2)/maxdist-1    
    return (-t*t+1)*maxheight
endfunction
   
private struct Data
    unit c
    real X2
    real Y2
    real til
    real dur
    group g
    real maxDist
    xefx fx
    
    static method create takes unit caster, real targX, real targY, group gro, real fxx, real fxy returns Data
     local Data D = Data.allocate()
        set D.c = caster
        set D.X2 = targX
        set D.Y2 = targY
        set D.dur = 0.
        set D.g = gro
        set D.fx.x = fxx
        set D.fx.y = fxy
        set D.fx.z = 60
        set D.maxDist = SquareRoot((D.X2 - D.fx.x)*(D.X2 - D.fx.x)+(D.Y2 - D.fx.y)*(D.Y2 - D.fx.y))
     return D
    endmethod
    
endstruct

private function Conditions takes nothing returns boolean    
    return GetSpellAbilityId() == SpellID
endfunction

private function Timer takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Data D = Data(GetTimerData(tim))
    if D.dur >= 5. then
        call ReleaseTimer(tim)
        call D.destroy()
    else
    set damage = 5 * GetUnitAbilityLevel(D.c, SpellID)
    call GroupEnumUnitsInRange(D.g, D.X2, D.Y2, 150 + 45. * GetUnitAbilityLevel(D.c, SpellID), function GroupEnum)
    set D.dur = D.dur + 1.
    endif
endfunction

private function MissileMove takes nothing returns nothing
 local timer tim = GetExpiredTimer()
 local Data D = Data(GetTimerData(tim))
 local real maxHeight = 500.
 local real cur_dist = SquareRoot((D.fx.x - D.X2)*(D.fx.x - D.X2)+(D.fx.y - D.Y2)*(D.fx.y-D.Y2))
 local real fly = JumpParabola(cur_dist, D.maxDist, maxHeight)//Shadow1500 jump parabola 
 local real angle = Atan2(D.X2 - GetUnitY(D.c), D.Y2 - GetUnitX(D.c))
 local real moveX              = D.fx.x + 21. * Cos(angle)
 local real moveY              = D.fx.y + 21. * Sin(angle)
 local timer tim2
    if cur_dist <= 21.0 then
        //Do Damage Stuff Here
    
        //End Damage Stuff
        call D.fx.destroy()
        call ReleaseTimer(tim)
        
    else
        set D.fx.x = moveX
        set D.fx.y = moveY 
        set D.fx.z = fly  
    endif
    
endfunction

private function Actions takes nothing returns nothing
 local timer tim = NewTimer()
 local timer tim2 = NewTimer()
 local real X1 = GetUnitX(GetTriggerUnit())
 local real Y1 = GetUnitY(GetTriggerUnit())
 local real a = Atan2(GetSpellTargetX() - GetUnitY(GetTriggerUnit()), GetSpellTargetY() - GetUnitX(GetTriggerUnit()))
 local xefx fx = xefx.create(GetUnitX(GetTriggerUnit())+20.0*Cos(a), GetUnitY(GetTriggerUnit())+20.0*Sin(a), a)
 local Data D = Data.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY(), CreateGroup(), fx.x, fx.y)
 
 local real til = SquareRoot(Pow(X1 - D.X2, 2) + Pow(Y1 - D.Y2, 2)) / 700.
    
    
    set fx.fxpath = "Abilities\\Weapons\\BatTrollMissile\\BatTrollMissile.mdl"
    
    call SetTimerData(tim, D)
    call TimerStart(tim, 0.03, true, function MissileMove)
    call TriggerSleepAction(til)
    set caster = D.c
    call DestroyEffect(AddSpecialEffect("Abilities\\Weapons\\DemolisherFireMissile\\DemolisherFireMissile.mdl", D.X2, D.Y2))
    set damage = 25 + 100 * GetUnitAbilityLevel(D.c, SpellID)
    call GroupEnumUnitsInRange(D.g, D.X2, D.Y2, 150 + 45. * (GetUnitAbilityLevel(D.c, SpellID) - 1), function GroupEnum)
    call SetTimerData(tim2, D)
    call TimerStart(tim2, 1., true, function Timer)
        call D.fx.destroy()
 set tim = null
endfunction

//===========================================================================
private function Init takes nothing returns nothing  
 local trigger t = CreateTrigger()
  call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )  
  call TriggerAddCondition( t, Condition( function Conditions ) )  
  call TriggerAddAction( t, function Actions )
  call XE_PreloadAbility(SpellID)
endfunction

endscope
 
Level 4
Joined
Jan 27, 2010
Messages
133
You never assign D.fx. You should pass xefx through the struct create function.

JASS:
struct Data
    static method create takes xefx fx, etc...
       local Data d = Data.allocate()
       set d.fx = fx

      // etc....
    endmethod
endstruct

// In function Actions:
local xefx fx = xefx.create( ... )
local Data D = Data.create(fx, ...)

I'm also concerned about this:

JASS:
    call TimerStart(tim2, 1., true, function Timer)
        call D.fx.destroy() // Destroy the effect instantly?
 set tim = null

EDIT

And finally, you're a bit lost on the maths... :p

Store the x and y speeds in the struct. Don't compute them again for each loop.

JASS:
// In function Actions:
// Atan2 wants ( difference in Y, difference in X )
 local real angle = Atan2(GetSpellTargetY() - GetUnitY(GetTriggerUnit()), GetSpellTargetX() - GetUnitX(GetTriggerUnit()))
 set D.x_speed = 21 * Cos(angle)
 set D.y_speed = 21 * Sin(angle)

// In function MissileMove:
        set D.fx.x = D.x_speed
        set D.fx.y = D.y_speed
        set D.fx.z = fly
 
Level 13
Joined
Jul 26, 2008
Messages
1,009
Ahh thanks. It totally slipped my mind that I needed to assign D.fx :X Looking at it I understand how I missed it as I'm still learning, but it's obvious now.

As for the part where you're concerned, that was a temporary copy and paste from where it use to be in the MissileMove actions between the two comments to add damage. It was so that the TriggerSleepAction(til) would pause then just destroy the effect and do the damage, since the MissileMove timer wasn't working properly.

I changed th Atan2 for the missile move actions as well. That's definently good to know so I can compute angles now with this given formula :D

EDIT: AH! I found something. This:

set D.fx.x = D.x_speed
set D.fx.y = D.y_speed

should be this:

local real moveX = D.fx.x + D.x_speed
local real moveY = D.fx.y + D.y_speed

set D.fx.x = moveX
set D.fx.y = moveY

Adjustment made, and the spell moves correctly. The only problem now is that the final timer won't go off, which does repeat damage in the AoE where the grenade was lobbed.

JASS:
scope AlcheBomb initializer Init

globals
    private constant integer    SpellID = 'bomb'
    private constant damagetype dmg     = DAMAGE_TYPE_MAGIC
    private constant weapontype wpn     = WEAPON_TYPE_WHOKNOWS
    private constant attacktype atk     = ATTACK_TYPE_PIERCE
    private constant string  MODEL_PATH_MISSILE       = "Abilities\\Weapons\\BatTrollMissile\\BatTrollMissile.mdl"
    private constant string  MODEL_PATH_FLASH         = null
    private unit caster
    private real damage
endglobals

private function GroupEnum takes nothing returns boolean
    if IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(caster)) then
        call UnitDamageTarget(caster, GetFilterUnit(), damage, true, false, atk, dmg, wpn)
        call DestroyEffect(AddSpecialEffectTarget("Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl", GetFilterUnit(), "origin"))
    endif
 return false
endfunction

private function JumpParabola takes real dist, real maxdist,real maxheight returns real
    local real t = (dist*2)/maxdist-1    
    return (-t*t+1)*maxheight
endfunction
   
private struct Data
    unit c
    real X2
    real Y2
    real til
    real dur
    group g
    real maxDist
    xefx fx
    real x_speed
    real y_speed
    
    static method create takes xefx fx, unit caster, real targX, real targY, group gro, real fxx, real fxy returns Data
     local Data D = Data.allocate()
        set D.fx = fx
        set D.c = caster
        set D.X2 = targX
        set D.Y2 = targY
        set D.dur = 0.
        set D.g = gro
        set D.fx.x = fxx
        set D.fx.y = fxy
        set D.fx.z = 60
        set D.maxDist = SquareRoot((D.X2 - D.fx.x)*(D.X2 - D.fx.x)+(D.Y2 - D.fx.y)*(D.Y2 - D.fx.y))
     return D
    endmethod
    
endstruct

private function Conditions takes nothing returns boolean    
    return GetSpellAbilityId() == SpellID
endfunction

private function Timer takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Data D = Data(GetTimerData(tim))
    if D.dur >= 5. then
        call ReleaseTimer(tim)
        call D.destroy()
    else
    set damage = 5 * GetUnitAbilityLevel(D.c, SpellID)
    call GroupEnumUnitsInRange(D.g, D.X2, D.Y2, 150 + 45. * GetUnitAbilityLevel(D.c, SpellID), function GroupEnum)
    set D.dur = D.dur + 1.
    endif
endfunction

private function MissileMove takes nothing returns nothing
 local timer tim = GetExpiredTimer()
 local Data D = Data(GetTimerData(tim))
 local real maxHeight = 150.
 local real cur_dist = SquareRoot((D.fx.x - D.X2)*(D.fx.x - D.X2)+(D.fx.y - D.Y2)*(D.fx.y-D.Y2))
 local real fly = JumpParabola(cur_dist, D.maxDist, maxHeight)//Shadow1500 jump parabola 
 local real angle = Atan2(D.Y2 - GetUnitY(D.c), D.X2 - GetUnitX(D.c))
 local real moveX = D.fx.x + D.x_speed 
 local real moveY = D.fx.y + D.y_speed
 local timer tim2
    if cur_dist <= 15.0 then
        //Do Damage Stuff Here
    call DestroyEffect(AddSpecialEffect("Abilities\\Weapons\\DemolisherFireMissile\\DemolisherFireMissile.mdl", D.X2, D.Y2))
    set damage = 25 + 100 * GetUnitAbilityLevel(D.c, SpellID)
    set caster = D.c
    call GroupEnumUnitsInRange(D.g, D.X2, D.Y2, 105 + 45. * GetUnitAbilityLevel(D.c, SpellID), function GroupEnum)
    call D.fx.destroy()
    call ReleaseTimer(tim)
    call SetTimerData(tim2, D)
    call TimerStart(tim2, 1., true, function Timer)
        //end
    else
        set D.fx.x = moveX
        set D.fx.y = moveY
        set D.fx.z = fly  
    endif
    
endfunction

private function Actions takes nothing returns nothing
 local timer tim = NewTimer()
 local timer tim2 = NewTimer()
 local real X1 = GetUnitX(GetTriggerUnit())
 local real Y1 = GetUnitY(GetTriggerUnit())
 local real angle = Atan2(GetSpellTargetY() - GetUnitY(GetTriggerUnit()), GetSpellTargetX() - GetUnitX(GetTriggerUnit()))
 local xefx fx = xefx.create(GetUnitX(GetTriggerUnit())+20.0*Cos(angle), GetUnitY(GetTriggerUnit())+20.0*Sin(angle), angle)
 local Data D = Data.create(fx, GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY(), CreateGroup(), fx.x, fx.y)
 
 //local real til = SquareRoot(Pow(X1 - D.X2, 2) + Pow(Y1 - D.Y2, 2)) / 700.
        set D.x_speed = 15. * Cos(angle) 
        set D.y_speed = 15. * Sin(angle)
        set fx.fxpath = "Abilities\\Weapons\\BatTrollMissile\\BatTrollMissile.mdl"
     
    call SetTimerData(tim, D)
    call TimerStart(tim, 0.03, true, function MissileMove)
 //   call TriggerSleepAction(til)
endfunction

//===========================================================================
private function Init takes nothing returns nothing  
 local trigger t = CreateTrigger()
  call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )  
  call TriggerAddCondition( t, Condition( function Conditions ) )  
  call TriggerAddAction( t, function Actions )
  call XE_PreloadAbility(SpellID)
endfunction

endscope
 
Last edited:
Status
Not open for further replies.
Top