HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Height not working for custom projectiles

08-07-2008, 06:01 AM#1
Bobo_The_Kodo
I am trying to make a projectile system to handle all the projectiles for my map. There is one problem though,
the height of the projectile will increase/decrease with the terrain height and I don't know what's causing it.
The movement type for the dummy unit is NOT fly or hover

(It will only work properly if I cast it from 0 terrain height)

I would appreciate if someone helped me find the problem

Collapse JASS:
library ProjectileSystem initializer Init

globals
    private constant integer DummyRawcode          = 'h000'
    private timer Timer                            = CreateTimer()
    private integer Counter                        = 0
    private integer array Projectiles
    private constant real TimerInterval            = 0.03
    private boolexpr Boolexpr
    private group EnumGrp                          = CreateGroup()
    private location Zloc                          = Location( 0, 0 )
    
    private real Temp1
    private real Temp2
    private integer TempInt
    private unit TempUnit
    private unit TempKnocked
endglobals

private struct Projectile
    unit missile
    effect sfx
    real x
    real y
    real z
    real fly
    real speed
    real time
    real incX
    real incY
    real incZ
    real startZ
    real lastZ
    real radius
    unit origin
    integer damageType
    boolean hitend
    string onhit
    
    
    static method create takes real x, real y, real z, real tx, real ty, real tz, real speed, real radius, integer damageType, unit origin, string sfx, string onhit, boolean hitend returns Projectile
        local Projectile p = Projectile.allocate()
        local real dist = SquareRoot( ( tx - x ) * ( tx - x ) + ( ty - y ) * ( ty - y ) + ( tz - z ) * ( tz - z ) )
        local real angle = Atan2( ty - y, tx - x )

        set p.origin = origin        
        set p.missile = CreateUnit( GetOwningPlayer( origin ), DummyRawcode, x, y, Atan2( ty - y, tx - x ) * bj_RADTODEG )
        call UnitAddAbility( p.missile, 'Amrf' )
        call UnitRemoveAbility( p.missile, 'Amrf' )
        set p.sfx = AddSpecialEffectTarget( sfx, p.missile, "origin" )
        set p.hitend = hitend
        
        set p.time = dist / speed
        set p.incX = dist * Cos( angle ) / ( p.time / TimerInterval )
        set p.incY = dist * Sin( angle ) / ( p.time / TimerInterval )
        set p.incZ = ( tz - z ) / ( p.time / TimerInterval )
        set p.x = x
        set p.y = y
        set p.z = z
        call MoveLocation( Zloc, x, y )
        set p.startZ = GetLocationZ( Zloc )
        set p.radius = radius
        set p.damageType = damageType
        
        set p.onhit = onhit
        return p
    
    endmethod
    private method onDestroy takes nothing returns nothing
        call DestroyEffect( .sfx )
        call KillUnit( .missile )
    endmethod
endstruct

private function ProjectileHit takes nothing returns boolean
    local Projectile p = TempInt
    local unit u = GetFilterUnit()
    if GetWidgetLife( u ) > 0 and GetUnitFlyHeight( u ) + 50 < p.fly + p.radius and GetUnitFlyHeight( u ) + 50 > p.fly - p.radius and IsUnitEnemy( u, GetOwningPlayer( p.origin ) ) then
        call UnitDamageTargetEx( p.origin, u, 100, ATTACK_TYPE_NORMAL, p.damageType, false )
        if p.onhit != null and p.onhit != "" then
            call ExecuteFunc( p.onhit )
        endif
        if p.hitend then
            set p.time = 0
        endif
    endif
    set u = null
    return false
endfunction

