Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
I'm didn't read Kenny's system yet, so a link would be helpful.
Then I can tell you the differences.
You can do really a lot of things EXCEPT the things you mentioned.
Missile enumeration is not very well supported, because no-one so far requested that feature.
Instead I was asked to keep things as easy and simple as possible.
However it would be easy for me to implement what you are asking for.
The required data structure is already there, just that there is no API
for missileCollidesWithMissile, EnumMissilesOfType, EnumMissilesInRange and
GetInstanceByMissileDummy.
I know I could use an EnumMissilesInRange function to make an aura that can affect missiles. Eg: a unit has a aura that can force missiles in flight to change course/target.
I know you joined the party kinda late, so it seems like you haven't heard of it It's a projectile system back from thehelper.net was a good place and Jesus4lyf was still around, that has a tons of features. You can read its documentation, it contains the projectile and projgroup API.
Thanks for the link. I'll take a look into it asap.
Edit:
So I started reading Kenny's code. It's pretty cool. It has more features. Some of them are very neat.
Personally I think the API in there is a bit more convoluted.
It's definitly worse in performance, not only due to trigger evaluations vs function calls.
What's so great about this version that you completely discard the already-perfect-and-awesome previous version? Did you consider to submit this one as another Missile lib, named BezierMissile or something? I don't have idea how could you throw your great work away just like that. I got a heartbreak here.
The previous missile library had a major flaw.
It wasn't working properly in 3D space, but only in 2D.
Speed was a vector in x and y axis, but z was left out.
For example you were not able to make a jump in place missile or
a falling meteor.
Don't worry I will not delete the old missile system,
but to me it was not satisfying in the end.
I could give this library the name Projectile if people want me to.
I will write a detailed tutorial about this library soon.
That can be achieve by using Missile_MOTION_DRIVER_NONE.
You can set start angle, start pitch, speed, acceleration and gravity.
onPeriodic gives you loop controll if you wish to change angle, pitch, ...
in a certain phase of the missile movement.
JASS:
static if thistype.onPeriodic.exists then
if this.exists then
static if LIBRARY_MissileCollision then
call this.cacheUnitPos()
endif
if thistype.onPeriodic(this) then
call thistype.terminateM(this)
endif
endif
endif
onTerrain is called if the missile hits the ground.
JASS:
static if thistype.onTerrain.exists then
if this.exists and this.z <= this.minFlyHeight then
call GetTerrainNormal(this.x, this.y, TERRAIN_TILE_RADIUS)
// Compute the dot product of the terrain vector and missile velocity.
if tempX*this.velX + tempY*this.velY + tempZ*this.velZ < 0.00 and thistype.onTerrain(this, tempX, tempY, tempZ) then
call thistype.terminateM(this)
endif
endif
endif
Missile_MOTION_DRIVER_BALLISTIC is the arcing movement
which is used for hardcoded warcraft projectiles. method setArcByTime takes real tX, real tY, real tZ, real arc, real time returns nothing
and method setArcBySpeed takes real tX, real tY, real tZ, real arcValue, real newSpeed returns nothing
Parabolic-based(simple "give start-end points and max height")
Huh? Why not? Jump in place could be solved by using onFinish or onTerrain and re-launching the missile at reduced speed. A falling meteor was just a missile spawned at a very high starting Z? Am I missing something here?
Huh? Why not? Jump in place could be solved by using onFinish or onTerrain and re-launching the missile at reduced speed. A falling meteor was just a missile spawned at a very high starting Z? Am I missing something here?
In the previous Missile version the speed was defined by vector components velX and velY.
Also the total distance traveled was calculated by the horizontal velocity*time.
vertical velocity ( movement in z ) was computed by pitch*distanceTraveldXY.
A meteor was de facto not possible, because if the displacement in plane xy is zero,
z doesn't alter ( z = slope*distanceXY ). I built in a mechanism to compensate that issue, by
replacing 0 xy distance with a very small value and adjusting the speed ( xy displacement per tick ) to also a very small value.
In the new code speed has 3 vector components. velX, velY, velZ.
Here you can have speed = 15 while velX = 0, velY = 0, velZ = 15.
I know that the previous Missile fits perfect for most requirements.
It's just not so perfect from an aspect of making a projectile system.
It's a bit sloppy.
I would agree on re-naming the new system I wrote to library projectile and keep the old
one as library Missile.
I've ran into flaws in the new version. It will take time to fix them.
After some consideration I restored the previous missile library and give
the new system another name. I think they can co-exists as they are different in code.
Therefore I will first update the old Missile to a final version 2.6
with optimizations here and there.
Then I'll upload the fixed other system under the name Projectile.
I ran into a little issue when using collision with Missile. If a projectile is supposed to deal area damage on collision, and the collision of the struck object is quite large (like a building), then I call a GroupEnumUnitsInRange to pick everything in the projectile's detonation aoe. The problem that seems to arise is that if the radius of the GroupEnumUnitsInRange doesn't reach the center point of the building, the building doesn't get picked.
Is there a way I can remedy to this, or a function within Missile itself that addresses this?
Wait you use GroupEnumUnitsInRange yourself instead of using the internal missile collision detection. I see.
You'll have to pick all units in collision range + MAX_COLLISION_SIZE to make sure all desired units are picked.
The largest collision size in Warcraft is the townhall pathing, which is 196.00. Unit pathing normally doesn't surpass 64.00.
Then use the IsUnitInRange(dummy, whichUnit, dummyCollision) native.
"whichUnit" has a collision size which is defined in the object editor.
The dummy has an internal collision of 0.00. That's why you need the "dummyCollision" argument.
IsUnitInRange is an internal distance comparison,
while dX and dY is the distance between x and y coordinates of unit A and unit B
and crA and crB are the obejct editor defined collision radii of unit B and unit B
and "range" is the dummyCollision argument from above
You'll have to pick all units in collision range + MAX_COLLISION_SIZE to make sure all desired units are picked.
The largest collision size in Warcraft is the townhall pathing, which is 196.00. Unit pathing normally doesn't surpass 64.00.
Then use the IsUnitInRange(dummy, whichUnit, dummyCollision) native.
"whichUnit" has a collision size which is defined in the object editor.
The dummy has an internal collision of 0.00. That's why you need the "dummyCollision" argument.
IsUnitInRange is an internal distance comparison,
while dX and dY is the distance between x and y coordinates of unit A and unit B
and crA and crB are the obejct editor defined collision radii of unit B and unit B
and "range" is the dummyCollision argument from above
Huh, I see. I did GroupEnumUnitsInRange and went with Missile AoE radius + Missile_MAXIMUN_COLLISION_SIZE and then checked with if IsUnitInRange(m.dummy, u, m_MissileAOE[m]) then
Seems to be working
I would like to know what using the internal Missile collision detection looks like. I do use the m.collision to detect onCollide, but otherwise idk how else to make use of this.
The internal collision is the check that gives you the event "A unit collides with a Missile" (the one created by the system).
You had to do the exact same check to make sure that you include units by collision.
That is what you are doing now.
Also, a quick note to method enableHitAfter ... from the description it seems that this doesn't work if the unit was just hit? How am I supposed to add units dynamicly then? If I use it onCollide, it's already too late, isn't it?
What I'm saying is: there should be a method enableHitAfterAll, which automaticly does enableHitAfter for every unit it hits.
I started to write a v. 2.6 of Missile, but the problem is that is would not be backwards compatible to the current version.
While I made less important minor changes here and there,
the main issue is the current create / createXYZ / createEx methods.
Currently we have:
static method create takes real x, real y, real z, real angle, real distanceXY returns Missile
This is good for a 2D scenario, but in 3D it's rather bad, because there is no pitch argument in this method.
The current createXYZ is ok, because you can derive an target angle and target pitch from its arguments.
At this time I think that the creator method should have been always like: static method create takes real x, real y, real height, real angle, real pitch returns Missile
This creates a Missile object at pos x/y with a certain fly height, an initial yaw ( angle ) and pitch. ( There is no roll in wc3 )
Methods like: method setTargetPos takes real x, real y, real height returns nothing
allow you to define a target position and target distance.
Eventually it would require two boolean arguments for applyTargetAngle / applyTargetPitch.
I prefer "height" over "z", because I think it's unnecessary to work with absolute z value in Warcraft.
A lot of Missile systems use vectors, simply because they are easy to modify
and calculations like dot product and projection vector can be very handy.
Another argument is that they are quite fast, because posX + velX is faster than posX + speed*Cos(angle)*Cos(pitch)
I still want to stick to trigometric functions ( Cos / Sin ), simply because the
previous Missile version was using aswell.
Furthermore Cos(angle)*Cos(pitch)can be cached and the speed difference becomes minimal.
If I release a new Missile version, then it will not be compatible with the previous version
concerning the struct creators.
Huh, not compatible, you say? Are the create methods the only thing that are going to change? I mean, how many lines am I (potentially) going to have to change to make the new version work?
I can't decide how to re-implement arc / curved missiles.
In my eyes there are more elegant solutions ( ? ) than the parabola function
z = (4 * h / d) * (d - x) * (x / d)
For example a quadratic bezier, which would also allow a smooth jump in place.
This however is a curve movement over time and not via speed.
Or as probably Wc3 does internally by manipulating the z speed vector component and acceleration:
set velZ = ((distance * arc) / (flyTime / 4.00) + distanceZ / flyTime ) * TIMER_TIMEOUT
set accZ = 2.00 * (distanceZ / flyTime / flyTime * TIMER_TIMEOUT * TIMER_TIMEOUT - (velZ * TIMER_TIMEOUT) / flyTime)
Or all 3 ( 4 ) options and an integer array to decided which type of core movement driver the missile should use?
Or write a new library which covers everything ( collision, MissileStruct module, etc ) but no motion phase and let the user code the missile motion by him/herself.
That last one sounds good.
I went to go for 3d motion calculations and this is one of the reasons why.
Having a jump in my system would start with lets say 400 speed per second and a 90 degrees pitch, which would result in (0, 0, 400) as xVelocity, yVelocity, zVelocity.
The gravity would do the rest (also coded in the system ofcourse)... games have no real gravity
I have experienced a strange bug with this system. Sometimes when creating and launching a dozen missiles at the same time (typically when creating a circle of missiles after a missile removal), a few of them will not appear. This happens at random. From my observation, they indeed are created and are launched but the travel time is instant, because some of my units would get hit by "nothing" at the same time as the circle of missiles was created.
Wc3's built-in artillery (e.g Mortar Team) projectiles have an arc parameter (Combat - Attack 1 - Projectile Arc (uma1)) that seems to work like this:
when the projectile arc is set to 1.0 and the unit fires from a distance of say 500.0 units, the projectile's maximum reached height will be 500.0 units, if it fires from a distance of 1000.0 the max height will be 1000.0, etc.
Now my question is, how can I do this with Missle, i.e launch a parabolic missile that will reach a maximum height equal to that of the distance between the "initial position" and the "impact position"? I am ignoring the case when launching from lower/higher z to higher/lower z, although I suppose it should work in that case as well with a maximum height reached bigger than the distance.
The problem with that is that if the target moves, this calculation would be incorrect.
I assume that if it is set to 0.5, it would be 0.5 * distance(/2 ?) as max height.
With 2d movement calculations, this could be simple, but with 3d movement calculations, it gets really hard.
method operator flightDur takes real duration returns nothing
method operator moveSpeed takes real value returns nothing
local Missile m = Missile.createXYZ(x,y,z,tx,ty,tz)
set m.flightDur = 1. // 1 second
set m.moveSpeed = 800. // Warcraft III Speed
instead of
JASS:
method flightTime2Speed takes real duration returns nothing
method setMovementSpeed takes real value returns nothing
and can you explain why static method onPeriod is running so fast on this code
JASS:
//! zinc
library Rake requires Missile
{
private
{
constant integer SPELL_ID = 'plsh';
constant string MISSILE_MODEL = "Abilities\\Weapons\\GlaiveMissile\\GlaiveMissile.mdl";
constant real MISSILE_SCALE = 1;
constant integer MISSILE_TOTAL = 10;
constant real DISTANCE = 600.;
constant real TRAVEL_DURATION = 0.5;
constant real BASE_DAMAGE = 80.;
constant real LEVEL_DAMAGE = 50;
constant real COLLISION = 32;
constant boolean DAMAGE_ONCE = false;
function Damage(integer lvl) -> real{return BASE_DAMAGE + LEVEL_DAMAGE * lvl ;}
}
private
{
real time[];
struct Rake extends array
{
static method onFinish(Missile m) -> boolean {return false;}
static method onPeriod(Missile m) -> boolean
{
integer data = m.data;
unit c = m.source;
time[data] = time[data] + Missile_TIMER_TIMEOUT;
BJDebugMsg(R2S(time[data]));
if (time[data] >= TRAVEL_DURATION){
//m.origin.move(m.x,m.y,m.z);
//m.impact.move(GetUnitX(c),GetUnitY(c),m.z);
}
return false;
}
module MissileStruct;
}
// ENDSTR
function AbetXY(real x1, real y1, real x2, real y2) -> real
{
return bj_RADTODEG * Atan2(y2 - y1, x2 - x1);
}
function PolarX(real source, real distance, real angle) -> real
{
real x = source + distance * Cos(angle * bj_DEGTORAD);
return x;
}
function PolarY(real source, real distance, real angle) -> real
{
real y = source + distance * Sin(angle * bj_DEGTORAD);
return y;
}
function AngleFormula() -> real
{
integer i = MISSILE_TOTAL;
if (i == 0)
{
return 0.;
}
if (i < 4)
{
return 40. / (MISSILE_TOTAL - 1);
}
return 80. / (MISSILE_TOTAL - 1);
}
function ModifyMissile (Missile m, integer lvl)
{
m.model = MISSILE_MODEL;
m.scale = MISSILE_SCALE;
m.damage = Damage(lvl);
m.collision = COLLISION;
time[m.data] = 0.;
m.flightTime2Speed(TRAVEL_DURATION);
}
function onCast() -> boolean
{
unit c = GetTriggerUnit();
real x, y, tx, ty, px, py, angle;
Missile m;
integer i = 0, lvl = GetUnitAbilityLevel(c, SPELL_ID);
x = GetUnitX(c) ; y = GetUnitY(c) ; tx = GetSpellTargetX() ; ty = GetSpellTargetY();
angle = AbetXY(x, y, tx, ty);
if (GetSpellAbilityId() != SPELL_ID){return false;}
for(0 <= i < MISSILE_TOTAL)
{
px = PolarX(x, DISTANCE, angle + (i * 1 - (MISSILE_TOTAL / 2 - 0.5)) * AngleFormula());
py = PolarY(y, DISTANCE, angle + (i * 1 - (MISSILE_TOTAL / 2 - 0.5)) * AngleFormula());
m = Missile.createXYZ(x, y, 50., px, py, 50);
m.source = c;
m.owner = GetOwningPlayer(c);
ModifyMissile(m, lvl);
Rake.launch(m);
}
return true;
}
function onInit()
{
trigger t = CreateTrigger();
Cheat("iseedeadpeople");
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
TriggerAddCondition(t, function onCast);t = null ;
}
}
}
//! endzinc
on the onPeriod method, my BJDebugMsg(R2S(time[data])); printing 6~10x faster than Missile_TIMER_TIMEOUT.
example :
actual Missile time = 1 second
time[missiledata] = 8.712 second
it makes my code going wrong, and i got confused because it
EDIT : the onPeriod problem is solved!
the problem is, i dont set the missile.data, so missile.data always 0.
And i'm set time[missile.data] on the onPeriod method.
thank you a lot for creating this resource! I am interested in using this as it simplifies a lot without any down sides to me. I am glad, you added this excellent documentation so I can actually try to understand it. Thank you for that user-friendliness!
However I got some questions about this system which I think, you are able to answer pretty easily anyway..
Question 1----------------------------------------------------------------------------------------------
what is the purpose of the bounce method, if there is the deflect method? Is there some situation, where I would need to use that instead of deflect? (sry if this is a stupid question)
I think I do not understand it because I did not really get these 2 following methods. Sry, can you shortly explain this to me. So why do I have to use these methods and what are they doing? I am kind of confused about this after reading through the whole missile code.
Do these 2 functions simply reset the start and end point of the missile? It seems so, judged from the demo code, you provided (simple spell collection, guided arrow). but why dont you deflect it there?
Question 2----------------------------------------------------------------------------------------------
I think, there is a typo in the comments about the "onItemFilter"? you wrote 2x "onDestructableFilter", but I think, the second filter should read "onItemFilter". I was not able to spot this in the code, but I hope, the typo occures only in the description? Is this even a mistake, or did I read it wrong?
// By the way, I think there are some more typos in the explanation, e.g. toogles instead of toggles
Question 3----------------------------------------------------------------------------------------------
So, by reading the code and your explanation, it is pretty clear, we do not want unit indexers to recognize missiles, and you kindly provided
JASS:
/**
* Unit indexers and missiles ( Only if you don't use a dummy recycling library )
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* It is most likely intended that projectiles don't run through a unit indexing process.
* ToogleUnitIndexer runs:
* • Directly before a dummy is created.
* • Directly after dummy unit creation.
*
* Please return the previous setup of your indexing tool ( enabled, disabled ),
* so Missile can properly reset it to the original state.
*/
private function ToogleUnitIndexer takes boolean enable returns boolean
local boolean prev = true//UnitIndexer.enabled
// set UnitIndexer.enabled = enable
return prev
endfunction
But I do not really understand, how I would link that hypothetical unit indexer to missile. I actually do not use any unit indexer, so I wondered, whether the enabled/disabled state of the indexer can be dynamic, or would I simply add true or false like in the presets?
Sry, I am confused right there, could you simply put an example? Would it be
JASS:
private function ToogleUnitIndexer takes boolean enable returns boolean
local boolean prev = UnitIndexer.enabled
set UnitIndexer.enabled = enable
return prev
endfunction
Question 4----------------------------------------------------------------------------------------------
Phew, so this is the last question, thanks for reading all that.
It is short: When I copy over MissileRecycler by Bribe, do I have to follow the instructions for implementing it, or does missile do that for me? I guess, I have to follow the instructions, right? And, I assume, that the 'dumi' unit is the same correct one, that is needed for both systems, is that correct?
I have a question about the missile speed - how do the values work? If I look at the projectile speed of the sorceress, it's 900, so it covered 900 units in 1 second. But how does Missile handle speed? A value of 40 seems much faster than the 900 of the sorceress, but it also doesn't look like it's updating the x/y of the missile by 40 units every .03125 seconds. Can you shed some light on this? I need to know the unit of speed to be able to calculate other stuff.
real speed // Vector lenght for missile movement in plane x / y. ( DOES NOT TAKE THE TIMER TIMEOUT IN ACCOUNT )
method flightTime2Speed takes real duration returns nothing
// • Converts a fly time to a vector lenght for member "speed".
// • Does not take acceleration into account. ( Disclaimer )
method setMovementSpeed takes real value returns nothing
// • Converts Warcraft III movement speed to a vector lenght for member "speed".
^Those might be interesting for you.
40 per tick is faster than 900, or I misunderstand something? 32x40 = 1280 (per second)
Huh, for some reason I thought a value of 40 was not moving the missile 40 units per .03125 seconds. I guess I can just use that then.
That being said, how do I call these methods? Missile.flightTime2Speed(value) doesn't work. I'm still pretty fuzzy on a lot of the details pertaining to structs.
Q1: Method bounce, in short, resets the missile's traveled distance. It's useful for bouncing missile spell like dota's paralyzing cask, you determine the next "impact" position by your self (for instance to the next target unit position) using the call impact.move(x, y, z) method. While method deflect is useful for, well, deflecting missiles. It calculates the new impact point for you depending on the collision point of the missile and the object (unit/wall/etc), passed as parameter x & y. deflectEx allows you to determine the new impact z, useful for bouncing linear trajectory missiles, and maybe something else.
Q3: If you don't use any UnitIndexer then you don't need to touch that part. Different indexers probably have different approach to disable indexing (can be using boolean variable/function/ or method). I think his example is using Nestharus' UnitIndexer. If you use other indexer then you can ask me or moderator about how to edit that part, since BPower isn't active atm.
Q4: You only need to add locust ability to the DummyRecycler's dummy in the object editor, in case it doesn't have one yet.
That being said, how do I call these methods? Missile.flightTime2Speed(value) doesn't work. I'm still pretty fuzzy on a lot of the details pertaining to structs.
Apologies for necromancy, but I've ran into a little issue that I'm hoping can be resolved. When I call .createXYZ, the missile pitches up and down as it arcs properlly, but when I do .createEx it doesn't. I'm using my own unit instead of the normal dummy because A) I need that missile to be vulnerable and B) I'd like it to have team colour. Since this system requires the dummy.mdx model, I'm assuming it needs this becuase there's something specific about that that enables Missile to make the model pitch, so my custom missile is using dummy.mdx as a base model and uses an ability as the special effect that can be team coloured. Any insight as to how I fix this?
Iirc, in my hero spell pack there is an ability called "Hero Glow". It attaches team colored glow to my custom hero. Maybe using other model works too. I forgot what's the base ability tho. You can attach that ability to your dummy.
Well... the pitching issue seems to have resolved itself for no reason whatsoever, so I'm still very confused, but I do have another problem now.
JASS:
//onFinish
private static method onFinish takes Missile m returns boolean
if m.data == 1 then
set m.arc = 20. * bj_DEGTORAD
set m.acceleration = .35
set m.data = 0
call m.deflectEx(X[m], Y[m], 0.)
return false
else
call DestroyEffect(AddSpecialEffect(OWL_IMPACT_EFFECT, m.x, m.y))
//call UnitApplyTimedLife(m.dummy, 'BTLF', .01)
return true
endif
endmethod
It is my understanding that m.deflectEx will call bounce, which will reset the distance travelled... but it doesn't appear to reset the state of being onFinish, so the moment m.data is set to 0, it immediately calls onFinish again and destroys the missile. How can I stop this from happening? I'm trying to make the missile travel in a straight line and 600. units from impact it will pitch downwards and dive into the ground. I'm trying to do this by calculating the initial 'impact' location and then telling the missile to deflect to the actual impact when it reaches its first 'impact' point.
EDIT: After testing things out a bit more, I just ended up calling Missile.createEx() after destroying the first instance instead of fiddling with m.impact.move() (which was causing the missile to shoot straight down without any arc).
It's working nicely now:
There are many systems and snippets present in Jass Resources subforum that need an update or upgrade.
I've spoken with Ralle about the possibility of "filter" feature addition on this subforum and some categorization (division of threads to specific categories e.g. events, utils, containers).
Problem is, Ralle does not have much time currently, i.e. he has higher priority task related to THW than this.
This doesn't mean @IcemanBo or other moderators can't step in and fix at least some of the issues. Being idle or doing nothing is the worst thing we can do.
I could fix some of these, though I'm still in-process of fixing mine (that's why 1st page slowly becomes Bannar page because none updates their scripts).
However, without categorization and updating old resources, this subforum (Jass) is dead for any new user. One needs to ask too many questions, ask for everything, be unsure if this or that is working or not.
TLDR: DEAD subforum. Cries for help.
Hmm, I see. I had thought projectile systems would get special attention due to the sweet new natives that came with the new patches, allowing users to finally move special effects without the overhead of using units. I'll take a look and see if I can jerry-rig something for special effects inside of Missile, though I'm not very confident my vJass-fu will be up to task.
Does this system support interacting with other abilities? Like being able to change the speed, acceleration, or facing of a missile once it's already in flight?
If you know the instance of the Missile, you can modify those values without any issue. Changing facing may not be instant, however, since Missile still uses dummy units and unit facing cannot be set to an certain angle instantly.
In general, no, you can use systems without much knowledge (they are designed to be simple). In this case, however, you’re not going to be able to use much if you don’t understand vJASS’s OOP syntax. You can read about it here: JassHelper 0.A.0.0. vJASS is not difficult to comprehend.
In general, no, you can use systems without much knowledge (they are designed to be simple). In this case, however, you’re not going to be able to use much if you don’t understand vJASS’s OOP syntax. You can read about it here: JassHelper 0.A.0.0. vJASS is not difficult to comprehend.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.