Snippet InterceptionData

Komaqtion

You can change this now in User CP.
Reaction score
469
Hello :D

I was just helping out a person in a thread in the WE Help section, and he asked for a rather complex formula to calculate the interception-point of two entities moving.

This formula was made up by perkeyone, but he didn't really know how to put it into JASS code, so I did that and also made a few tweaks and additions to it, and here it is:

JASS:
library InterceptionData

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*********************************************************************************************************//
//@@/////////////////////////////////// *// InterceptionData \\* ////////////////////////////////////////@@//
//@@                                                                                                     @@//
//@@                              Made by , Komaqtion @ TheHelper.net                                    @@//
//@@                                                                                                     @@//
//@@                                                                                                     @@//
//@@                                         Purpose:                                                    @@//
//@@                                                                                                     @@//
//@@         # The purpose of this small snippet is to easily be able to locate a point                  @@//
//@@           in the map, where two entities will meet if their path isn't changed.                     @@//
//@@                                                                                                     @@//
//@@                                         Usage:                                                      @@//
//@@                                                                                                     @@//
//@@         # *NOTE*: All functions here use one of two sets of parameters:                             @@//
//@@                                                                                                     @@//
//@@   Parameter-Set 1:                                                                                  @@//
//@@   'real castX, real castY, real castSpeed, real targX, real targY, real targSpeed, real targAngle'  @@//
//@@                                                                                                     @@//
//@@         real castX, real castY -> These are the coordinates of the entity which you probably        @@//
//@@                                   will want to use to move against the intersection point.          @@//
//@@         real castSpeed         -> This is the current speed of the "casting" entity.                @@//
//@@         real targX, real targY -> These are the current coordinates of the entity which the         @@//
//@@                                   caster will be intersecting                                       @@//
//@@         real targSpeed         -> This is the current speed of the target entity                    @@//
//@@         real targAngle         -> This is the angle of which the target is currently moving         @@//
//@@                                   towards.                                                          @@//
//@@                                                                                                     @@//
//@@   Parameter-Set 2:                                                                                  @@//
//@@   'unit caster, real casterSpeed, unit target, real targetSpeed'                                    @@//
//@@                                                                                                     @@//
//@@         unit caster            -> This is the "casting" unit, or the unit which you will            @@//
//@@                                   use the intersection point with.                                  @@//
//@@         real casterSpeed       -> This is the current speed of the "casting" unit.                  @@//
//@@         unit target            -> This is the target unit, which currently has a heading            @@//
//@@         real targetSpeed       -> This is the current speed of the target unit.                     @@//
//@@                                                                                                     @@//
//@@                                                                                                     @@//
//@@         # Now, the functions provided by this small snippet are:                                    @@//
//@@           *NOTE*: ALL THE ANGLES IN THE PARAMETERS HERE ARE IN RADIANS !                            @@//
//@@                   (To make degrees into radians, simply multiply the angle by bj_DEGTORAD)          @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionLoc takes Parameter-Set 1 returns location                          @@//
//@@                                                                                                     @@//
//@@               - "returns location" -> This function will return the location where                  @@//
//@@                 the two paths will intersect, or cross eachother.                                   @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionLocSimple takes Parameter-Set 2 returns location                    @@//
//@@                                                                                                     @@//
//@@               - "returns location" -> This function will return the location where                  @@//
//@@                 the two paths will intersect, or cross eachother.                                   @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionX takes Parameter-Set 1 returns real                                @@//
//@@                                                                                                     @@//
//@@               - "returns real"     -> This function will return the X-coordinate                    @@//
//@@                 where the two paths will intersect, or cross eachother.                             @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionXSimple takes Parameter-Set 2 returns real                          @@//
//@@                                                                                                     @@//
//@@               - "returns real"     -> This function will return the X-coordinate                    @@//
//@@                 where the two paths will intersect, or cross eachother.                             @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionY takes Parameter-Set 1 returns real                                @@//
//@@                                                                                                     @@//
//@@               - "returns real"     -> This function will return the Y-coordinate                    @@//
//@@                 where the two paths will intersect, or cross eachother.                             @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionYSimple takes Parameter-Set 2 returns real                          @@//
//@@                                                                                                     @@//
//@@               - "returns real"     -> This function will return the Y-coordinate                    @@//
//@@                 where the two paths will intersect, or cross eachother.                             @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionAngle takes Parameter-Set 1 returns real                            @@//
//@@                                                                                                     @@//
//@@               - "returns real"     -> This function will return the angle                           @@//
//@@                 from the "caster" to the intersect point. (Should be in radians !)                  @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionAngleSimple takes Parameter-Set 1 returns real                      @@//
//@@                                                                                                     @@//
//@@               - "returns real"     -> This function will return the angle                           @@//
//@@                 from the "caster" to the intersect point. (Should be in radians !)                  @@//
//@@                                                                                                     @@//
//@@                                                                                                     @@//
//@@                                     Requirements:                                                   @@//
//@@                                                                                                     @@//
//@@             # This snippet's only requirement is vJASS compilement, which is                        @@//
//@@               easiest achieved by downloading JASS Newgen Pack, at                                  @@//
//@@               <a href="http://www.thehelper.net/forums/showthread.php?t=73936" class="link link--internal">http://www.thehelper.net/forums/showthread.php?t=73936</a>                                @@//
//@@               You&#039;ll also have to update JASS Helper to the latest version...                       @@//
//@@                                                                                                     @@//
//@@                                                                                                     @@//
//@@                                        Credits:                                                     @@//
//@@                                                                                                     @@//
//@@             # - Da1nOnlyEd, for actually making me code this little thing <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite8" alt=":D" title="Big Grin    :D" loading="lazy" data-shortname=":D" />                        @@//
//@@               - Perkeyone, for actually doing the math and providing the formula                    @@//
//@@                 which this snippet uses ! <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite8" alt=":D" title="Big Grin    :D" loading="lazy" data-shortname=":D" />                                                        @@//
//@@               - BlackRose, for a small tweak for a slight efficiency gain ! <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite2" alt=";)" title="Wink    ;)" loading="lazy" data-shortname=";)" />                      @@//
//@@                                                                                                     @@//
//@@             # And credits, if you use this that is, is not needed to give me                        @@//
//@@               though it&#039;s always welcome <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite2" alt=";)" title="Wink    ;)" loading="lazy" data-shortname=";)" />                                                         @@//
//@@                                                                                                     @@//
//@@/////////////////////////////////////////////////////////////////////////////////////////////////////@@//
//*********************************************************************************************************//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////


    function GetInterceptionLoc takes real castX, real castY, real castSpeed, real targX, real targY, real targSpeed, real targAngle returns location
        local real difx = castX - targX
        local real dify = castY - targY
        local real interX
        local real interY
        local real cosa = Cos( targAngle )
        local real sina = Sin( targAngle )
        
        local real dist = difx * difx + dify * dify
        
        local real a = 2 * targSpeed * ( cosa * difx + sina * dify )
        local real sqr = SquareRoot( ( a * a ) - ( 4 * dist * ( targSpeed * targSpeed - castSpeed * castSpeed ) ) )
        local real x1 = 2 * dist / ( ( -a ) + sqr )
        local real x2 = 2 * dist / ( ( -a ) - sqr )
        
        local real x
        
        if x1 &gt;= 0. and ( x1 &lt; x2 or x2 &lt;= 0. ) then
            set x = x1
        else
            set x = x2
        endif
        
        set interX = targX + targSpeed * x * cosa
        set interY = targY + targSpeed * x * sina
        
        return Location( interX, interY )
    endfunction
    
    function GetInterceptionLocSimple takes unit caster, real casterSpeed, unit target, real targetSpeed returns location
        return GetInterceptionLoc( GetUnitX( caster ), GetUnitY( caster ), casterSpeed, GetUnitX( target ), GetUnitY( target ), targetSpeed, GetUnitFacing( target ) * bj_DEGTORAD )
    endfunction
    
    function GetInterceptionX takes real castX, real castY, real castSpeed, real targX, real targY, real targSpeed, real targAngle returns real
        local real difx = castX - targX
        local real dify = castY - targY
        local real cosa = Cos( targAngle )
        local real sina = Sin( targAngle )
        
        local real dist = difx * difx + dify * dify
        
        local real a = 2 * targSpeed * ( cosa * difx + sina * dify )
        local real sqr = SquareRoot( ( a * a ) - ( 4 * dist * ( targSpeed * targSpeed - castSpeed * castSpeed ) ) )
        local real x1 = 2 * dist / ( ( -a ) + sqr )
        local real x2 = 2 * dist / ( ( -a ) - sqr )
        
        local real x
        
        if x1 &gt;= 0. and ( x1 &lt; x2 or x2 &lt;= 0. ) then
            set x = x1
        else
            set x = x2
        endif
        
        return targX + targSpeed * x * cosa
    endfunction
    
    function GetInterceptionXSimple takes unit caster, real casterSpeed, unit target, real targetSpeed returns real
        return GetInterceptionX( GetUnitX( caster ), GetUnitY( caster ), casterSpeed, GetUnitX( target ), GetUnitY( target ), targetSpeed, GetUnitFacing( target ) * bj_DEGTORAD )
    endfunction
    
    function GetInterceptionY takes real castX, real castY, real castSpeed, real targX, real targY, real targSpeed, real targAngle returns real
        local real difx = castX - targX
        local real dify = castY - targY
        local real cosa = Cos( targAngle )
        local real sina = Sin( targAngle )
        
        local real dist = difx * difx + dify * dify
        
        local real a = 2 * targSpeed * ( cosa * difx + sina * dify )
        local real sqr = SquareRoot( ( a * a ) - ( 4 * dist * ( targSpeed * targSpeed - castSpeed * castSpeed ) ) )
        local real x1 = 2 * dist / ( ( -a ) + sqr )
        local real x2 = 2 * dist / ( ( -a ) - sqr )
        
        local real x
        
        if x1 &gt;= 0. and ( x1 &lt; x2 or x2 &lt;= 0. ) then
            set x = x1
        else
            set x = x2
        endif
        
        return targY + targSpeed * x * sina
    endfunction
    
    function GetInterceptionYSimple takes unit caster, real casterSpeed, unit target, real targetSpeed returns real
        return GetInterceptionY( GetUnitX( caster ), GetUnitY( caster ), casterSpeed, GetUnitX( target ), GetUnitY( target ), targetSpeed, GetUnitFacing( target ) * bj_DEGTORAD )
    endfunction
    
    function GetInterceptionAngle takes real castX, real castY, real castSpeed, real targX, real targY, real targSpeed, real targAngle returns real
        local real difx = castX - targX
        local real dify = castY - targY
        local real interX
        local real interY
        local real cosa = Cos( targAngle )
        local real sina = Sin( targAngle )
        
        local real dist = difx * difx + dify * dify
        
        local real a = 2 * targSpeed * ( cosa * difx + sina * dify )
        local real sqr = SquareRoot( ( a * a ) - ( 4 * dist * ( targSpeed * targSpeed - castSpeed * castSpeed ) ) )
        local real x1 = 2 * dist / ( ( -a ) + sqr )
        local real x2 = 2 * dist / ( ( -a ) - sqr )
        
        local real x
        local real angle
        
        if x1 &gt;= 0. and ( x1 &lt; x2 or x2 &lt;= 0. ) then
            set x = x1
        else
            set x = x2
        endif
        
        set interX = targX + targSpeed * x * cosa
        set interY = targY + targSpeed * x * sina
        
        set angle = Atan2( interY - castY, interX - castX )
        
        return angle
    endfunction
    
    function GetInterceptionAngleSimple takes unit caster, real casterSpeed, unit target, real targetSpeed returns real
        return GetInterceptionAngle( GetUnitX( caster ), GetUnitY( caster ), casterSpeed, GetUnitX( target ), GetUnitY( target ), targetSpeed, GetUnitFacing( target ) * bj_DEGTORAD )
    endfunction
        
