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

Level 23
Joined
Apr 16, 2012
Messages
4,041
you mean like the knockback 3D which has literally 40 features that have nothing to do with the knockback as is but are kind of like, package of features you could've implemented yourself in 5 minutes? :D

Yes I will never stop it is ridiculous
 
you mean like the knockback 3D which has literally 40 features that have nothing to do with the knockback as is but are kind of like, package of features you could've implemented yourself in 5 minutes? :D
Uh, yeah Knockback 3D-is a bad case of featuritis.

I'm not even sure if we really need missile-to-missile collision. At least nobody ever complained about the lack of it in this thread, right?
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Please don't overload this system with useless extra features.

I like the current missile for it's simplicity. Why would we need polygon collision or item collision? That's useless junk tbh.
If people really need item collision, they can manually write it via the periodic callback.
You didn't even see the new code. It's cleaner and more read-able, all in all just better. It''s also not more.
Apart from the rectangle collision for units the code got slimer.


There is no polygon detection and there will never be one. Those who need it can run this onPeriod.

Item collision is internally merged with destructable collision to widget collision.
The functions use widget as argument and work for both handle types.

The only thing different is that you can declare onItem, which is put into "static if" like every static method of
the entire interface for structs.
Apart from that no extra code is generated.

you mean like the knockback 3D which has literally 40 features that have nothing to do with the knockback as is but are kind of like, package of features you could've implemented yourself in 5 minutes? :D

Yes I will never stop it is ridiculous
I think thats not a good comparison. Missile is much better in any aspect.
Also using Missile is easy like pie.

I'm not even sure if we really need missile-to-missile collision. At least nobody ever complained about the lack of it in this thread, right?
I thought about it today and decided I will not write it. Too many questions came up and there too many possible flaws. ( rectangle collision, z-collision ... )
Maps who really need it ( Warlock ) can track all units of the entire map and compare them one by one in onPeriod.

I will upload a list of everything what is new in Missile v2.0 later.
There are a few nice features ( don't worry no extra overhead and no useless stuff )
the code is finished and I will release it in the evening once I checked everything again.

Also Missile v2.0 will perform minimal faster than previous versions, because I changed the
order of if then else conditions a bit. The mechanics remain as before.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
Missile version 2.0
What is new? What is different?

1.) Collision types:

In the global setup there are now 3 new constants:
  • public constant integer COLLISION_TYPE_CIRCLE = 0
  • public constant integer COLLISION_TYPE_RECTANGLE = 1
  • public constant real COLLISION_ACCURACY_FACTOR = 1.

COLLISION_TYPE_CIRCLE and COLLISION_TYPE_RECTANGLE are indentifiers for the two possible mechanics for detecting close widgets.
You don't have to bother them. Just be aware they exist.

We need rectangle detection for missiles which fly too fast. In that case circular detection would leave undetected areas.
Missile detects automatically what type should be used. You can't influence directly what type is chosen, however you can set an accuracy factor.

COLLISION_ACCURACY_FACTOR determines when rectangle collision detection should be chosen over circular detection.
the formula is speed >= collision*Missile_COLLISION_ACCURACY_FACTOR.
Ergo for smaller values rectangle collision is applied earlier.
Important to know is that rectangle collision is less performant than circular.

JASS:
                // Detect the collision type.
                if (v < collision*Missile_COLLISION_ACCURACY_FACTOR) then
                    set collisionType = Missile_COLLISION_TYPE_CIRCLE
                else
                    set collisionType = Missile_COLLISION_TYPE_RECTANGLE
                endif

It works for any speed / collision setup. Even for speed=999999999 while collision is set to 10.

JASS:
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

JASS:
        private static method filterUnits takes nothing returns boolean
            local thistype this = thistype.temp
            //
            local unit u = GetFilterUnit()
            local real a 
            local real b 
            local real s 
            local boolean is
            // Missile knows this unit already.
            if HaveSavedBoolean(HASH, this, GetHandleId(u)) then
                set u = null
                return false
            endif
            //
            set a = x - xPrev
            set b = y - yPrev
            set s = (a*(GetUnitX(u) - xPrev) + b*(GetUnitY(u)- yPrev))/(a*a + b*b)
            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
        //
        // 5.) Proper rect preparation.
        //
        // For rectangle.
        private method prepareRectRectangle takes nothing returns nothing
            local real x1 = xPrev 
            local real y1 = yPrev 
            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
        //
        // 5.) API for the MissileStruct iteration.
        //
        method groupEnumUnitsRectangle takes nothing returns nothing
            call prepareRectRectangle()
            set thistype.temp = this
            call GroupEnumUnitsInRect(GROUP, RECT, unitFilter)
        endmethod