private function Update takes nothing returns nothing
    local integer i = Counter - 1
    local Projectile p
    
    loop
        exitwhen i < 0
        set p = Projectiles[i]
        set p.time = p.time - TimerInterval
        if p.time > 0 then
            set p.x = p.x + p.incX
            set p.y = p.y + p.incY
            call MoveLocation( Zloc, p.x, p.y )
            set p.z = p.z + p.incZ
            set p.fly = p.z + p.startZ - GetLocationZ( Zloc )
            set p.lastZ = p.fly
            call SetUnitAnimationByIndex( p.missile, R2I( Acos( ( p.fly - p.lastZ ) / SquareRoot( p.incX * p.incX + p.incY * p.incY + ( p.fly - p.lastZ ) * ( p.fly - p.lastZ ) ) ) * bj_RADTODEG ) )
            call SetUnitX( p.missile, p.x )
            call SetUnitY( p.missile, p.y )
            call SetUnitFlyHeight( p.missile, p.fly, 0 )
            if p.fly < 0 then
                set p.time = 0
            endif
            set TempInt = p
            call GroupEnumUnitsInRange( EnumGrp, p.x, p.y, p.radius, Boolexpr )
        else
            call p.destroy()
            set Counter = Counter - 1
            if Counter < 0 then
                call PauseTimer(Timer)
                set Counter = 0
            else
                set Projectiles[i] = Projectiles[Counter]
            endif
        endif
        
        set i = i - 1
    endloop
endfunction

function ProjectileStop takes unit targ returns boolean
    local integer i = Counter - 1
    local Projectile p = 0
    local boolean b = false
    loop
        exitwhen i < 0
        set p = Projectiles[i]
        if p.missile == targ then
            call p.destroy(p)
            set Counter = Counter - 1
            if Counter < 0 then
                call PauseTimer(Timer)
                set Counter = 0
            else
                set Projectiles[i] = Projectiles[Counter]
            endif
            set b = true
        endif
        set i = i - 1
    endloop
    return b
endfunction


function AddProjectile takes real x, real y, real z, real tx, real ty, real tz, real speed, real radius, integer damageType, unit origin, string sfx, string onhit, boolean hitend returns boolean
    local Projectile p = 0

    set p = Projectile.create( x, y, z, tx, ty, tz, speed, radius, damageType, origin, sfx, onhit, hitend )
    if Counter == 0 then
        call TimerStart(Timer, TimerInterval, true, function Update)
    endif
    set Projectiles[Counter] = p
    set Counter = Counter + 1
    return true
endfunction

private function Init takes nothing returns nothing
    set Boolexpr = Condition( function ProjectileHit )
endfunction
endlibrary

This is the function I use to create the projectile:

Collapse JASS:
    private function Fireball takes nothing returns nothing
        local unit u = GetTriggerUnit()
        local location l = GetSpellTargetLoc()
        call AddProjectile( GetUnitX( u ), GetUnitY( u ), GetUnitFlyHeight( u ) + 65, GetLocationX( l ), GetLocationY( l ), GetLocationZ( l ), 3000, 85, DAMAGE_FIRE, u, "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl", "", true )
        call RemoveLocation( l )
        set l = null
        set u = null
    endfunction
08-07-2008, 09:52 AM#2
Troll-Brain
GetUnitZ(u) == GetUnitFlyingHeight(u) + GetLocationZ(GetUnitLoc(u))
That's why it works only for 0 terrain height.
08-07-2008, 10:28 AM#3
dorreen
I myself, like to use Anitarfs vectors for stuff like this. Use GetLocationZ on units location. This way its allso easy to detect when bullet or projectile hits the ground.

Set Units fly height: Units Z value - GetLocationZ on units location

You should think twice before you do this, because, like Ash said:
Quote:
the '3D bullet system' gives you agent smith properties
08-07-2008, 11:07 AM#4
d07.RiV
he does that...

or he fixed that after the comments?
08-07-2008, 11:16 AM#5
Troll-Brain
Quote:
Originally Posted by d07.RiV
he does that...

or he fixed that after the comments?

And where ? , as far i see, he doesn't.
08-07-2008, 12:46 PM#6
d07.RiV
Collapse JASS:
set p.startZ = GetLocationZ( Zloc )
...
set p.fly = p.z + p.startZ - GetLocationZ( Zloc )
call SetUnitFlyHeight( p.missile, p.fly, 0 )

Also, are you using some special model that has a lot of animations for different tilts?
08-07-2008, 02:29 PM#7
Bobo_The_Kodo
Thanks for the replies, but none of these methods work so far.

