• 🏆 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!

[System] [Needs work] Collision Missiles v1.a

Level 12
Joined
Mar 28, 2005
Messages
160
Introduction:

In the spirit of Vexorian's classic Collision Missiles system, I present for initial review and comment, a fully functional collision missiles system. Those that have popped up recently are generally neither as robust nor so easy to employ, and always tend to lack on key feature or another. I aimed to incorporate all that necessary functionality you'd expect, without limiting your potential through hard to navigate and use API.

My first attempt, a while back, was teh pooh. This has been rewritten from the ground up, with ease of use at the fore front of my priorities. Things can be as simple or as complicated as you want them to be, its entirely your decision.

What this does well:

  • Simple missile launching at a specified velocity and direction, with the ability to home onto targets
  • Simple API and straight forward usage
  • Timed lives, maximum distances, with the ability to dynamically change values
  • Safe debri destruction
  • Realistic terrain/unit/missile collision physics
  • Dynamic missile scaling of size and radius
  • Simple parabolic movements, with realistic dummy pitch updates
  • Gravity
  • Resting state recognition
  • OnEvent responses
  • Rolling missiles with ground friction
  • Optimal periodic execution format and linked list struct stack

What this doesn't do well (* denotes a planned incorporation in future updates):

  • Creation at an offset at a point or unit
  • Multiple collisions on the same target*
  • Find new targets if primary becomes un-reachable*
  • Realistic 3D collisions(doesn't take into account z position/velocity when conserving momentum)*
  • Determining if a missile is rolling or not*
  • O(n) missile-on-missile collisions
  • Advanced interface method functionality*:
    • createFromUnit
    • set/addAcceleration
    • projectToPoint
    • controlled parabolic movements
    • etc.
Requirements:

This currently requires GroupUtils, but as I come to decide on how to handle multiple collisions/target that will probably be removed. UnitAlive native employed for dummy unit life status checking.


How Can I Help?

Review, review, review. I know there is no demo map. I haven't had the time to really put something together at the production level I would like. But, the script is all here. I understand that this is probably no where near an acceptable state, but please bear that in mine as you review, too.

Things to pay particular attention to:

  • Can things be optimized at all?
  • A better way to tell if missiles are rolling
  • A better way to limit facing angle updates when homing
  • How do you feel about my cliff/ground detection?
  • Can we produce a better means of missile-on-missile collision detection? Rather than O(n).
  • Where is this lacking, in the functionality department?
  • What separates this from prior art, if anything?
  • What do you think about controlling knock-backs using this system as well? Would put all possible map particle movement into one system, opening up a lot of really neat possibilities IMO.
  • How to approach multiple collisions/target, w/o having to worry about multiple collisions happening one after the other after the other, until a large enough gap is observed. This can become an issue.
  • How to approach removal of initial cast missile collision with a caster.
And probably most importantly, is this even needed? Is what is out there already good enough???



System Script:
JASS:
//=========================\\
//==System made by emjlr3==\\
//==Version 1.a, 01/24/12==\\
//=========================\\
library CollisionMissiles requires GroupUtils
native UnitAlive takes unit id returns boolean
   
struct missile
    //== CONFIGURATION ==\\
    private static constant integer         DUMMY       = 'dumy' // dummy unit to use for projectiles
    private static constant integer         ABIL        = 'tree' // rawcode of tree destruction ability
    private static constant real            REST        = 45. // units/sec velocity considered to be 0.
    private static constant real            TIMEOUT     = 0.03125 // periodic timer callback interval
    //== REQUIRED ==\\
    private static          group           G           = CreateGroup()
    private static          integer         C           = 0
    private static          location        LOC         = Location(0.,0.)
    private static          thistype array  M
    private static constant real            GRAVITY     = -32.144*TIMEOUT
    private static constant real            FRACPI      = bj_PI/16.
    private static constant real            HALFPI      = bj_PI/2.
    private static constant real            REALREST    = REST*TIMEOUT
    private static constant real            TWOPI       = bj_PI*2.
    private static constant real            TERMZ       = 90./(1500.*TIMEOUT)
    private static          timer           T           = CreateTimer()
    private static          trigger         TRIG        = CreateTrigger()
    private static          unit            DUM         = null
    
    private thistype next
    private thistype prev

    missileData data  
    unit dum=null
    unit targ=null
    player p=null
    effect sfx=null
    real xP
    real yP
    real zP
    real z0
    real zV=0.
    real ang
    real cos
    real sin
    real vel=0.
    real dist=0.
    real maxDist=-0.
    real life=-0.
    real sqRadius
    boolean end=false
    boolean rolling=false
    group g
    
    // destroy this instance
    method destroy takes nothing returns nothing
        call .data.onDeath(this)
        call DestroyEffect(.sfx)
        call KillUnit(.dum)
        call ReleaseGroup(.g)
        call .deallocate()
    endmethod
    // collision with hostiles/missiles
    static method getDeflectionAngle takes thistype this, unit t returns real
        return 2.*Atan2(GetUnitY(t)-.yP,GetUnitX(t)-.xP)+bj_PI-.ang
    endmethod
    // collisions with cliffs
    static method getDeflectionAngleCliff takes thistype this, real x, real y returns real            
        return 2.*Atan2(y-.yP,x-.xP)+bj_PI-.ang
    endmethod
    static method periodic takes nothing returns boolean
        local thistype this=thistype(0).next
        local thistype t
        local real array r
        local real s
        local unit u=null
       
        loop
            exitwhen this==0
           
           // update positioning
            set .dist=.dist+.vel
            // target found?
            if .targ!=null and UnitAlive(.targ) then
                set .ang=.ang*bj_RADTODEG
                set s=Atan2(GetUnitY(.targ)-.yP,GetUnitX(.targ)-.xP)*bj_RADTODEG
                // Solve for updated facing accounting for angular rotation limitations
                // a large limit allows for instant facing changes
                if s>.ang then
                    set s=s-.ang
                    if s>.data.angularRotation then
                        set s=.data.angularRotation
                    endif
                    set .ang=(.ang+s)*bj_DEGTORAD
                else
                    set s=.ang-s
                    if s>.data.angularRotation then
                        set s=.data.angularRotation
                    endif
                    set .ang=(.ang-s)*bj_DEGTORAD
                endif 
                set .cos=Cos(.ang)
                set .sin=Sin(.ang)
            endif
            set .xP=.xP+.vel*.cos
            set .yP=.yP+.vel*.sin
            set .zP=.zP+.zV
            // missile is rolling, apply ground friction
            if .rolling then
                // check incase we've added a z velocity since I last established that this missile was rolling
                // if its rolling it should have a 0 z velocity
                if RAbsBJ(.zV)>0. then
                    set .rolling=false
                else
                    set .vel=.vel*.data.friction
                endif
            else
            // if the missile is in the air, apply gravity and drag
                set .vel=.vel*.data.drag
                if .data.gravity then
                    set .zV=.zV+GRAVITY
                endif
            endif
            // update position
            call SetUnitX(.dum,.xP)
            call SetUnitY(.dum,.yP)
            call MoveLocation(LOC,.xP,.yP)
            // update fly  height
            call SetUnitFlyHeight(.dum,.zP-GetLocationZ(LOC),0.)
            call SetUnitFacing(.dum,.ang*bj_RADTODEG)
            // update pitch, maximum zV of 1500/s
            // anything above this and our missile is traveling too damned fast to see anyways
            call SetUnitAnimationByIndex(.dum,90+R2I(.zV*TERMZ))
            
            // tree destruction if the missile is not above the trees
            // kudos to Bribe for this neat trick
            // i added in  the height gap check because it looks strange of missiles clearly
            // above the tree line are destroying trees in their wake
            if .data.wantDestroyTrees and .zP-GetLocationZ(LOC)<375. then
                call SetUnitAbilityLevel(DUM,ABIL,IMaxBJ(4,R2I(.data.radius/32.)))
                call IssuePointOrder(DUM,"flamestrike",.xP,.yP)
            endif
            
            // cliff detection
            // i divided by 2 because I was having issues with cliffs being found earlier than desired
            // for aesthetics sake really
            set r[1]=GetLocationZ(LOC)+GetUnitFlyHeight(.dum)-.data.radius/2.
            set r[2]=.ang-HALFPI
            set r[3]=.ang+HALFPI
            // loop in a half circle using the missiles facing
            loop
                exitwhen r[2]>r[3]
                set r[4]=.xP+.data.radius*Cos(r[2])
                set r[5]=.yP+.data.radius*Sin(r[2])
                call MoveLocation(LOC,r[4],r[5])
                set r[6]=GetLocationZ(LOC)
                if r[6]>=r[1] and r[6]>r[9] then // collision found
                    // update dynamic array members
                    set r[7]=r[4] // x
                    set r[8]=r[5] // y
                    set r[9]=r[6] // z
                endif
                set r[2]=r[2]+FRACPI
            endloop
            if r[9]>0. then
                call MoveLocation(LOC,.xP,.yP)
                if r[9]-GetLocationZ(LOC)>=10. then // cliff collision
                    call .data.onCollisionCliff(this)
                    // bounce the missile
                    if .data.bounce then
                        set .ang=thistype.getDeflectionAngleCliff(this,r[7],r[8])
                        set .cos=Cos(.ang)
                        set .sin=Sin(.ang)
                    endif
                else // ground collision
                    if not .rolling then
                        call .data.onCollisionGround(this)
                        if .data.bounce then
                            // stop ground bouncing, otherwise the bouncing gets really small and looks really odd
                            if RAbsBJ(.zV)<=REALREST then
                                set .zV=0.
                                call SetUnitFlyHeight(.dum,0.,0.)
                                set .rolling=true
                            else
                                // apply restitution values
                                set .zV=RAbsBJ(.zV*.data.restitution)
                                set .vel=.vel*.data.restitution
                            endif
                        endif
                    endif
                endif
            endif            
            
            // hostiles collision
            // +128 assumes a collision radius for the targets
            call GroupEnumUnitsInRange(G,.xP,.yP,.data.radius+128.,null)
            loop
                set u=FirstOfGroup(G)
                exitwhen u==null
                call GroupRemoveUnit(G,u)
                
                call MoveLocation(LOC,GetUnitX(u),GetUnitY(u))   
                // x/y range is good, but is z position also?
                // we can currently only collide with hostiled units
                // until I decide on a method to remove initial caster collisions
                if RAbsBJ(.zP-(GetLocationZ(LOC)+GetUnitFlyHeight(u)))<.data.radius+128. and UnitAlive(u) and IsUnitEnemy(u,.p) then
                    call data.onCollisionHostile(this,u)
                    set .ang=thistype.getDeflectionAngle(this,u)
                    set .cos=Cos(.ang)
                    set .sin=Sin(.ang)
                    // no more collision for this hostile with this missile :(
                    // future updates should remove this limitation, same goes for missiles
                    call GroupAddUnit(.g,u)
                endif
            endloop
            
            // missiles collision, O(n) ftl
            // most systems don't allow for missile collisions, probably because it's so slow
            set t=thistype(0).next
            loop
                exitwhen t==0
                if .dum!=t.dum then
                    if not IsUnitInGroup(t.dum,.g) then
                        // is distance between missiles greater than the sum of their radii?
                        if (t.xP-.xP)*(t.xP-.xP)+(t.yP-.yP)*(t.yP-.yP)+(t.zP-.zP)*(t.zP-.zP)<=.sqRadius+t.sqRadius then
                            call data.onCollisionHostile(this,t.dum)
                            set .ang=thistype.getDeflectionAngle(this,t.dum)
                            set .cos=Cos(.ang)
                            set .sin=Sin(.ang)
                            call GroupAddUnit(.g,t.dum)
                            
                            call data.onCollisionHostile(t,.dum)
                            set t.ang=thistype.getDeflectionAngle(t,.dum)
                            set t.cos=Cos(t.ang)
                            set t.sin=Sin(t.ang)
                            call GroupAddUnit(t.g,.dum)
                        endif     
                    endif
                endif
                set t=t.next
            endloop
            
            // maximum distance traveled
            if .maxDist>0. then
                if .dist+.vel>=.maxDist then
                    set .end=true
                    set .vel=.maxDist-.dist
                endif
            endif
            
            // timed life up
            if .life>0. then
                set .life=.life-TIMEOUT
                if .life<=0. then
                    set .end=true
                endif
            endif
            
            // resting state established
            if .vel<=REALREST then
                call .data.onRest(this)
                if .data.wantDestroyOnRest then
                    set .end=true
                endif
            endif
            
            call .data.onLoop(this)
            
            // destroy the missile
            if .end or not UnitAlive(.dum) then
                set this.prev.next=this.next
                set this.next.prev=this.prev
                call .destroy()
                set C=C-1
            endif
           
            set this=this.next
        endloop
       
       // no more missile instances left
       if C==0 then
            call PauseTimer(T)
        endif
        
        // required for a boolean returning function
        return false
    endmethod
    
    //== CREATION METHODS ==\\
    // currently only the one :(
    static method create takes missileData data, player p, real x, real y, real height, real face, real velocity returns thistype
        local thistype this=thistype.allocate()
       
        set .data=data
        set .g=NewGroup()
        // create dummy
        set .dum=CreateUnit(p,DUMMY,x,y,face*bj_RADTODEG)
        call SetUnitX(.dum,x)
        call SetUnitY(.dum,y)
        set .p=p
        set .ang=face
        set .cos=Cos(.ang)
        set .sin=Sin(.ang)
        call UnitAddAbility(.dum,'Aloc')
        call UnitAddAbility(.dum,'Amrf')
        call SetUnitScale(.dum,.data.scale,.data.scale,.data.scale)
        set .sfx=AddSpecialEffectTarget(.data.sfx,.dum,"origin")
        call SetUnitAnimationByIndex(.dum,90)
        call SetUnitPathing(.dum,false)
        call SetUnitInvulnerable(.dum,true)       
       
        // positioning
        set .xP=x
        set .yP=y
        call MoveLocation(LOC,x,y)
        set .z0=GetLocationZ(LOC)
        set .zP=height+.z0      
        call SetUnitFlyHeight(.dum,height,0)   
        if height<=0 then
            set .rolling=true
        endif
        set .vel=velocity*TIMEOUT
        set .sqRadius=.data.radius*.data.radius
       
        // update linked list
        set C=C+1
        set thistype(0).next.prev=this
        set this.next=thistype(0).next
        set thistype(0).next=this
        set this.prev=thistype(0)
        if C==1 then
            call TimerStart(T,TIMEOUT,true,function thistype.tick)
        endif
       
        call data.onCreate(this)       
        return this
    endmethod
    
    //== FORCE APPLICATION METHODS ==\\
    // these are pretty self explanatory
    method addMaxDistance takes real r returns nothing
        set .maxDist=r+.maxDist
    endmethod
    method setMaxDistance takes real r returns nothing
        set .maxDist=r
    endmethod
    method addVelocity takes real r returns nothing
        set .vel=r*TIMEOUT+.vel
    endmethod
    method setVelocity takes real r returns nothing
        set .vel=r*TIMEOUT
    endmethod
    method addZVelocity takes real r returns nothing
        set .zV=r*TIMEOUT+.zV
    endmethod
    method setZVelocity takes real r returns nothing
        set .zV=r*TIMEOUT
    endmethod    
    
    //== FUNCTIONALITY METHODS ==\\
    // these too
    method addRadius takes real r returns nothing
        set .data.radius=r+.data.radius
        set .sqRadius=.data.radius*.data.radius
    endmethod
    method setRadius takes real r returns nothing
        set .data.radius=r
        set .sqRadius=.data.radius*.data.radius
    endmethod
    method addScale takes real r returns nothing
        set .data.scale=r+.data.scale
        call SetUnitScale(.dum,.data.scale,.data.scale,.data.scale)
    endmethod
    method setScale takes real r returns nothing
        set .data.scale=r
        call SetUnitScale(.dum,r,r,r)
    endmethod  
    method setTarget takes unit t returns nothing
        set .targ=t
    endmethod
    method forgetTarget takes unit t returns nothing
        set .targ=null
    endmethod
    method addTimedLife takes real r returns nothing
        set .life=r+.life
    endmethod
    method setTimedLife takes real r returns nothing
        set .life=r
    endmethod          
    method remove takes nothing returns nothing
        set .end=true
    endmethod
    
    // some how some way, this is faster than simply running everything
    // in the timer handlerFunc execution
    private static method tick takes nothing returns nothing
        call TriggerEvaluate(TRIG)
    endmethod
    private static method onInit takes nothing returns nothing
        call TriggerAddCondition(TRIG,Condition(function thistype.periodic))
        
        set DUM=CreateUnit(Player(15),DUMMY,0.,0.,0.)
        call UnitAddAbility(DUM,ABIL)
        call UnitAddAbility(DUM,'Aloc')
        call SetUnitPathing(DUM,false)
        call SetUnitInvulnerable(DUM,true)       
        call PauseUnit(DUM,false)
    endmethod
endstruct
   
endlibrary
Missile Interface:
JASS:
library MissileData

interface missileData
    real        angularRotation     = 30.   // maximum rotation angle/TIMEOUT in degrees
    real        drag                = 1.0   // fraction of velocity retained when flying (0 is most, 1 is least, values should be close to 1.0 as this fires every timer interval)
    real        restitution         = 1.0   // fraction of velocities recovered following collisions (0 is most, 1 is least)
    real        friction            = 1.0   // fraction of velocity retained when rolling (0 is most, 1 is least, values should be close to 1.0 as this fires every timer interval)
    real        radius              = 32.   // collision radius   
    real        scale               = 1.0   // dummy scale
   
    boolean     bounce              = true  // bounce off the ground and cliffs? (sorry no trees yet)
    boolean     collide             = true  // collide with other missiles and hostile units? (sorry no non-hostiles yet)
    boolean     gravity             = true  // should gravity affect missiles?
    boolean     wantDestroyOnRest   = true  // destroy missiles when they stop moving?
    boolean     wantDestroyTrees    = true  // destroy trees in a missiles path?
   
    string      sfx                 = ""    // sfx applied to the missile
   
    // these are all very self explanatory and shouldn't require much elaboration
    method onCollisionCliff takes missile m returns nothing defaults nothing
    method onCollisionGround takes missile m returns nothing defaults nothing
    method onCollisionHostile takes missile m, unit target returns nothing defaults nothing
    method onCollisionMissile takes missile m1, missile m2 returns nothing defaults nothing
    method onCreate takes missile m returns nothing defaults nothing
    method onDeath takes missile m returns nothing defaults nothing
    method onLoop takes missile m returns nothing defaults nothing
    method onRest takes missile m returns nothing defaults nothing
endinterface

endlibrary
Example Use:
JASS:
//========================\\
//==Spell made by emjlr3==\\
//==Version XX, XX/XX/XX==\\
//========================\\
scope FireBomb

private struct data extends missileData
    //== Configuration Section ==\\
    static constant integer     ABIL        = 'BOMB'    // abilid rawcode
    
    //== Struct Specific ==\\  
    
    // interface specific
    real        angularRotation     = 30.
    real        drag                = 1.0
    real        restitution         = .7
    real        friction            = .98
    real        radius              = 32.   
    real        scale               = 1.0
   
    boolean     bounce              = true
    boolean     collide             = true
    boolean     gravity             = true
    boolean     wantDestroyOnRest   = true
    boolean     wantDestroyTrees    = true
        
    string sfx = "Abilities\\Weapons\\LordofFlameMissile\\LordofFlameMissile.mdl"
    
    // specific to this spell
    real x
    real y
    unit u
    real ux
    real uy
    real ang
    real r=0.
    real dist
    missile m
    
    method destroy takes nothing returns nothing  
        // see if we store our missile struct, we can use its members :)
        call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl",.m.xP,.m.yP))
        call .deallocate()
        // remove the missile and its struct
        call m.remove()
    endmethod
    method onCollisionGround takes missile m returns nothing  
        //call .destroy()
        // do stuff when we hit the ground
    endmethod
    method onCollisionHostile takes missile m, unit target returns nothing
        //call .destroy()
        // do stuff when we strike an enemy unit
    endmethod
    method onCollisionCliff takes missile m returns nothing
        //call .destroy()
        // do stuff when we hit a cliff
    endmethod
    method onLoop takes missile m returns nothing
        // do things periodically
    endmethod
    static method create takes nothing returns thistype
        local thistype this=thistype.allocate()
        local missile m // we can create a new missile using a struct interface
        
        set .x=GetSpellTargetX()
        set .y=GetSpellTargetY()
        set .u=GetTriggerUnit()
        set .ux=GetUnitX(.u)
        set .uy=GetUnitY(.u)
        set .ang=ABPXY(.ux,.uy,.x,.y)
        set .dist=DBPXY(.ux,.uy,.x,.y)
        // create a new missile for the caster, at its location, with fly height 75., facing/moving towards the targeted spot, at 500units/s
        set m=missile.create(this,Player(0),.ux,.uy,75.,.ang,500.)
        // we only want it to move so far as the targeted spot
        call m.addMaxDistance(.dist)
        // nice looking parabola
        // eventually I will make methods to create a fluid parabola from spot a to spot b, given a max height
        // some day....
        // currently this will create a seemingly unpredictable arc, and will probably hit the maxDist well before it hits the ground
        // if we wanted to use calculus we could solve for the sum of the function that creates it
        // some day....
        call m.addZVelocity(950.)
        // storing the missile struct is a nice touch, I recommend it
        set .m=m
                
        return this
    endmethod

    static method conditions takes nothing returns boolean
        if GetSpellAbilityId()==ABIL then
            call thistype.create()
        endif
        return false
    endmethod    
    static method onInit takes nothing returns nothing
        local trigger trig=CreateTrigger()
        local integer index=0

        loop
            call TriggerRegisterPlayerUnitEvent(trig,Player(index),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
            set index=index+1
            exitwhen index==bj_MAX_PLAYER_SLOTS
        endloop
        call TriggerAddCondition(trig,Condition(function thistype.conditions))
    endmethod
endstruct

endscope
Thanks for taking the time to read all of this. Hope you enjoyed.

-Hulk3
 
Last edited by a moderator:
Well, you know, a lot of systems could support collision missiles by treating them as if they were other units themselves.

Can things be optimized at all?

Yes. A lot. First, you could remove the interface, make the struct extend an array, and use a tiny module interface instead :p

Is what is out there already good enough???

Well, there's Dirac's Missile
 
Level 4
Joined
Jun 9, 2011
Messages
91
JASS:
native UnitAlive takes unit id returns boolean
>>>>>
JASS:
private function UnitAlive takes unit u returns boolean
	return not IsUnitType(u,UNIT_TYPE_DEAD)
endfunction

JASS:
struct missile
>>>>
JASS:
struct Missile extends array

JASS:
static method periodic takes nothing returns boolean
>>>>>>>
JASS:
private static method periodic takes nothing returns boolean

JASS:
method destroy takes nothing returns nothing
>>>>>>>
JASS:
private method destroy takes nothing returns nothing
 
Level 12
Joined
Mar 28, 2005
Messages
160
Well, you know, a lot of systems could support collision missiles by treating them as if they were other units themselves.
sorry, don't follow

First, you could remove the interface, make the struct extend an array, and use a tiny module interface instead :p
this either :( point me in the direction of an example please? what makes this better?

Well, there's Dirac's Missile

I am perfectly fine with helping to perfect an existing system - but its up to the author to implement the changes
 
sorry, don't follow

What I meant was that you wouldn't even have to do iteration if you don't give missiles the locust ability because then the system would treat missiles like units and thus collide and kill them ^.^
You could even add checks to detect whether the target you hit was a missile or not to instantly kill it :D

this either :( point me in the direction of an example please? what makes this better?

Well, developing a module interface is not the easiest thing in the world :eek:
Normal interfaces generate a lot of code and duplicate functions, so instead of making structs extend structs and interfaces, we implement tiny modules that do all the work an interface would do instead ^.^
Sort of like UnitIndexerStruct and MissileStruct ;p

You'd implement an onInit method to register events for the methods in the struct that you're implementing the module into, first by checking if the static methods actually exist like this: thistype.onLoop.exists

This method is usually faster because it only generates as much code as should be generated and it removes all the slow trigger evaluations ;D
In a projectile system, optimizations are imperative :O
 
Level 12
Joined
Mar 28, 2005
Messages
160
I'm sorry if you didn't feel like it, but your criticism to Missile was very well received. I'll perform the proper updates when i have the time

it wasn't a slight at all to you in particular, simply a statement in general

What I meant was that you wouldn't even have to do iteration if you don't give missiles the locust ability because then the system would treat missiles like units and thus collide and kill them ^.^

than you could select them....and they would have hp bars...ewww
 
Level 6
Joined
Jun 20, 2011
Messages
249
Currently with Missile you can do this
JASS:
static method onPeriod takes Missile this returns boolean
	local MissileList node = MissileList.base.next
	loop
		exitwhen node.head
		if SquareRoot((this.x-node.x)*(this.x-node.x)+(this.y-node.y)*(this.y-node.y)) <= 150 then
			call this.deflect(node.x,node.y)
		endif
		set node = node.next
	endloop
	return false
endmethod
 
Level 12
Joined
Mar 28, 2005
Messages
160
the user?

that functionality is not built in though right? IMO it should be - and should be toggleable for each projectile

I should not have to rewrite that for each missile when the system could take care of it all in one for me

really that is just one of the big things yours is missing, unfortunately...
 
Top