Disclaimer:
  • Rectangle collision doesn't care specifically about z - axis collision.
  • It runs the z collision check like the circular collision type. Otherwise there would be too much overhead added.
  • Rectangle collision detection is surely much slower than the circular ( still it is really fast, don't worry )


2.) Maximum range:

Since v.2.0 missiles are no longer able to fly further than their max distance.

Example: Missile.create(x, y, z, angle, 1000, endZ); speed = 400.

In the previous version the missile would fly in vector lenght: 400 --> 800 --> 1200 ( finish )
Now it is: 400 --> 800 --> 1000 ( finish )

This also applies if you set the speed to 99999999. The maximum reached distance will be 1000.


3.) New methods for your needs:

method setMovementSpeed takes real value returns nothing
  • Converts movement speed to vector lenght and assigns it to "speed"
  • Unlike speed=x, this function does take the timer timeout into account, hence if you change the timeout, the movement speed for your missile remains equal

method enableHitAfter takes widget w, real seconds returns nothing
  • This one is interesting. It allows a missile to hit a widget ( unit, destructable, item ) again after "seconds" time.
  • There is 0 overhead added, if you don't use this feature.
  • If you use it the overhead is so minimal that it can't be measured.

4.) static method onFinish takes Missile missile returns boolean.

I was looking closer into onFinish and decided it is not good the way it was.
From v.2.0 onFinish ONLY fires if a missile successfully reaches it's destination point.

Go with onRemove to catch when a Missile is deallocated.
 
Last edited:
Nice! Great idea with the "enableHitAfter" method, this is nice for stuff like "jumping" missiles that change targets after hitting a unit.


About your implementation of the rectangle collision:
1) maybe use more descriptive variable names: a and b should be dx and dy. s could be relDist (but this one is just imho, as it bloats up the length of your expressions).
2) set s = (a*(GetUnitX(u) - xPrev) + b*(GetUnitY(u)- yPrev))/(a*a + b*b) ... using the projected point on the trajectory to get IsUnitInRangeXY into the mix? GG I say!
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Nice! Great idea with the "enableHitAfter" method, this is nice for stuff like "jumping" missiles that change targets after hitting a unit.
I was playing Diablo II in the past and I thought about Amazon's Guided Arrow + Pierce ( If you know that one ).
It's an arrow which turns and eventually hits the target again and again and again, ...

I also like it and in performance it's a very cheap feauture.

1) maybe use more descriptive variable names: a and b should be dx and dy. s could be relDist (but this one is just imho, as it bloats up the length of your expressions).
I can do that.
set s = (a*(GetUnitX(u) - xPrev) + b*(GetUnitY(u)- yPrev))/(a*a + b*b) ... using the projected point on the trajectory to get IsUnitInRangeXY into the mix? GG I say!
I was thinking for quite some time, what formula fits best.
Finally I have to give credits to Vexorian ( once again ), because I found Disintegrate.
It was better than my first attempt, I was not thinking about IsUnitInRangeXY, so I just transformed his code to fit into Missile.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Of course. I know that from school. :) Uhh school that's now 7 years past ...

Just quickly towards the mechanics of rectangle collision.

Collision checks between two timer timeouts overlap, because the maxX / maxY of the previous loop is the minX minY of the next loop.

If you disagree and say there should be an offset in plne x / y, then I have to set up a minimum vector length for missiles in percent.
An absolut value i.e. 1. is not valid, because there can be missiles with a speed vector of for example 0.0003 ( Falling meteors )
A minimum speed would be for example (speed / 999999). Then on the other side the maximum speed for missiles in general would be 999999.

My conclusion is that this doesn't have an impact on map making, hence is useless to implement.
 
Last edited:
Of course. I know that from school. :) Uhh school that's now 7 years past ...