endlibrary


This is just a small snippet, so I don't think a demo map is necessary ;)
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
I really don't understand the math behind this but what happens when the two entities are moving so there can't be an intersection point (parallel).

And maybe GetInterseptionLoc can reuse GetInterseptionX/Y?

Oh and I really like your documentation! ^^

Edit: haha i really spelled those utterly wrong xD
 

Flare

Stops copies me!
Reaction score
662
I really don't understand the math behind this but what happens when the two entities are moving so there can't be an intersection point (parallel).
Judging by the parameters, one of the units is assumed to be not moving at the time (if you want to interpret it that way, since the unit technically won't be able to move anyway but upwards, which is irrelevant when only XY coordinates are taken into account, unless it has an angle at which to move, on the XY plane) - since one unit is given no angle, this function is, indirectly, providing that angle by returning a point/pair of coordinates that 'cast' must travel to in order to intercept 'target', assuming conditions remain the same - same idea as leading your target in an first-person (or, even, third-person) game with projectile-based weaponry.

Also, out of curiosity, would it be possible to see a version of this where time is a factor? Grenade-style abilities (or AI's) could benefit from it ^^
 

Komaqtion

You can change this now in User CP.
Reaction score
469
I really don't understand the math behind this but what happens when the two entities are moving so there can't be an intersection point (parallel).

Ok, maybe I should've explained that a bit.

So, let's say a unit (A) is currently moving straight to the north, meaning 270 degrees (If I'm not mistaken :S) at the speed of 250 range-per-second.

Now, another unit (B) which is currently located 400 range to the south-west of A isn't moving (Though has 325 movement-speed), but he will, instead of homing A and trying to catch up with him, try to intercept A by e.g going through the woods.

So, with this formula I have here you'll be able to calculate where A and B will meet if B goes through the woods :D

Hope you understand this ;)

