• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[vJASS] Missile

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Hey, what should I return on this method?
JASS:
static method onCollide takes Missile missile, unit hit returns boolean

I should you should add it to the document.

EDIT:
Damn, missed this:
JASS:
*       All of them return a boolean, which determines the future of that Missile instance.
*           -   return true  --> destroy the Missile.
*           -   return false --> keep the Missile flying.
Sorry
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Basically no important change, if 1.3 is satisfying for you.

Iirc Missile.create is no longer calling createEx first to allocate a new instance

Missile.createEx is new and takes a unit argument, so can shoot any unit like it
would be a Missile. Units which do not match the dummy type id are not recycled/removed.
 
Missile.createEx is new and takes a unit argument, so can shoot any unit like it
would be a Missile. Units which do not match the dummy type id are not recycled/removed.
That is actually pretty neat. So it basicly comes with an in-build knockback-feature now? Does it account for pathability on X/Y movement? If not, that should definitely be an input parameter here; I suggest three different types:
1) Ignores all pathing in movement (for "flying" or "ghostly" type units that should be able to pass through walls... the default behaviour of missiles)
2) Ignores pathing only when airborne (for "thrown" units with non-zero .height); if the landing spot is blocked, move the unit to the closest possible pathable point
3) Always consider pathing, even when airborne (useful if you want to prevent thrown units from f.ex. getting thrown through dungeon walls); if the next X/Y coordinate is blocked, set X/Y velocity to 0.
 
It's an half-baked knockback feature. I have to compare Missile to approved Knockback
systems to see what is missing.

Basically one could use onPeriod as it runs each timer tick and do all required checks
you mentioned here.
True, didn't think of that. Anyways, great idea with the integrated "knockback light". It's a great addition when combined on a map with Knockback3D, as it has different input parameters and some additional functionality.