Also, I am using the dummy model that has 360 different animations for the tilt.
08-07-2008, 02:37 PM#8
Troll-Brain
Hmm correct me if i'm wrong, but you don't use GetLocationZ before calculate the dist in the static method create of your struct.
08-07-2008, 02:52 PM#9
Bobo_The_Kodo
Quote:
set p.startZ = GetLocationZ( Zloc )

That? And I do it for the target location when I create the fireball,

Quote:
GetLocationZ( l )

I think I may do something wrong somewhere there though >.>
08-07-2008, 03:19 PM#10
Troll-Brain
No, that :
Collapse JASS:
local real dist = SquareRoot( ( tx - x ) * ( tx - x ) + ( ty - y ) * ( ty - y ) + ( tz - z ) * ( tz - z ) )
08-07-2008, 03:43 PM#11
Bobo_The_Kodo
Ok, I changed up the code a bit, and the height only glitches up when it goes over water now... Here it is so far:

EDIT: it is now a problem with Warcraft's water being lower than GetLocationZ shows. TY for the help >.>

Collapse JASS:
library ProjectileSystem initializer Init

globals
    private constant integer DummyRawcode          = 'h000'
    private timer Timer                            = CreateTimer()
    private integer Counter                        = 0
    private integer array Projectiles
    private constant real TimerInterval            = 0.03
    private boolexpr Boolexpr
    private group EnumGrp                          = CreateGroup()
    private location Zloc                          = Location( 0, 0 )
    
    private real Temp1
    private real Temp2
    private integer TempInt
    private unit TempUnit
    private unit TempKnocked
endglobals

private struct Projectile
    unit missile
    effect sfx
    real x
    real y
    real z
    real fly
    real speed
    real time
    real incX
    real incY
    real incZ
    real startZ
    real lastZ
    real radius
    unit origin
    integer damageType
    boolean hitend
    string onhit
    
    
    static method create takes real x, real y, real z, real tx, real ty, real tz, real speed, real radius, integer damageType, unit origin, string sfx, string onhit, boolean hitend returns Projectile
        local Projectile p = Projectile.allocate()
        local real dist
        local real angle = Atan2( ty - y, tx - x )
        local real locz
        local real tlocz
        call MoveLocation( Zloc, x, y )
        set locz = GetLocationZ( Zloc )
        call MoveLocation( Zloc, tx, ty )
        set tlocz = GetLocationZ( Zloc )
        set dist = SquareRoot( ( tx - x ) * ( tx - x ) + ( ty - y ) * ( ty - y ) + ( tz + tlocz - ( z + locz ) ) * ( tz + tlocz - ( z + locz ) ) )

        set p.origin = origin        
        set p.missile = CreateUnit( GetOwningPlayer( origin ), DummyRawcode, x, y, Atan2( ty - y, tx - x ) * bj_RADTODEG )
        call UnitAddAbility( p.missile, 'Amrf' )
        call UnitRemoveAbility( p.missile, 'Amrf' )
        set p.sfx = AddSpecialEffectTarget( sfx, p.missile, "origin" )
        set p.hitend = hitend
        
        set p.time = dist / speed
        set p.incX = dist * Cos( angle ) / ( p.time / TimerInterval )
        set p.incY = dist * Sin( angle ) / ( p.time / TimerInterval )
        set p.incZ = ( tz + tlocz - ( z + locz ) ) / ( p.time / TimerInterval )
        set p.x = x
        set p.y = y
        set p.z = z + locz
        set p.radius = radius
        set p.damageType = damageType
        
        set p.onhit = onhit
        return p
    
    endmethod
    private method onDestroy takes nothing returns nothing
        call DestroyEffect( .sfx )
        call KillUnit( .missile )
    endmethod
endstruct

private function ProjectileHit takes nothing returns boolean
    local Projectile p = TempInt
    local unit u = GetFilterUnit()
    if GetWidgetLife( u ) > 0 and GetUnitFlyHeight( u ) + 50 < p.fly + p.radius and GetUnitFlyHeight( u ) + 50 > p.fly - p.radius and IsUnitEnemy( u, GetOwningPlayer( p.origin ) ) then
        call UnitDamageTargetEx( p.origin, u, 100, ATTACK_TYPE_NORMAL, p.damageType, false )
        if p.onhit != null and p.onhit != "" then
            call ExecuteFunc( p.onhit )
        endif
        if p.hitend then
            set p.time = 0
        endif
    endif
    set u = null
    return false