Just quickly towards the mechanics of rectangle collision.

Collision checks between two timer timeouts overlap, because the maxX / maxY of the previous loop is the minX minY of the next loop.

If you disagree and say there should be an offset in plne x / y, then I have to set up a minimum vector length for missiles in percent.
An absolut value i.e. 1. is not valid, because there can be missiles with a speed vector of for example 0.0003 ( Falling meteors )
A minimum speed would be for example (speed / 999999). Then on the other side the maximum speed for missiles in general would be 999999.

My conclusion is that this doesn't have an impact on map making, hence is useless to implement.
Actually, unlike the 999999 speed, the "zero speed" missile thing is important. I had issues with that in the past when I wanted to use missile to replicate the blizzard ability. I had to offset the start and landing point by some coordinate units for the system to take it without crashing the game.

You should probably revisit your algorithm to allow stuff like this. Or at least build in some internal protection so that the game doesn't crash from a divide by zero error.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Uffff well missile doesn't allow that the origin point equals the impact point,
but doesn't crash if you would pass in equal points on the map.

This is from MissilePosition, while a and b are origin and impact.
Basically Missile changes your point arguments minimal, to keep the thread running.
( a.alpha is the base value for the pitching angle for the SetUnitAnimationByIndex() native. )
JASS:
            private static method math takes thistype a, thistype b returns boolean
                // Set a.
                set a.angle     = Atan2(b.y - a.y, b.x - a.x)
                set a.distance  = SquareRoot((b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y))
                if (a.distance != 0) then
                    set a.slope = (b.z - a.z)/a.distance
                else// For distance == 0 the thread crashes! 
                    set a.x     = a.x + .0001
                    return false// Runs the thread again.
                endif
                set a.alpha     = Atan(a.slope)*bj_RADTODEG
                // Set b.
                set b.angle     = a.angle + bj_PI
                set b.distance  = a.distance
                set b.slope     = -a.slope
                set b.alpha     = -a.alpha
                return true
            endmethod
            
            static method link takes thistype a, thistype b returns nothing
                set a.ref = b
                set b.ref = a
                loop
                    exitwhen math(a, b)
                endloop
            endmethod
Now it's difficult to setup a speed vector for low distances.
That's why this method exists: ( Does not work properly if an acceleration is set )
JASS:
        // Please do not pass in 0.
        method flightTime2Speed takes real duration returns nothing
            set speed = RMaxBJ(0.00000001, (origin.distance - distance)*Missile_TIMER_TIMEOUT/duration)
        endmethod

This spell is a good example ( link: Armageddon )
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
I've updated Missile to v.2.0.2.1 ( [self=http://www.hiveworkshop.com/forums/jass-resources-412/missile-265370/#post2681196]Changelog[/self] ).

Those who use v2.0.2 must not update to v.2.0.2.1, because the changes are too minimal.

Now I consider Missile as a finished resource, unless someone comes up with a genius idea or request...

There is only one small thing in the code which maybe is not a good idea:
Using the constant id for structs as array index.

Struct ids start off with 1 and increase 1 per struct. I guess struct ids never go over 8910.

But if the first struct which uses Missile in your map has the id 120 ..., then there
are 120 unused array indexes. On the other hand using the struct id gives you
perfect controll over Missile as you could in theory access a missile list of a specific struct via for example missileList[Fireball.structid].
I would like to keep it the way it is now. Is anyone against that?
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I had to update to v.2.0.2.2, because the idea of using SetUnitPosition when no
WorldBounds/BoundSentinel is in the map was stupid.
The fired stop order canceled animations / orders for non dummy type units.

Missile now draws on RectContainsCoords(bj_mapInitialPlayableArea, x, y) if you don't have one of those libraries.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I heard that Pausing a unit before using SetUnitPosition, then unpausing conserves the original order and doesn't issue the stop order.
I don't know. What I know is that you can use Pause / stop / Pause to cancel an spell effect event.
In general I don't like it when methods gets too hacky and abusive.

I was more concerned about non dummy units.
Since I added the createEx method, you can launch any unit, I'm sure you're aware of that.
I took advantage of that in the barbarian leap spell I've made.

As normal casting unit, I don't want it to be paused, therefore not having
WorldBounds would break the entire spell or a least the jump attack animation.