Knockback3D:
- for all the standard use-cases in which you only want to knock a unit in a certain direction with a certain speed and want the integrated friction effects or rebound
- lightweight (as it doesn't use implement)

Missile:
- if you want true parabolic movement with no friction component
- unit-to-unit movement
- custom callbacks
 
Okay, I just noticed another issue with this.

I basicly made a spell that is ... okay, let's admit it: it spams missiles a lot. It basicly creates 7 missiles every 0.2 seconds over 8 seconds. So in total 280. All of these missiles have an onFinish method implemented that just deals some AoE damage.

These missiles all have the same flying direction (as I designed them falling down from the ceiling, I made them move at very low speed from x+10, y+10 to x and y, only changing their z-coordinates).

The game performance starts to drop severely after consecutive use of this ability... which leads me to believe that there is something fishy going on here. This can not be just the minor leak that is created by creating/destroying a unit alone.
Maybe it's something with MissileRecycler; I'm not sure on this.

I can give you the code for said ability if you want.
 
You need to set MissileRecycler's recycled indices allowed to a much higher number if you want insane missile counts like that. I think that is about as overboard as it gets, lol.
This still doesn't explain why the spell works fine for several dozen casts, but then gradually gets more and more taxing. This is the typical behaviour one would expect from a major leak. But I checked the spell code and there aren't any leaks there (because, it's pretty simple).

I guess the dummy id set in MissileRecycler matches to the one set in the Missile library.
Yes. But even if not; just creating/killing units alone is not a sufficient explanation for the sudden FPS drop on consecutive casts, considering that tower defense maps with thousands of dying and spawning units are a thing.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Yes. But even if not; just creating/killing units alone is not a sufficient explanation for the sudden FPS drop on consecutive casts, considering that tower defense maps with thousands of dying and spawning units are a thing.

Once you have MissileRecycler in your map, Missile wants to recycle Missile units.
If the dummy id doesn't match then the system does literally nothing and the units
just remain where they are.

Another reason could be ( just thinking loud ), that you accumulate units on your screen ( 280 units). invisble and paused, but they are there.

Track how many units are created by MissileRecycler, it shouldn't be much more than 1-2 x 280.
 
Once you have MissileRecycler in your map, Missile wants to recycle Missile units.
If the dummy id doesn't match then the system does literally nothing and the units
just remain where they are.
The dummy id matches. That's not the problem.

Another reason could be ( just thinking loud ), that you accumulate units on your screen ( 280 units). invisble and paused, but they are there.
I return "true" at the onFinish method, so they should be properly recycled, unless there is a bug with your system here. I'll check out the spell code again if I maybe made a mistake here, but I'm pretty sure I didn't.

Track how many units are created by MissileRecycler, it shouldn't be much more than 1-2 x 280.
Will do. Something is fishy about this. Btw I'm still on Missile 1.3; could that be the issue?
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Hotfix:

By accident (I was really lucky ) I found a very small flaw in the code.
readonly static constant real HIT_BOX = (2/3) should rather be:

readonly static constant real HIT_BOX = (2./3.)
The upper truncs to 0, the lower is ~0.66.

HIT_BOX is more or less a visual feature, as Missile dummies want to hit a target on its chest and not at its toe.
It's there for the following calculation: GetUnitBodySize*HIT_BOX, so if a unit has a z - size ( customize - able value ) of 100
the missile dummy will impact at unit z + the upper caculation.

You should definitly fix the problem or download/copy the newest version from here.
 
Question - isn't weapontype always going to be the same thing whether it's null or not? Or does it actually play a sound when you use things like WEAPON_TYPE_LIGHT_CHOP?
Yes, it plays the sound if you use something else than WEAPON_TYPE_WHOKNOWS. Some weapon types don't work, though.
 
Is this a bug or am I just missing something here?

JASS:
private struct ChainLightning 
    static method onFinish takes Missile this returns boolean
        local group g
        local unit u
        if IsAliveEnemy(this.target, GetOwningPlayer(this.source)) then
            call DestroyEffect(AddSpecialEffectTarget("Abilities\\Weapons\\ChimaeraLightningMissile\\ChimaeraLightningMissile.mdl", this.target, "chest"))
            call UnitDamageTargetEx(this.source, this.target, StatSpellpower[this.source]*2*this.scale*this.scale*GetRandomReal(0.9, 1.1), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_LIGHTNING, WEAPON_TYPE_WHOKNOWS)
            if this.scale > 0.85 then //jump twice
                set this.scale = this.scale * 0.9
                set g = CreateGroup()
                call GroupEnumUnitsInRange(g, GetUnitX(this.target), GetUnitY(this.target), 400, null)
                loop
                    set u = FirstOfGroup(g)
                    exitwhen u == null
                    if IsAliveEnemy(u, GetOwningPlayer(this.source)) and IsUnitPet(u) == false and u != this.target and IsPointInCone(GetUnitX(this.target), GetUnitY(this.target), GetUnitX(u), GetUnitY(u), this.angle, 45*bj_DEGTORAD) then
                        
                        set this.target = u //lock on a new target

                        call TimedL.QuickU2U("CLSB", this.source, this.target, 1.0, 40, 40, 1, 1, 1, 1, 1)
                        exitwhen true
                    endif
                    call GroupRemoveUnit(g, u)
                endloop
                call DestroyGroup(g)
                set g = null
                set u = null
                return false //keep the missile flying
            endif
        endif
        return true
    endmethod
    
    implement MissileStruct

    static method fire takes unit cast, unit targ returns nothing
        local Missile m
        set m = Missile.createXYZ(GetUnitX(cast), GetUnitY(cast), 60, GetUnitX(targ), GetUnitY(targ), 60)
        set m.speed=50
        set m.model="Abilities\\Weapons\\ChimaeraLightningMissile\\ChimaeraLightningMissile.mdl"
        set m.height=0
        set m.source=cast
        set m.scale=1
        set m.target = targ
        call thistype.launch(m)
        call TimedL.QuickU2U("CLPB", cast, targ, 1.0, 40, 40, 1, 1, 1, 1, 1)
    endmethod
endstruct

The example above is a simple chain lightning replica (don't mind some of the custom functions, they work fine).
For some reason, the spell always hits the same target thrice instead of "jumping" to the next target or terminating if it can't find any.


EDIT:
Nevermind... I did a classic mistake here: not accounting for the case in which no valid target is present. In this case, the function still returns false and keeps the missile at the target, triggering onFinish again.
 
Last edited:
JASS:
*       static method onCollide takes Missile missile, unit hit returns boolean
You forgot to explain what the returned boolean will do.
Errrm, he did?
JASS:
   Important:     
 *
 *       All of them return a boolean, which determines the future of that Missile instance.
 *           -   return true  --> destroy the Missile.
 *           -   return false --> keep the Missile flying.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
@Quilnez: Déjà vu?
Somewhere a bit earlier in the thread:
Hey, what should I return on this method?
Jass:
static method onCollide takes Missile missile, unit hit returns boolean

I should you should add it to the document.

EDIT:
Damn, missed this:
Jass:
* All of them return a boolean, which determines the future of that Missile instance.
* - return true --> destroy the Missile.
* - return false --> keep the Missile flying.
Sorry
 
Btw, I think Wietlol makes a good point in this thread:
click

onCollide() might fail if a missile or unit is moving very fast and the collision size of the missile is small.
You could fix that by adding the velocity of the missile to the collision radius, then check if the unit is within travel direction of the missile.
Basicly, you overlay your circular collision check with a rectangular check (the black arrow displays the movement the missile does within 1 frame):
 

Attachments

  • missile.jpg
    missile.jpg
    158.2 KB · Views: 87
Level 19
Joined
Mar 18, 2012
Messages
1,716
It's a corner case and Wietlol is totally right.
So basically a rectangle check has to be done, if the missile collision is lower than it's velocity.

One possibility would be to use this function:
JASS:
    function IsPointInRectangle takes real ax, real ay, real bx, real by, real cx, real cy, real dx, real dy, real px, real py returns boolean
        local real cross0 = (py-ay)*(bx-ax)-(px-ax)*(by-ay)
        local real cross1 = (py-cy)*(ax-cx)-(px-cx)*(ay-cy)
        local real cross4 = (py-dy)*(ax-dx)-(px-dx)*(ay-dy)
        
        return ((cross0*cross1 >= 0) and (((py-by)*(cx-bx)-(px-bx)*(cy-by))*cross1 >= 0)) or ((cross0*cross4 >= 0) and (((py-by)*(dx-bx)-(px-bx)*(dy-by))*cross4 >= 0))
    endfunction
Another option would be like it is done here ( link )

I will think about, how to add it without adding too much overhead to the system.

You could also increase the accuracy by decreasing the periodic timer timeout.
I wouldn't recommend it, because the speed member in Missile is not adjusted if you change the timeout.

Why is that so? Because it was like this in the original Missile system. :/
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
hi all

I just saw the error concerning Dummy. I'm going to investigate and fix it. It uses a really good O(1) data structure that keeps everything as balanced as possible, so it'd be an absolute shame not to fix whatever is causing the problem.

I'll also see about doing a general delayed dummy recycling lib that sits on top of any of the dummy libs ^_-.


Hopefully I'll have this all fixed over the weekend and then we'll be cool.


edit
Also, the reason why Dummy doesn't disable Unit Indexer is because dummies interact with the map. For example, they enter regions and unit ranges. They can also potentially deal damage (they have mana). In order to prevent bugs with systems, any unit that interacts with the map should not use its own unit user data as this can cause collisions : ). This was discussed awhile ago : P. Dummies are not used for only missiles. One of my libs has them casting spells with their mana while they move around ;D. This means that they need to be registered to things like DDS.

So the thing is, how should we differentiate between fully featured dummies and partial dummies? Should fully featured dummies always be present?

My idea is as such. Indices are allocated/deallocated to dummies as they are recycled. This keeps the unit indexer as free as possible. Beyond this, the allocation/deallocation events can be fired separately from the allocation. This means that if you do want it going through DDS and everything to be a fully featured thing, you can do it.

Why do I still insist that they get a unit index? It is so that they can interact with any element of the map without bugging things up. Most spells and stuff, when they assume a unit indexer is used, also assume that any unit that interacts with them is indexed. They typically don't call IsUnitIndexed. At the very least, my stuff doesn't : P.

I believe that this is a good compromise. I'll work at updating Dummy to utilize the latest Unit Indexer system and I'll add some new features to that system to do the stuff above. I'll also fix the issues with dummy. This'll make dummies from Dummy completely reliable as well as absolutely safe. Users that use this Missile library will also be able to have Missiles do other interesting things if they want run them through the indexer events.


edit
Ok, so each dummy should definitely have a unit index assigned to it. No unit index will cause very bad things to happen.

For a missile system, the damage should be calculated as the missiles are created and then stored on the missiles. The missiles should also be the ones to deal the damage. However, missiles can't take damage and can't really have anything happen to them, so it is completely pointless to allocate them for something like DDS. They should be able to interact with units in a system like DDS, but units shouldn't be able to interact with them.

Thus, allowing manual allocation/deallocation with the option to fire events or not is the right call. With dummies, you'd never want to fire events.
 
Last edited:
It's a corner case and Wietlol is totally right.
So basically a rectangle check has to be done, if the missile collision is lower than it's velocity.
I would absolutely recommend it. I recently coded a simple volley spell which actually suffered from heavy (and deadly for the players) imprecision because of that. I could slow the missiles down to fix it but it would look ugly.

On the bright side, you won't add any additional overhead on most missiles anyway, as this extra safety can be enabled/disabled behind the scenes based on a simple comparison:

if thistype.speed >= thistype.radius then

Note that this only applies to collision radius, not diameter (so you might need a factor 2 depending on what you use), as shown below.
 

Attachments

  • missile2.jpg
    missile2.jpg
    29.1 KB · Views: 130
Level 19
Joined
Mar 18, 2012
Messages
1,716
Updated to v1.4.1.

Changes:
- Non dummy units ( createEx ), do no longer change their animation index.

Sadly I didn't fix the issue with for collision combined with a huge speed setup.
I made a small list yesterday of possible improvements.
  • This rectangle collision Zwiebelchen mentioned. I will look into Wietlols solution and probably implement it into Missile.
  • .
  • The timer timeout is not adjustable, because "speed=" is a public member and not a function call.
    It was like this in the previous Missile library. I can change this, while keeping the current API via operator overloading.
  • .
  • The onFinish impact location varies strongly along with the speed setup.
    Hilarious example: You want a missile travel a distance of 800 units. You set the speed to 300.
    -> 300 -> 600 -> 900 ( finish ).
    I could check if the a Missile goes beyond it's destination and adjust the travel distance on the last tick.
  • .
  • I'm not 100% sure if there is a small chance that the internal list of trigger conditions can lose their next node,
    when using TriggerRemoveCondition the way I do it here. ( Nestharus? ).
  • .
  • turn= still remains as useless member. Maybe I should remove it ...
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
I'm not 100% sure if there is a small chance that the internal list of trigger conditions can lose their next node,
when using TriggerRemoveCondition the way I do it here. ( Nestharus? ).

If you use TriggerRemoveCondition on a trigger that is currently being evaluated and you happen to remove the condition that is currently being evaluated, any conditions beyond the current condition will not be run for that instance.

A way to fix this problem is to Or all of the conditions together into an AVL Tree. This will also slightly improve the speed of the trigger. My Trigger lib does this ; P. BooleanExpression also does this (what Trigger uses).
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I just checked CTL ( obviously this must be considered there too ).
So removing triggerconditions, those who should be removed, after the trigger fired is safe.

I will go with the CTL solution. I'm aware of Trigger and BooleanExpression. It's impressive coding, but
Missile will no longer and never again be popular if it needs too many extra required libraries.

Another thing:
Based on my latests test "onDestructableFilter", which should runs before onDestructable seems to be terrible
unperformant and should not be used ( someone has other experiences? ) .

Anyway for destructable enumeration, it would be wise to use a Table in order to check, if a destructable
has been already considered before. For units I use a group handle, for destructable I could store their id/handle in
a Table and check if table.has(id).
Note: Destructable enumerations also use the default maximum collision size,
so a invalid destructable could be passed x times to the onDestructable function.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
This piece of code handles if the timer of Missile should be started or stopped.
It looks good to me. Can someone approve that I didn't make a logical mistake.

JASS:
// Handles the periodic timer callback and fires the trigger.
    globals
        private constant trigger CORE = CreateTrigger()
        private constant timer CLOCK  = CreateTimer()
        private integer active = 0
        
        private integer             array   instances
        private Missile             array   missileStack
        private boolexpr            array   expression
        private triggercondition    array   condition
        
        private integer             array   remove
    endglobals
    
    private function Fire takes nothing returns nothing
        local integer i = remove[0]
        set remove[0] = 0
        loop
            exitwhen i == 0
            if (instances[i] == 0) and (condition[i] != null) then
                call TriggerRemoveCondition(CORE, condition[i])
                set condition[i] = null
                set active = active - 1
            endif
            set i = remove[i]
        endloop
        if (active == 0) then
            call PauseTimer(CLOCK)
        else
            call TriggerEvaluate(CORE)
        endif
    endfunction
    
    private function MissileCreateExpression takes integer structId, code func returns nothing
        set expression[structId] = Condition(func)
    endfunction
    
    // Start timer if not already running.
    private function StartPeriodic takes integer structId returns nothing
        if (instances[structId] == 0) then
            if (condition[structId] == null) then
                set condition[structId] = TriggerAddCondition(CORE, expression[structId])
                set active = active + 1
            endif
            if (active == 0) then
                call TimerStart(CLOCK, Missile_TIMER_TIMEOUT, true, function Fire)
            endif
        endif
        set instances[structId] = instances[structId] + 1
    endfunction
    
    // And stops it.
    private function StopPeriodic takes integer structId returns nothing
        set instances[structId] = instances[structId] - 1
        if (instances[structId] == 0) then
            set remove[structId] = remove[0]
            set remove[0] = structId
        endif
    endfunction
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Absolutely :/ !!!! Luckily I didn't update the first page. I will not do that anyway until,
I'm 100% sure the new code is working and beneficial, sincce the current version is very stable.

JASS:
    // Handles the periodic timer callback and fires the trigger.
    globals
        private constant trigger CORE = CreateTrigger()
        private constant timer CLOCK  = CreateTimer()
        private integer active = 0
        
        private integer             array   instances
        private Missile             array   missileStack
        private boolexpr            array   expression
        private triggercondition    array   condition
        
        private integer             array   remove
    endglobals
    
    private function Fire takes nothing returns nothing
        local integer i = remove[0]
        set remove[0] = 0
        loop
            exitwhen i == 0
            if (instances[i] == 0) and (condition[i] != null) then
                call TriggerRemoveCondition(CORE, condition[i])
                set condition[i] = null
                set active = active - 1
            endif
            set i = remove[i]
        endloop
        if (active == 0) then
            call PauseTimer(CLOCK)
        else
            call TriggerEvaluate(CORE)
        endif
    endfunction
    
    private function MissileCreateExpression takes integer structId, code func returns nothing
        set expression[structId] = Condition(func)
    endfunction
    
    // Start timer if not already running.
    private function StartPeriodic takes integer structId returns nothing
        if (instances[structId] == 0) then
            if (active == 0) then
                call TimerStart(CLOCK, Missile_TIMER_TIMEOUT, true, function Fire)
            endif
            if (condition[structId] == null) then
                set condition[structId] = TriggerAddCondition(CORE, expression[structId])
                set active = active + 1
            endif
        endif
        set instances[structId] = instances[structId] + 1
    endfunction
    
    private function StopPeriodic takes integer structId returns nothing
        set instances[structId] = instances[structId] - 1
        if (instances[structId] == 0) then
            set remove[structId] = remove[0]
            set remove[0] = structId
        endif
    endfunction

Edit:

I guess I will also add this function. Does only work if no acceleration is used.
JASS:
        method flightTime2Speed takes real duration returns nothing
            set speed = RMaxBJ(0.00000001, (origin.distance - distance)*Missile_TIMER_TIMEOUT/duration)
        endmethod
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
I will start to work on the destructable collision first.

I found two major flaws:
  • Unlike units, destructables are hit every timer period over and over again.
  • .
  • The destructable filter must always check for the destructable height,
    currently it only does if z collision is activated. This results in that destructables are hit from missiles i.e. 1000 units above.

Probably there are more.

Another question:
Do we need a toogle for detecting missie - item collision? I thinks it's not required or?

Edit2:
I started to write a manual for Missile collision types. link
There would be room for:
  • Item collision ( as addition to destructable collision )
    .
  • Collision types: ( see below )
  • Circular collision ( is currently used for all objects )
  • Rectangle collision
  • Polygon collision ( requires a polygon library, my opinion about it is below )

I'm mentioning polygons because it was an hot topic the last weeks in our forums.
My opinion is that a polygon collision detection can be done in static method onPeriod.
Simply assign the polygon instance to missile.data and then run the collision check every timer timeout.

I've ran a couple of stress test:
  • Creates 9 missile every 0.05 seconds in 9 different structs.
  • Executes 9 triggers every 0.05 seconds
  • includes arc, curve, widget collision, z-collision ...

My laptops CPU and GPU are really good, so it's not the best base for a more than 10 years old game,
but the result is very satisfying.

You can safely have 200 active Missile during the same time with all options ticked.

Dirac once stated you can have ~800. That's blowing your own trumpet ... ( found this idiom on dict.cc :D )
I guess you can have 800 missile on an empty map fired from one function, using a very simple model,
do not performing any missile action like onCollide. Yes then you can have that much missiles.

Edit: I officially updated to v 1.4.2 and working on v.1.4.3
I'm tackling now the rectangle collision and improving the widget collision.
I also started to centralize the code a bit to make implemented code via module smaller.
 

Attachments

  • Stress test 1.jpg
    Stress test 1.jpg
    631.4 KB · Views: 95
  • Stress Test2.jpg
    Stress Test2.jpg
    663.5 KB · Views: 89
  • Stress Test 3.jpg
    Stress Test 3.jpg
    639.6 KB · Views: 119
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
I cleaned up the entire library. Out of this resulted version 1.5
I also updated the tutorial on the first page.

You can read the changelog [self=http://www.hiveworkshop.com/forums/jass-resources-412/missile-265370/#post2681196]here[/self].

Now that everything is well structured I can start working on version 2.0, which will include all the stuff discussed the last days.

Edit:
The code for the rectangle collision is done ( units only )

It goes like this:
1.) Detect collision type. ( This could also become a fix member, which a user can define )
JASS:
// Detect the collision type ( internally each loop )
//
if (speed < collision) then// maybe also speed < collision*.5 for accuracity.
    set collisionType = Missile_COLLISION_TYPE_CIRCLE
else
    set collisionType = Missile_COLLISION_TYPE_RECTANGLE
endif

2.) Detect units. ( part of the MissileStruct module.
JASS:
// Runs unit collision.
static if thistype.onCollide.exists then
    if (this.allocated) and (0 != this.collision) then
        set b = (this.collisionType == Missile_COLLISION_TYPE_RECTANGLE)  
        if b then
            call this.groupEnumUnitsRectangle()
        else
            call GroupEnumUnitsInRange(GROUP, this.x, this.y, this.collision + Missile_MAXIMUM_COLLISION_SIZE, null)
        endif
        loop
            set u = FirstOfGroup(GROUP)
            exitwhen u == null
            call GroupRemoveUnit(GROUP, u)
            if ((IsUnitInRange(u, this.dummy, this.collision)) or (b)) then
            // These units are passed in onCollide.
3.) What happens in this.groupEnumUnitsRectangle()
JASS:
private static method enumUnits takes nothing returns boolean
    local thistype this = thistype.temp
    //
    local unit u = GetFilterUnit()
    local real a = x - xPrev
    local real b = y - yPrev
    local real s = (a*(GetUnitX(u) - xPrev) + b*(GetUnitY(u)- yPrev))/(a*a + b*b)
    local boolean is
    if(s < 0) then
        set s = 0.
    elseif(s > 1) then
        set s = 1.
    endif
    set is = IsUnitInRangeXY(u, xPrev + s*a, yPrev + s*b, collision) 
    set u  = null
    return is
endmethod
        
private method prepareRectRectangle takes nothing returns nothing
    local real x1 = xPrev// x of the previous loop
    local real y1 = yPrev// y of the previous loop
    local real x2 = x
    local real y2 = y
    local real  d = collision + Missile_MAXIMUM_COLLISION_SIZE
    // What is min what is max ...
    if(x1 < x2) then
        if( y1 < y2) then
            call SetRect(RECT, x1 - d, y1 - d, x2 + d, y2 + d)
        else
            call SetRect(RECT, x1 - d, y2 - d, x2 + d, y1 + d)
        endif
    else
        if( y1 < y2) then
            call SetRect(RECT, x2 - d, y1 - d, x1 + d, y2 + d)
        else
            call SetRect(RECT, x2 - d, y2 - d, x1 + d, y1 + d)
        endif
    endif 
endmethod
        
method groupEnumUnitsRectangle takes nothing returns nothing
    call prepareRectRectangle()
    set thistype.temp = this
    call GroupEnumUnitsInRect(GROUP, RECT, Filter(function thistype.enumUnits)) 
endmethod

I tested it with lightnings and various setups of speed and collision.
For example Missile speed = 1000. and collision = 5. Works flawless.

Disclaimer:
Enumerations bewteen two loops overlap, however ingame that shouldn't be a problem at all. ( never ever )

Opinions please.

New update:

The collision detection for units is working now. For every speed / collision scenario.

Example: collision = 32, speed = 99999999. If the unit is in between it will be hit.

Also something new in the Missile 2.0 will be that only the max defined distance can be reached. ( 100 % accurate )

Example: distance= 1000, speed= 450 results in movement 0 --> 450 --> 900 --> 1000. ( and not 1350 )


Basically rectangle collision is automatically used if speed > collision*precisionfactor, while the factor
can be set in the GLOBALS and will be by default 1.

You will be able to read the collision type which was applied, but you will not be able to set it. Missile decides this for you.
Simply because it's not useful to apply rectangle collision in 99% of all cases. Then again I would have had to add more
members which you must set: collisionX, collisionY, collisionZ to properly draw the rect. ( I don't do that either. )

Disclaimer:
1.) Does not work for arcing/curving missles. ( Then the system would have had to check the whole curve piece by piece )
I don't do this because it kills performance.

2.) Z - collision is also incorrect for high speeds, because here applies the same as above.
The system would have had to compare terrainZ of units with terrainZ of imaginary point of the Missile.
We don't do that because it hits the performance.

3.) Other widget collision does not work so far for rectangle collision. I'm on it.
 
Last edited:
Top