endfunction

private function Update takes nothing returns nothing
    local integer i = Counter - 1
    local Projectile p
    
    loop
        exitwhen i < 0
        set p = Projectiles[i]
        set p.time = p.time - TimerInterval
        if p.time > 0 then
            set p.x = p.x + p.incX
            set p.y = p.y + p.incY
            call MoveLocation( Zloc, p.x, p.y )
            set p.z = p.z + p.incZ
            set p.fly = p.z + p.startZ - GetLocationZ( Zloc )
            call BJDebugMsg( R2S( p.fly ) )
            call BJDebugMsg( R2S( GetLocationZ( Zloc ) ) )
            set p.lastZ = p.fly
            call SetUnitAnimationByIndex( p.missile, R2I( Acos( ( p.fly - p.lastZ ) / SquareRoot( p.incX * p.incX + p.incY * p.incY + ( p.fly - p.lastZ ) * ( p.fly - p.lastZ ) ) ) * bj_RADTODEG ) )
            call SetUnitX( p.missile, p.x )
            call SetUnitY( p.missile, p.y )
            call SetUnitFlyHeight( p.missile, p.fly, 0 )
            if p.fly < 0 then
                set p.time = 0
            endif
            set TempInt = p
            call GroupEnumUnitsInRange( EnumGrp, p.x, p.y, p.radius, Boolexpr )
        else
            call p.destroy()
            set Counter = Counter - 1
            if Counter < 0 then
                call PauseTimer(Timer)
                set Counter = 0
            else
                set Projectiles[i] = Projectiles[Counter]
            endif
        endif
        
        set i = i - 1
    endloop
endfunction

function ProjectileStop takes unit targ returns boolean
    local integer i = Counter - 1
    local Projectile p = 0
    local boolean b = false
    loop
        exitwhen i < 0
        set p = Projectiles[i]
        if p.missile == targ then
            call p.destroy(p)
            set Counter = Counter - 1
            if Counter < 0 then
                call PauseTimer(Timer)
                set Counter = 0
            else
                set Projectiles[i] = Projectiles[Counter]
            endif
            set b = true
        endif
        set i = i - 1
    endloop
    return b
endfunction


function AddProjectile takes real x, real y, real z, real tx, real ty, real tz, real speed, real radius, integer damageType, unit origin, string sfx, string onhit, boolean hitend returns boolean
    local Projectile p = 0

    set p = Projectile.create( x, y, z, tx, ty, tz, speed, radius, damageType, origin, sfx, onhit, hitend )
    if Counter == 0 then
        call TimerStart(Timer, TimerInterval, true, function Update)
    endif
    set Projectiles[Counter] = p
    set Counter = Counter + 1
    return true
endfunction

private function Init takes nothing returns nothing
    set Boolexpr = Condition( function ProjectileHit )
endfunction
endlibrary
08-07-2008, 04:20 PM#12
Troll-Brain
Quote:
EDIT: it is now a problem with Warcraft's water being lower than GetLocationZ shows. TY for the help >.>

Try to use an offset and regions.
Create the region where there is water.
Then check when an unit enter the region, and when it leaves.

Creates as many regions as you have different depths.
08-07-2008, 05:05 PM#13
d07.RiV
I'm pretty sure there's another way to check for water, try using search.
08-07-2008, 05:20 PM#14
Troll-Brain
Try to change the model of your projectile.
For example a flying unit and then a swimming unit.
Maybe the model can affect the flying height.
08-07-2008, 08:15 PM#15
d07.RiV
Why would it be related to a model and not to walking type.

Also please don't use a model with 360 animations >< It should be 180 and you can safely make it for every 5th angle with absolutely no impact on visualisation. I know those animations are tiny but 360 of them is several kilos. Your whole model would probably easily fit into 5-10k .. Even if the missile can change pitch increments of 5 will look well enough at the speeds you get.