I assume that WorlBounds or BoundSentinel is present in most maps.
 
I've encountered a little issue where curved missiles travel forward and around halfway through begin the curve, unlike in the demo map where the curve starts instantly. This only seems to happen when the target is not directly to the east or west.

I'm pretty sure the problem must come from my end (though I have no idea what's causing it) since curved on the Missile demo map is working correctly, I'm just curious if you've ever encountered something like that before.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I've encountered a little issue where curved missiles travel forward and around halfway through begin the curve, unlike in the demo map where the curve starts instantly. This only seems to happen when the target is not directly to the east or west.

I'm pretty sure the problem must come from my end (though I have no idea what's causing it) since curved on the Missile demo map is working correctly, I'm just curious if you've ever encountered something like that before.
Thanks for reporting, I will do some tests to see if the problem is inside Missile.
If not I can help you to find the problem in your code.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I'm on it.

Once we found the bug I will update the homing missile behaviour to
properly update the MissilePosition struct variables.
Currently you loose .origin values once you call target=, that shouldn't be.

I also started to write Missile GUI as small brother of Missile for the Spell Section.

I had a case in your demo map, where all Missiles stop to move, which
makes me believe that I made a mistake in the way the
periodic timer starts ands stops. IF that's the case, I'll fix it this week.
 
Oh yeah, I just noticed that yesterday too. After a friend was testing it for me he said that if there were too many missiles at once on the map, everything would freeze up. I managed to replicate this behavious by having all units attack at the same time.

If it helps, the only timer I'm using in the demo map is a repeating timer to launch missiles one after the other (with TimerUtils).
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
It's not directly timer related. Missile starts running on the first instance launched
and stops running when the last instances terminates.
This works for all normal case of create and destroy missile objects.

There are special cases were we have to protect the internal linked list
of trigger conditions added to the missile core trigger.
The code I used should also be able to deal with this issue,
I will controll it today, when I come home.

For understanding. The data structure of trigger conditions of a trigger is ( probably ) a doubly linked list.
There is TriggerAddCondition and TriggerRemoveCondition
to insert ( at position, I don't know first or last? ) and remove triggerconditions from a trigger.
Removing a condition outside a TriggerEvaluate event is safe.
Removing a condition inside that event will make the list loose the next node ( the one after the condition beeing removed )
and the internal data structure fails.

If there is a bug with the data structure, I will fix it today.

To do list:
  • Checking start/stop system timer.
  • Fixing target=, so all position, distance and vector members still update properly.
  • I will work again on the destructable collision to make it absolut similar to unit collision.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
I've updated to 2.0.3.
The way how trigger conditions are removed and added should work now flawless.
There seemed to be problems in previous versions.

I will have to change a few things with item and destructable collision.
This will result in that onDestructableFilter / onItemFilter are no longer part
of the Missile API from version 2.1 on.

Destructable & item collision will be totally similar to unit collision from version 2.1 on.
Currently you can only one destructable per iteration, from 2.1 on you will hit all of them if they are in range.

Edit:
Updated to 2.1.

onDestructableFilter / onItemFilter are no longer supported in the Missile interface. ( They were crap anyway )
The item and destructable collision is now absolut similar to the unit collision, probably also faster.

Now I'll try to fix target=, so all position and vector missile members update properly after calling that method.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
Well, I stress tested the 2.1 version and it still bugs outs when there are too many projectiles out and they all stop moving :\
With your test map? How can I reproduce the bug?
I will try it later.

I was wondering if your code runs into an OP limit, when too many missiles are flying.
All static method of Missile run via function call and not a trigger evaluation.
Missile runs all projectiles of yours in the same thread, as all are launched from the same struct.

That however wouldn't explain why the timer stops running.
It would just crash the current thread.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Yes I'm also curious about your testing area.
That's very important, because if the number is relative low
I might have to change the data structure in Missile so that one list
of missile types can't exceed for example 150 nodes.
Instead a new list must be started which runs in an own thread.

Another solution is that you start new threads in your own code.
I was never expecting that the code running after i.e. onPeriod
is huge, as Missile does already quite a lot for the user.

If it's not an OP limit you are running in I want to fix the bug in Missile.
 
A rough estimate would be around 200 missile is when the system freezes. Could be less, I just calculated the number of missiles some units would attack with a for some doubled and for some tripled the number to account for multiple instances existing at the same time. I'm not sure how my own code could interfere with Missile.

Also if that's of any importance, I have USE_COLLISION_Z_FILTER set to false on Missile.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
A rough estimate would be around 200 missile is when the system freezes. Could be less, I just calculated the number of missiles some units would attack with a for some doubled and for some tripled the number to account for multiple instances existing at the same time. I'm not sure how my own code could interfere with Missile.
Ok thanks. I start testing :)