And maybe GetInterseptionLoc can reuse GetInterseptionX/Y?

Yeah, but since I already get both X and Y in the "Loc" function, I think it's better this way... But I'll consider it ;)

Oh and I really like your documentation! ^^

Thank you ! :D

Also, out of curiosity, would it be possible to see a version of this where time is a factor? Grenade-style abilities (or AI's) could benefit from it ^^

Not too sure I understand what you mean there... :S
How would time be a factor ?
 

Accname

2D-Graphics enthusiast
Reaction score
1,462
So, let's say a unit (A) is currently moving straight to the north, meaning 270 degrees (If I'm not mistaken :S)[...]
you are mistaken. north is 90°.
starting from the left being 0° its going counterclockwise.

on topic: i dont really see a need for this. i mean, i never needed something like this and i believe most maybe all wont either.
 

Komaqtion

You can change this now in User CP.
Reaction score
469
Here's an example of it's use (Even though kinda awkward to use...)

JASS:
library WaveTemplate requires T32, optional Recycle, optional GroupUtils

    globals
        private constant integer DUMMY_ID           =       &#039;1337&#039;
        
        private constant string  ATTACH_POINT       =       &quot;chest&quot;
        
        private constant boolean DAMAGE_IS_ATTACK   =       true
        private constant boolean DAMAGE_IS_RANGED   =       true
        
        private constant attacktype ATTACK_TYPE     =       ATTACK_TYPE_MAGIC
        private constant damagetype DAMAGE_TYPE     =       DAMAGE_TYPE_MAGIC
        private constant weapontype WEAPON_TYPE     =       WEAPON_TYPE_WHOKNOWS
    endglobals
    
    native UnitAlive takes unit id returns boolean
    
    interface DamageFilters
        
        method damageFilter takes unit u returns boolean defaults true
        
    endinterface
    
    private function DefaultFilters takes unit u returns boolean
        return UnitAlive( u )
    endfunction

    struct WaveProjectile extends DamageFilters
        unit missileCaster = null
        unit missileDummy = null
        
        player casterOwner = null
        
        real missileAngle = 0.
        real missileSpeed = 0.
        real missileDmg = 0.
        real missileAoE = 0.
        real finalAoE = 0.
        real finalDmg = 0.
        real distanceTraveled = 0.
        real distanceLeft = 0.
        real totalDistance
        real missileX = 0.
        real missileY = 0.
        real targetX = 0.
        real targetY = 0.
        
        boolean finalExplosion
        
        string finalSFX
        
        effect missileSFX = null
        
        group missileDamaged = null
        
        private static group tempDamaged
        
        private static thistype EnumData = 0
        
        static method create takes unit projectileCaster, string sfxPath, real projectileSpeed, real projectileAngle, real spellDistance, real projectileDamage, real projectileAoE,/*
        */                         boolean finalBang, real finalArea, real finalDamage, string finalSFXPath returns thistype
        
            local thistype this = thistype.allocate()
            
            set missileCaster = projectileCaster
            set casterOwner = GetOwningPlayer( projectileCaster )
            set missileX = GetUnitX( projectileCaster )
            set missileY = GetUnitY( projectileCaster )
            set targetX = missileX + spellDistance * Cos( projectileAngle * bj_DEGTORAD )
            set targetY = missileY + spellDistance * Sin( projectileAngle * bj_DEGTORAD )
            set missileAngle = projectileAngle
            set missileDmg = projectileDamage
            set missileDummy = CreateUnit( casterOwner, DUMMY_ID, missileX, missileY, projectileAngle )
            set missileSFX = AddSpecialEffectTarget( sfxPath, missileDummy, ATTACH_POINT )
            set missileDmg = projectileDamage
            set missileSpeed = projectileSpeed * T32_PERIOD
            set finalExplosion = finalBang
            set finalAoE = finalArea
            set finalDmg = finalDamage
            set finalSFX = finalSFXPath
            set distanceLeft = spellDistance
            set totalDistance = spellDistance
            set UnitMovementData( GetUnitId( missileDummy ) ).currentSpeed = projectileSpeed
            set UnitMovementData( GetUnitId( missileDummy ) ).movementAngle = projectileAngle
            
            static if LIBRARY_GroupUtils then
                set missileDamaged = NewGroup()
            elseif LIBRARY_Recycle then
                set missileDamaged = Group.get()
            else
                set missileDamaged = CreateGroup()
            endif
            
            call startPeriodic()
            
            return this
        endmethod
        
        private static method AddnDamageUnitsFirst takes nothing returns boolean
            local unit u = GetFilterUnit()
            local thistype this = EnumData
            
            if not IsUnitInGroup( u, missileDamaged ) and damageFilter( u ) and DefaultFilters( u ) and not( IsUnit( u, missileDummy ) and IsUnit( u, missileCaster ) ) then
                call GroupAddUnit( missileDamaged, u )
                call UnitDamageTarget( missileCaster, u, missileDmg, DAMAGE_IS_ATTACK, DAMAGE_IS_RANGED, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE )
            endif
            
            return false
        endmethod
            
        private method periodic takes nothing returns nothing
            local real newx = missileX + missileSpeed * Cos( missileAngle * bj_DEGTORAD )
            local real newy = missileY + missileSpeed * Sin( missileAngle * bj_DEGTORAD )
            local real difx = newx - missileX
            local real dify = newy - missileY
            
            if GetRectMaxX( bj_mapInitialPlayableArea ) &gt; newx and GetRectMinX( bj_mapInitialPlayableArea ) &lt; newx and GetRectMaxY( bj_mapInitialPlayableArea ) &gt; newy and GetRectMinY( bj_mapInitialPlayableArea ) &lt; newy then
                call SetUnitX( missileDummy, newx )
                call SetUnitY( missileDummy, newy )
            else
                call stopPeriodic()
                
                call destroy()
            endif
            
            set missileX = newx
            set missileY = newy
            set distanceTraveled = distanceTraveled + missileSpeed
            set distanceLeft = distanceLeft - missileSpeed
            set EnumData = this
            
            call GroupEnumUnitsInRange( tempDamaged, missileX, missileY, missileAoE, Filter( function thistype.AddnDamageUnitsFirst ) )
            
            if distanceTraveled &gt;= totalDistance - missileSpeed / 2. then
                call stopPeriodic()
                
                call destroy()
            endif
            
        endmethod
        
        implement T32x
        
        private static method onInit takes nothing returns nothing
        
            static if LIBRARY_GroupUtils then
                set thistype.tempDamaged = NewGroup()
            elseif LIBRARY_Recycle then
                set thistype.tempDamaged = Group.get()
            else
                set thistype.tempDamaged = CreateGroup()
            endif
            
        endmethod
        
        private static method AddnDamageUnitsFinal takes nothing returns boolean
            local unit u = GetFilterUnit()
            local thistype this = EnumData
            
            if not IsUnitInGroup( u, missileDamaged ) and damageFilter( u ) and DefaultFilters( u ) and not( IsUnit( u, missileDummy ) and IsUnit( u, missileCaster ) ) then
                call GroupAddUnit( missileDamaged, u )
                call UnitDamageTarget( missileCaster, u, finalDmg, DAMAGE_IS_ATTACK, DAMAGE_IS_RANGED, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE )
            endif
            
            return false
        endmethod
        
        private method onDestroy takes nothing returns nothing
            call DestroyEffect( missileSFX )
            
            if finalExplosion then
                set EnumData = this
                
                call DestroyEffect( AddSpecialEffect( finalSFX, missileX, missileY ) )
                call GroupEnumUnitsInRange( tempDamaged, missileX, missileY, finalAoE, Filter( function thistype.AddnDamageUnitsFinal ) )
            endif
            
            static if LIBRARY_GroupUtils then
                call ReleaseGroup( missileDamaged )
            elseif LIBRARY_Recycle then
                call Group.release( missileDamaged )
            else
                call DestroyGroup( missileDamaged )
            endif
        
            call RemoveUnit( missileDummy )
        endmethod
            
    endstruct
    