Also if that's of any importance, I have USE_COLLISION_Z_FILTER set to false on Missile.
That is only be a performance factor. It has no direct influence on the core system of Missile.


Edit:
I've tested your map with 378 Missiles during the same time.
It took 10 minutes to create a missile freeze.
For me it looks like that Missile runs into an OP limit during the last
steps of the deallocation process. ( timer count decreases, but .deallocate is not called yet )
Honestly very intersting scenario. Let's see if what I assume is true...

This would mean that your system always creashes the thread, but only in that rare
case it actually matters for Missile's system solidity.

Even though Missile keeps on running a thread crash is very bad,
because it always produces one unit handle leak :/.

Edit2:
Looks like the thread is always running completly.
Maybe it's not Missile what crashed but the system which triggers firing projectiles in the first place.
I will also test that.


What I found so far:
In most cases the thread doesn't crash, but when all projectiles stop mid-air it does.
And from then on it ALWAYS crashes ... Now I have to find the source which causes the crash.

Next find: The thread crash is not in your code, it's in missleList.move().
There must be a rare case of a division with 0., which can only happen if d == 0
which is not possible as the code protect Missile form running in that issue. :D
I will fix it tonight.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
I've run a lot of test on all reported issues.
My conclusion:

Missile seems to loose stability on many projectiles?
Missile structures instances into doubly linked lists. These lists are struct id based,
therefore missiles launched from the same struct end up in the same list.
This provides solidity against OP boundaries if projectiles are launched from different structs.

In your demo map all missiles are fired from one struct. VolleyAttack.launch(missile).
When a certain list size is reached you run into a OP limit, that is not a problem by itself,
as Missile starts running correctly once a few nodes are removed from the list.
Critical is the position in the code where the OP limit is reached.
There is a small chance that the OP limit is reached, between missile.deallocate() and
private function StopPeriodic takes integer structId returns nothing.
In that case missile breaks and is for the entire game season broken.
As you fire hundreds of missiles per minute and you run frequently into OP limits with your code
that case will take place after a couple of minutes.

Solution:
I will seperate the thread for projectile motion from the rest
of the module MissileStruct interface via one trigger evaluation.
This will give Missile more stability against OP limit cases.

However finally your code in producing the OP limit by
firing ~350+ Missile from one struct during the same time and
running a lot of code and calculation after onCollide, onFinish.

Why did all projectiles stop mid-air?
That one was tricky. Missile draws on pythagoras and by definition you can't divide with 0.
That's why Missile checks if origin and impact share the same point and moves
one of the two points by a minimal offset. Seems reasonable ...

What I didn't see is in here set z = z + (4*h*s*(d - s)/(d*d)) and
any other calculation of d*d in Missile(this).move().
d is the distance between origin and impact, with the help of MissilePosition never 0.
But d*d for very small values is rounded to 0. Then you divide with 0., the thread crashes, no missile is moved.

Solution:
I'll check for d*d= 0 and set it to a fictional small value instead.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Update to 2.3

What is new?
1.) Missile motion and struct method evaluation is now seperated into two threads
via trigger evaluation. Before it was running in one thread via function call.
There is no real extra overhead so don't worry.
You can have now ~double amount of missiles from one struct.

JASS:
            set Missile.temp = this
            static if DEBUG_MODE and LIBRARY_ErrorMessage then
                if not TriggerEvaluate(MOVE) then
                    call Missile.temp.crash("thistype", "Motion trigger")
                endif
            else
                call TriggerEvaluate(MOVE)// Moves all missiles of this collection. 
            endif