endlibrary


JASS:
scope Shockwave initializer Init // requires T32, optional KT2, optional GroupUtils, optional Recycle

    globals
        private constant integer DUMMY_ID        =     &#039;1337&#039;
        private constant integer SPELL_ID        =     &#039;wave&#039;
        
        private constant string  SFX_PATH        =     &quot;Abilities\\Spells\\Undead\\Possession\\PossessionMissile.mdl&quot;
        private constant string  ATTACH_POINT    =     &quot;chest&quot;
        
        private constant real    MISSILE_SPEED   =     250.
    endglobals
    
    private constant function SpellDistance takes integer lvl returns real
        return lvl * 200. + 250.
    endfunction
    
    private function SpellDamage takes integer lvl returns real
        return 75. + 25. * Pow( 2., lvl - 1. )
    endfunction
    
    private function SpellAoE takes integer lvl returns real
        return 50. + 15. * lvl
    endfunction

    private function Conditions takes nothing returns boolean
        local unit u
        local real x
        local real y
        local real tx
        local real ty
        local real angle
        local integer lvl
        
        if GetSpellAbilityId() == SPELL_ID then
            set u = GetTriggerUnit()
            
            set x = GetUnitX( u )
            set y = GetUnitY( u )
            
            set tx = GetSpellTargetX()
            set ty = GetSpellTargetY()
            
            set angle = bj_RADTODEG * Atan2( ty - y, tx - x )
            
            set lvl = GetUnitAbilityLevel( u, GetSpellAbilityId() )
            
            call WaveProjectile.create( u, SFX_PATH, MISSILE_SPEED, angle, SpellDistance( lvl ), SpellDamage( lvl ), SpellAoE( lvl ), false, 0., 0., null )
            
            set u = null
        endif
        
        return false
    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 ) )
    endfunction
    
endscope


JASS:
scope MissileInterception initializer Init //requires AutoFly, Recycle, T32, InterceptionData, UnitMovementTracking 
    
    globals
        private constant integer DUMMY_ID = &#039;1337&#039;
        private constant integer SPELL_ID = &#039;A000&#039;
        
        private constant real DUMMY_SPEED = 300.
        
        private constant boolean HOME_MISSILE = false
        
        private constant string SFX_PATH = &quot;Abilities\\Spells\\Human\\StormBolt\\StormBoltMissile.mdl&quot;
        private constant string CONTACT_SFX_PATH_1 = &quot;Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl&quot;
        private constant string CONTACT_SFX_PATH_2 = &quot;Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl&quot;
    endglobals
    
    struct MissileData extends array
        //! runtextmacro AIDS()
        
        boolean isMissile
        
        private method AIDS_onCreate takes nothing returns nothing
            set isMissile = false
            
            if GetUnitTypeId( unit ) == &#039;1337&#039; then
                set isMissile = true
            endif
            
        endmethod
        
    endstruct
            

    private struct SpellData
        private unit caster
        private unit dummyMissile
        private unit targetMissile
        
        private real dummyX
        private real dummyY
        private real targetX
        private real targetY
        private real interX
        private real interY
        private real interAngle
        
        private effect dummyModel
        
        static method create takes unit u, unit targ returns thistype
            local thistype this = thistype.allocate()
            
            set caster = u
            set targetMissile = targ
            
            set dummyX = GetUnitX( u )
            set dummyY = GetUnitY( u )
            set targetX = GetUnitX( targ )
            set targetY = GetUnitY( targ )
            
            set interX = GetInterceptionX( dummyX, dummyY, DUMMY_SPEED, targetX, targetY, GetUnitSpeed( targ ), GetUnitMovementAngle( targ ) )
            set interY = GetInterceptionY( dummyX, dummyY, DUMMY_SPEED, targetX, targetY, GetUnitSpeed( targ ), GetUnitMovementAngle( targ ) )
            set interAngle = GetInterceptionAngle( dummyX, dummyY, DUMMY_SPEED, targetX, targetY, GetUnitSpeed( targ ), GetUnitMovementAngle( targ ) ) * bj_DEGTORAD
            
            call DisplayTextToPlayer( GetLocalPlayer(), 0., 0., &quot;Speed: &quot; + R2S(  GetUnitSpeed( targ ) ) )
            call DisplayTextToPlayer( GetLocalPlayer(), 0., 0., &quot;Angle: &quot; + R2S(  GetUnitMovementAngle( targ ) ) )
            
            set dummyMissile = CreateUnit( GetOwningPlayer( u ), DUMMY_ID, GetUnitX( u ), GetUnitY( u ), GetUnitFacing( u ) )
            
            set dummyModel = AddSpecialEffectTarget( SFX_PATH, dummyMissile, &quot;chest&quot; )
            
            set UnitMovementData( GetUnitId( dummyMissile ) ).currentSpeed = DUMMY_SPEED
            
            call startPeriodic()
            
            return this
        endmethod
        
        private method periodic takes nothing returns nothing
            local real newx = dummyX + DUMMY_SPEED * T32_PERIOD * Cos( interAngle )
            local real newy = dummyY + DUMMY_SPEED * T32_PERIOD * Sin( interAngle )
            
            if GetRectMaxX( bj_mapInitialPlayableArea ) &gt; newx and GetRectMinX( bj_mapInitialPlayableArea ) &lt; newx and GetRectMaxY( bj_mapInitialPlayableArea ) &gt; newy and GetRectMinY( bj_mapInitialPlayableArea ) &lt; newy then
                call SetUnitX( dummyMissile, newx )
                call SetUnitY( dummyMissile, newy )
            else
                call stopPeriodic()
                
                call destroy()
            endif
            
            if IsUnitInRangeXY( targetMissile, newx, newy, ( DUMMY_SPEED * T32_PERIOD ) / 2. ) then
                call DestroyEffect( dummyModel )
                call DestroyEffect( AddSpecialEffect( CONTACT_SFX_PATH_1, newx, newy ) )
                call DestroyEffect( AddSpecialEffect( CONTACT_SFX_PATH_2, newx, newy ) )
                
                call RemoveUnit( dummyMissile )
                call RemoveUnit( targetMissile )
                
                call stopPeriodic()
                
                call destroy()
            endif
            
            if HOME_MISSILE then
                set interX = GetInterceptionX( dummyX, dummyY, DUMMY_SPEED, targetX, targetY, GetUnitSpeed( targetMissile ), GetUnitMovementAngle( targetMissile ) )
                set interY = GetInterceptionY( dummyX, dummyY, DUMMY_SPEED, targetX, targetY, GetUnitSpeed( targetMissile ), GetUnitMovementAngle( targetMissile ) )
                set interAngle = GetInterceptionAngle( dummyX, dummyY, DUMMY_SPEED, targetX, targetY, GetUnitSpeed( targetMissile ), GetUnitMovementAngle( targetMissile ) )
            endif
            
            set dummyX = newx
            set dummyY = newy
        endmethod
        
        implement T32x
        
    endstruct
        
    private function Actions takes nothing returns boolean
        local unit u = GetTriggerUnit()
        local unit t = GetSpellTargetUnit()
        
        if GetSpellAbilityId() == SPELL_ID then
            call SpellData.create( u, t )
        endif
        
        set u = null
        set t = null
        
        return false
    endfunction
    
    private function SpellAbort takes nothing returns boolean
        local unit u = GetTriggerUnit()
        local unit t = GetSpellTargetUnit()
        
        if MissileData( GetUnitId( t ) ).isMissile != true and GetSpellAbilityId() == SPELL_ID then
            call SimError( GetOwningPlayer( u ), &quot;You need to target a valid projectile !&quot; )
            
            call PauseUnit( u, true )
            call IssueImmediateOrder( u, &quot;stop&quot; )
            call PauseUnit( u, false )
        endif
        
        set u = null
        set t = null
        
        return false
    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 Actions ) )
        
        set t = CreateTrigger()
        
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_CAST )
        call TriggerAddCondition( t, Condition( function SpellAbort ) )
    endfunction
    