2.) In case you run into an OP limit, Missile will detect that and return
an error message. Only if you have library ErrorMessage.
Credits to Waterknight for his debugging tutorial using the
return value of trigger evaluate in order to see if a thread ran or not.
JASS:
static if DEBUG_MODE and LIBRARY_ErrorMessage then
    method crash takes string name, string operation returns nothing
        local string text = "Current thread crashed by reaching the OP limit.\nCheck your code carefully!"
        call ThrowWarning(true, "Missile", name, operation, this, text)   
    endmethod
endif

3.) Reworked destructable and item collision. It runs now similar to unit collision.
Before you could only run onDestructable for one non unit widget per timer timeout.
Now it runs for all in collision range.

4.) The API no longer supports onDestructableFilter & onItemFilter. Both were crap anyways.

So the Volley Attack Allocation trigger wasn't doing anything then? It was the timer?
I was not reading the volley attack code. I focused on problems from Missiles side.
You demo map runs now with the new missile code I posted. Sometimes your units do not fire new missiles,
but that problem is in the volley attack code.

The goal was/is that you as user can do whatever you want in other libraries using Missile.
If you make a mistake concerning the Missile API ErrorMessage prints an ingame error message.
If there is no debug error message, everything is must be perfect.
 
Sometimes your units do not fire new missiles,
but that problem is in the volley attack code.
Really? o_O Was that always happening or did that start when you updated Missile?

At least it's not crashing anymore. Tested it the same and and it all worked out nicely. Now, if only you could fix .target to work with curve and arc :p also it looks like it's still applying damage multiple times.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Now, if only you could fix .target to work with curve and arc :p also it looks like it's still applying damage multiple times.
I know that target= is broken, at least when combined with curve= or arc=.
I started thinking about parabola and other math already yesterday with pencil and paper.

Missile works great for projectiles with static origin and impact point,
because many calculations are just done once on Missile.create().
But for target= all these informations must be repeatedly re-calculated.

I wonder how Blizzard made it for their homing projectiles.
It seems they also use a hardcoded maximum turn rate.

also it looks like it's still applying damage multiple times.
Missile doesn't apply damage at all. It's up to the user to run damage code,
in your case damage is applied in your volley attack code.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
Yeah, I meant it appears that it runs onFinish multiple times.
onFinish can run infinite times, if "false" is returned from the function.
onFinish just indicates that the impact point is reached.

If you return true from onFinish it will not run again for that missile instance.
Do you use ErrorMessage?
Missile can detect a lot of user mistakes, but only if ErrorMessage is present in your map.
 
Here's the deal, when I do Missile.createXYZ, everything runs fine. When I do Missile.create with .target, the missile starts behaving weirdly. If I make the missile bounce, it will either repeat the onFinish as many times as there are bounces on the one target, or it will split to as many targets as it's supposed to bounce. This doesn't happen with .createXYZ

The bounce trigger is in the Volley Attack Addons folder.

ErrorMessage doesn't find any bugs.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Missile.create with .target
.target= is not implemented well. It already caused me quite a headache,
but I didn't come up with a good solution without re-writing the projectile motion
part from the scratch.

I tried to figure out how Blizzard deals with homin missiles and arcs.
It seems their missiles system code seperates arcing and homing.
The missiles updates it's z and pitch as if origin and impact are static points,
once that distance is traveled it continues homing without an arc.

I will not make an update on .target= in the nearest future.
Don't be disappointed.

I've updated to 2.4

You could take a look into Tank Commanders master projectile or
Wietlol's Projectile system. Maybe they have implemented homing missiles in a way that it works
with curving or arcing projectiles.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
I've updated to version 2.5, because the compensatory mechanism for missiles
with equal origin and impact location was prone to hit the OP limit.
The impact was moved by steps of 0.0001 in x axis until the distance is not 0.
That was a too small value as warcraft rounds it after square root function to 0.
The chance for running into an OP limit was too high.

Now steps of 0.01 in x axis are used instead of 0.0001.
Thanks to Chaosy for reporting that problem.