endscope


Now, this last one is a spell which will be able (If you're fast, or you make the middle spell slow enough :p) to intercept the shockwave the middle trigger creates, although you will need to have a dummy which can be selected, but it shows you how it can be used anyways XD
 

BlackRose

Forum User
Reaction score
239
In GetInterceptionAngle(), the local real angle is not needed. You can just return the angle directly.
 

tooltiperror

Super Moderator
Reaction score
231
This has tremendous use when I think about it, especially for AI's. Imagine being able to do a simple If/Then/Else for seeing if you want the computer to try to block him off ahead, or chase him down.

Although, it could probably be merged into a library with other functions. Perhaps you should make a library to make creating an AI easier with math related functions?
 

Komaqtion

You can change this now in User CP.
Reaction score
469
Yeah, I've now converted everything to only use radians in as the parameters (Was unsure of how it actually was before XD):
JASS:
library InterceptionData

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*********************************************************************************************************//
//@@/////////////////////////////////// *// InterceptionData \\* ////////////////////////////////////////@@//
//@@                                                                                                     @@//
//@@                              Made by , Komaqtion @ TheHelper.net                                    @@//
//@@                                                                                                     @@//
//@@                                                                                                     @@//
//@@                                         Purpose:                                                    @@//
//@@                                                                                                     @@//
//@@         # The purpose of this small snippet is to easily be able to locate a point                  @@//
//@@           in the map, where two entities will meet if their path isn&#039;t changed.                     @@//
//@@                                                                                                     @@//
//@@                                         Usage:                                                      @@//
//@@                                                                                                     @@//
//@@         # *NOTE*: All functions here use one of two sets of parameters:                             @@//
//@@                                                                                                     @@//
//@@   Parameter-Set 1:                                                                                  @@//
//@@   &#039;real castX, real castY, real castSpeed, real targX, real targY, real targSpeed, real targAngle&#039;  @@//
//@@                                                                                                     @@//
//@@         real castX, real castY -&gt; These are the coordinates of the entity which you probably        @@//
//@@                                   will want to use to move against the intersection point.          @@//
//@@         real castSpeed         -&gt; This is the current speed of the &quot;casting&quot; entity.                @@//
//@@         real targX, real targY -&gt; These are the current coordinates of the entity which the         @@//
//@@                                   caster will be intersecting                                       @@//
//@@         real targSpeed         -&gt; This is the current speed of the target entity                    @@//
//@@         real targAngle         -&gt; This is the angle of which the target is currently moving         @@//
//@@                                   towards.                                                          @@//
//@@                                                                                                     @@//
//@@   Parameter-Set 2:                                                                                  @@//
//@@   &#039;unit caster, real casterSpeed, unit target, real targetSpeed&#039;                                    @@//
//@@                                                                                                     @@//
//@@         unit caster            -&gt; This is the &quot;casting&quot; unit, or the unit which you will            @@//
//@@                                   use the intersection point with.                                  @@//
//@@         real casterSpeed       -&gt; This is the current speed of the &quot;casting&quot; unit.                  @@//
//@@         unit target            -&gt; This is the target unit, which currently has a heading            @@//
//@@         real targetSpeed       -&gt; This is the current speed of the target unit.                     @@//
//@@                                                                                                     @@//
//@@                                                                                                     @@//
//@@         # Now, the functions provided by this small snippet are:                                    @@//
//@@           *NOTE*: ALL THE ANGLES IN THE PARAMETERS HERE ARE IN RADIANS !                            @@//
//@@                   (To make degrees into radians, simply multiply the angle by bj_DEGTORAD)          @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionLoc takes Parameter-Set 1 returns location                          @@//
//@@                                                                                                     @@//
//@@               - &quot;returns location&quot; -&gt; This function will return the location where                  @@//
//@@                 the two paths will intersect, or cross eachother.                                   @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionLocSimple takes Parameter-Set 2 returns location                    @@//
//@@                                                                                                     @@//
//@@               - &quot;returns location&quot; -&gt; This function will return the location where                  @@//
//@@                 the two paths will intersect, or cross eachother.                                   @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionX takes Parameter-Set 1 returns real                                @@//
//@@                                                                                                     @@//
//@@               - &quot;returns real&quot;     -&gt; This function will return the X-coordinate                    @@//
//@@                 where the two paths will intersect, or cross eachother.                             @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionXSimple takes Parameter-Set 2 returns real                          @@//
//@@                                                                                                     @@//
//@@               - &quot;returns real&quot;     -&gt; This function will return the X-coordinate                    @@//
//@@                 where the two paths will intersect, or cross eachother.                             @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionY takes Parameter-Set 1 returns real                                @@//
//@@                                                                                                     @@//
//@@               - &quot;returns real&quot;     -&gt; This function will return the Y-coordinate                    @@//
//@@                 where the two paths will intersect, or cross eachother.                             @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionYSimple takes Parameter-Set 2 returns real                          @@//
//@@                                                                                                     @@//
//@@               - &quot;returns real&quot;     -&gt; This function will return the Y-coordinate                    @@//
//@@                 where the two paths will intersect, or cross eachother.                             @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionAngle takes Parameter-Set 1 returns real                            @@//
//@@                                                                                                     @@//
//@@               - &quot;returns real&quot;     -&gt; This function will return the angle                           @@//
//@@                 from the &quot;caster&quot; to the intersect point. (Should be in radians !)                  @@//
//@@                                                                                                     @@//
//@@         function GetInterceptionAngleSimple takes Parameter-Set 1 returns real                      @@//
//@@                                                                                                     @@//
//@@               - &quot;returns real&quot;     -&gt; This function will return the angle                           @@//
//@@                 from the &quot;caster&quot; to the intersect point. (Should be in radians !)                  @@//
//@@                                                                                                     @@//
//@@                                                                                                     @@//
//@@                                     Requirements:                                                   @@//
//@@                                                                                                     @@//
//@@             # This snippet&#039;s only requirement is vJASS compilement, which is                        @@//
//@@               easiest achieved by downloading JASS Newgen Pack, at                                  @@//
//@@               <a href="http://www.thehelper.net/forums/showthread.php?t=73936" class="link link--internal">http://www.thehelper.net/forums/showthread.php?t=73936</a>                                @@//
//@@               You&#039;ll also have to update JASS Helper to the latest version...                       @@//
//@@                                                                                                     @@//
//@@                                                                                                     @@//
//@@                                        Credits:                                                     @@//
//@@                                                                                                     @@//
//@@             # - Da1nOnlyEd, for actually making me code this little thing <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite8" alt=":D" title="Big Grin    :D" loading="lazy" data-shortname=":D" />                        @@//
//@@               - Perkeyone, for actually doing the math and providing the formula                    @@//
//@@                 which this snippet uses ! <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite8" alt=":D" title="Big Grin    :D" loading="lazy" data-shortname=":D" />                                                        @@//
//@@               - BlackRose, for a small tweak for a slight efficiency gain ! <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite2" alt=";)" title="Wink    ;)" loading="lazy" data-shortname=";)" />                      @@//
//@@                                                                                                     @@//
//@@             # And credits, if you use this that is, is not needed to give me                        @@//
//@@               though it&#039;s always welcome <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite2" alt=";)" title="Wink    ;)" loading="lazy" data-shortname=";)" />                                                         @@//
//@@                                                                                                     @@//
//@@/////////////////////////////////////////////////////////////////////////////////////////////////////@@//
//*********************************************************************************************************//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////


    function GetInterceptionLoc takes real castX, real castY, real castSpeed, real targX, real targY, real targSpeed, real targAngle returns location
        local real difx = castX - targX
        local real dify = castY - targY
        local real interX
        local real interY
        local real cosa = Cos( targAngle )
        local real sina = Sin( targAngle )
        
        local real dist = difx * difx + dify * dify
        
        local real a = 2 * targSpeed * ( cosa * difx + sina * dify )
        local real sqr = SquareRoot( ( a * a ) - ( 4 * dist * ( targSpeed * targSpeed - castSpeed * castSpeed ) ) )
        local real x1 = 2 * dist / ( ( -a ) + sqr )
        local real x2 = 2 * dist / ( ( -a ) - sqr )
        
        local real x
        
        if x1 &gt;= 0. and ( x1 &lt; x2 or x2 &lt;= 0. ) then
            set x = x1
        else
            set x = x2
        endif
        
        set interX = targX + targSpeed * x * cosa
        set interY = targY + targSpeed * x * sina
        
        return Location( interX, interY )
    endfunction
    
    function GetInterceptionLocSimple takes unit caster, real casterSpeed, unit target, real targetSpeed returns location
        return GetInterceptionLoc( GetUnitX( caster ), GetUnitY( caster ), casterSpeed, GetUnitX( target ), GetUnitY( target ), targetSpeed, GetUnitFacing( target ) * bj_DEGTORAD )
    endfunction
    
    function GetInterceptionX takes real castX, real castY, real castSpeed, real targX, real targY, real targSpeed, real targAngle returns real
        local real difx = castX - targX
        local real dify = castY - targY
        local real cosa = Cos( targAngle )
        local real sina = Sin( targAngle )
        
        local real dist = difx * difx + dify * dify
        
        local real a = 2 * targSpeed * ( cosa * difx + sina * dify )
        local real sqr = SquareRoot( ( a * a ) - ( 4 * dist * ( targSpeed * targSpeed - castSpeed * castSpeed ) ) )
        local real x1 = 2 * dist / ( ( -a ) + sqr )
        local real x2 = 2 * dist / ( ( -a ) - sqr )
        
        local real x
        
        if x1 &gt;= 0. and ( x1 &lt; x2 or x2 &lt;= 0. ) then
            set x = x1
        else
            set x = x2
        endif
        
        return targX + targSpeed * x * cosa
    endfunction
    
    function GetInterceptionXSimple takes unit caster, real casterSpeed, unit target, real targetSpeed returns real
        return GetInterceptionX( GetUnitX( caster ), GetUnitY( caster ), casterSpeed, GetUnitX( target ), GetUnitY( target ), targetSpeed, GetUnitFacing( target ) * bj_DEGTORAD )
    endfunction
    
    function GetInterceptionY takes real castX, real castY, real castSpeed, real targX, real targY, real targSpeed, real targAngle returns real
        local real difx = castX - targX
        local real dify = castY - targY
        local real cosa = Cos( targAngle )
        local real sina = Sin( targAngle )
        
        local real dist = difx * difx + dify * dify
        
        local real a = 2 * targSpeed * ( cosa * difx + sina * dify )
        local real sqr = SquareRoot( ( a * a ) - ( 4 * dist * ( targSpeed * targSpeed - castSpeed * castSpeed ) ) )
        local real x1 = 2 * dist / ( ( -a ) + sqr )
        local real x2 = 2 * dist / ( ( -a ) - sqr )
        
        local real x
        
        if x1 &gt;= 0. and ( x1 &lt; x2 or x2 &lt;= 0. ) then
            set x = x1
        else
            set x = x2
        endif
        
        return targY + targSpeed * x * sina
    endfunction
    
    function GetInterceptionYSimple takes unit caster, real casterSpeed, unit target, real targetSpeed returns real
        return GetInterceptionY( GetUnitX( caster ), GetUnitY( caster ), casterSpeed, GetUnitX( target ), GetUnitY( target ), targetSpeed, GetUnitFacing( target ) * bj_DEGTORAD )
    endfunction
    
    function GetInterceptionAngle takes real castX, real castY, real castSpeed, real targX, real targY, real targSpeed, real targAngle returns real
        local real difx = castX - targX
        local real dify = castY - targY
        local real interX
        local real interY
        local real cosa = Cos( targAngle )
        local real sina = Sin( targAngle )
        
        local real dist = difx * difx + dify * dify
        
        local real a = 2 * targSpeed * ( cosa * difx + sina * dify )
        local real sqr = SquareRoot( ( a * a ) - ( 4 * dist * ( targSpeed * targSpeed - castSpeed * castSpeed ) ) )
        local real x1 = 2 * dist / ( ( -a ) + sqr )
        local real x2 = 2 * dist / ( ( -a ) - sqr )
        
        local real x
        local real angle
        
        if x1 &gt;= 0. and ( x1 &lt; x2 or x2 &lt;= 0. ) then
            set x = x1
        else
            set x = x2
        endif
        
        set interX = targX + targSpeed * x * cosa
        set interY = targY + targSpeed * x * sina
        
        set angle = Atan2( interY - castY, interX - castX )
        
        return angle
    endfunction
    
    function GetInterceptionAngleSimple takes unit caster, real casterSpeed, unit target, real targetSpeed returns real
        return GetInterceptionAngle( GetUnitX( caster ), GetUnitY( caster ), casterSpeed, GetUnitX( target ), GetUnitY( target ), targetSpeed, GetUnitFacing( target ) * bj_DEGTORAD )
    endfunction
        
endlibrary
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top