module MissileStruct has been split into
1.) module MissileLaunch
2.) module MissileTerminate
3.) module MissileAction
4.) module MissileStruct ( this one implements all 4 )
The advantage is that you can place module MissileLaunch at the
very top of the struct. If you create a new missile instance from one of your
Missile interface methods like in onCollide, no pseudo code must be generated by the JassHelper.
Basically you gain a bit more controll over the code in module MissileStruct, just in case you need it.
JASS:
    module MissileStruct
        implement MissileLaunch
        implement MissileTerminate
        implement MissileAction
        
        private static method onInit takes nothing returns nothing
            call MissileCreateCollection(thistype.typeid)
            call MissileCreateExpression(thistype.typeid, function thistype.missileIterateP)
        endmethod
    endmodule

Optimized collision detection a bit.

Removed collisionZ as member ( still there for bc )
JASS:
        // Setting collision z is deprecated since Missile v2.5. 
        method operator collisionZ= takes real value returns nothing
        endmethod
        method operator collisionZ takes nothing returns real
            return collision
        endmethod

Seperated missile motion into a own trigger and own list.
That's faster, generates less code and gives us a list of all launched missiles...

I'll implement onMissile in the next version.
Now that I have a list of all missiles, it's very easy.
It will not be very fast I fear.

Edit:
Also nodes are no longere enqueued to the list, but instead pushed front.
 
Last edited:
Level 17
Joined
Mar 21, 2011
Messages
1,597
Just a minor question. I am using your system for knockback too. My problem is, that every missile will face the direction it is moving to. Is there a way to turn that off just for my knockback actions? Also, is there a way to add a periodic effect to the missile?
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Just a minor question. I am using your system for knockback too. My problem is, that every missile will face the direction it is moving to. Is there a way to turn that off just for my knockback actions?
The unit facing updates every timer interval towards direction of flight.
There is not much you can do against that in the official version.
What you can do is add a member to the Missile library named i.e. boolean facing which is by default true,
if you don't need it you'll set it to false. I'm not doing that in the official version,
but feel free to manipulate the system to your needs.

The missile facing is updated in static method move, here:
JASS:
// Move the missile dummy via native.
                call SetUnitFlyHeight(u, z, 0.)
                call SetUnitFacing(u, a*bj_RADTODEG)
                
                // WorldBounds > BoundSentinel.
                static if not LIBRARY_BoundSentinel and not LIBRARY_WorldBounds then
                    if RectContainsCoords(bj_mapInitialPlayableArea, newX, newY) then
                        call SetUnitX(u, newX)
                        call SetUnitY(u, newY)
                    endif                    
                elseif LIBRARY_WorldBounds then
                    if newX < WorldBounds.maxX and newX > WorldBounds.minX and newY < WorldBounds.maxY and newY > WorldBounds.minY then
                        call SetUnitX(u, newX)
                        call SetUnitY(u, newY)
                    endif                
                else
                    call SetUnitX(u, newX)
                    call SetUnitY(u, newY)
                endif
^You may end up having something like
JASS:
// Move the missile dummy via native.
                call SetUnitFlyHeight(u, z, 0.)
                if facing then
                    call SetUnitFacing(u, a*bj_RADTODEG)
                endif
                
                // WorldBounds > BoundSentinel.
                static if not LIBRARY_BoundSentinel and not LIBRARY_WorldBounds then
                    if RectContainsCoords(bj_mapInitialPlayableArea, newX, newY) then
                        call SetUnitX(u, newX)
                        call SetUnitY(u, newY)
                    endif                    
                elseif LIBRARY_WorldBounds then
                    if newX < WorldBounds.maxX and newX > WorldBounds.minX and newY < WorldBounds.maxY and newY > WorldBounds.minY then
                        call SetUnitX(u, newX)
                        call SetUnitY(u, newY)
                    endif                
                else
                    call SetUnitX(u, newX)
                    call SetUnitY(u, newY)
                endif
Also, is there a way to add a periodic effect to the missile?
You can use static method onPeriod takes Missile missile returns boolean in your struct.
This function will be called every timer interval for missiles launched from this struct.
JASS:
struct Fireball extends array

    // Runs every Missile_TIMER_TIMEOUT for missiles launched from Fireball.
    private static method onPeriod takes Missile missile returns boolean
        call DestroyEffect(AddSpecialEffect("MyModel.mdx", missile.x, missile.y))
        return false
    endmethod

    implement MissileStruct
endstruct
^ This one runs every 1/32 seconds, if you need an effect every for example second,
you maybe have to add an array variable to your struct.
JASS:
struct Fireball extends array

    private static real array time
    // A missile instance can be used as unique array index.           
    private static method onPeriod takes Missile missile returns boolean
        set time[missile] = time[missile] - 0.03125
        if time[missile] <= 0 then
            set time[missile] = 1.00
            call DestroyEffect(AddSpecialEffect("MyModel.mdx", missile.x, missile.y))
        endif
        return false
    endmethod

    implement MissileStruct
endstruct
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
In the "interface for structs" section, you have onDestructableFilter listed twice. One of them is meant to be onItemFilter.

I wish this had been around while I was making my map. It looks pretty cool.
Thank you. Personally I enjoy using Missile in my projects, the API turned out the be very satisfying :)

Yes I have to update the first page description. I made it back then for version 1.5.
For example the "onDestructableFilter" doesn't exist anymore,
as it turned out to be not as useful as I thought in the beginning.
I will do so asap.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Spellbound contacted me via VM, because of an issue he had with Missile.
So I started using Missile again (because TankCommander's MPS is hard to configure, I can't make sense of it @_@). I'm trying to make shoot a unit as a missile and then make it return to it's original position. Trouble is, when I try to do it, it either doesn't work or lags the game like crazy.

when the missile in question reaches onFinish, it does the following:

Jass:
if SpecialFeature[m] == 1 then
set m = Missile.createEx(m.dummy, returnX[m], returnY[m], returnZ[m])
call launch(m)
When I do the above, the dummy doesn't move anymore.

Jass:
if SpecialFeature[m] == 1 then
call Missile.createEx(m.dummy, returnX[m], returnY[m], returnZ[m])
call launch(m)
This one causes the game to lag to the point where I can't even open the menu. If I remove the call launch(m) then it doesn't lag but it bugs out hard and the dummies flicker all over the place like they're constantly being moved between two points.

I didn't bother changing the m.speed or whatnot since this was inside the onFinish method, which meant the various m.speed/m.curve/etc were already configured.

EDIT: So when I re-set the speed and stuff it actually fired back to it's original position again, which means... are the m.speed and stuff set to 0/null onFinish?
EDIT2: I just realised something: if I do set m = Missile.createEx(m.dummy, returnX[m], returnY[m], returnZ[m]) , does that reset all the m.speed and stuff to 0?
The thing is that you were trying to create a new Missile instance from an still active missile dummy unit.
That's where the flickering effect comes from.

I'll work on an new update, where dummy units remember from which struct they were launched and which Missile instance they are connected to.

Towards:
EDIT2: I just realised something: if I do set m = Missile.createEx(m.dummy, returnX[m], returnY[m], returnZ[m]) , does that reset all the m.speed and stuff to 0?
Yes of course. "m" is a new instance of Missile without any specific properties.

Edit2:
@Spellbound: Try this one.
JASS:
call missile.impact.move(returnX[missile], returnY[missile], returnZ[missile])
call missile.bounce()
 
Last edited:
Well the reason why I asked about "m" was that it was being set inside the onFinish method:
private static method onFinish takes Missile m returns boolean. My initial thought was that m was already defined, so setting m to new coordinates with .createEx and launching it would launch the dummy back. In the end, I solved it by re-giving it new values after calling the .createEx:

JASS:
set Speed           = (SquareRoot( (returnY[m] - m.y)*(returnY[m] - m.y) + (returnX[m] - m.x)*(returnX[m] - m.x) ) / 2)  * .03125

set m               = Missile.createEx(m.dummy, returnX[m], returnY[m], returnZ[m])
set m.speed         = Speed

call launch(m)

The missile.bounce() solution you suggested just made the dummy move instantly to it's origin point.
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
How is this library compared to Kenny's missile system? I'm mainly interested in performance and features. I think it's definitely faster, but I'm interested in your results, and I'd like to know if you can do such advanced things with this system as missile enumeration (destroy all missiles targeting a specific unit, reflect all missiles around a unit coming from a specific angle, slow/haste/modify missiles in an area, etc.). I know I could propably find most of that out from the documentation, but honestly, it's pretty long and detailed, plus I haven't written any vJASS code in years, so you could definitely save me some time :)
 
Top