• 🏆 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] (system) Missile

I have worked on this missle engine for 2 months now and start the first official beta today.

Please post feedback, critism, pros, cons, bugs, etc.
Thanks all for your patience!

And thanks for downloading.
Preview image by Furby. Thanks a lot!

Edit:
I've updated the testmap and added a second spell.
There is a lot of improvements in version 0.1.7 and I plan to make it even faster and even more modular. I also plan to create an easy to understand API.

Used librarys:


Changed all constants (TRUE, FALSE) to true/false
Changed interval to 0.03125
Fixed object leaks
Performance improvements on MissileMovement: Less object generation
Merged MissileLocHelpers to Missile<type>Target
Removed HomingMissile wrapper
Added second spell (Rain of Fire)
Removed Libraries from Modules



Changed name to Missile.
Completely remade in modules, supports now a lot of optional stuff.
Added SpellHelper, fixed a lot of problems, made the Miranas Arrow example WAY more powerful to show what this system is able to be capable of.


Keywords:
Missile, Projectile, xe, xemissile, xecollider, projectile, collider, awesome, anachron, collide, colliding, vJass
Contents

CustomMissile 0.1.7 (Map)

Reviews
BPower: 11:02, 25th Feb 2016 Reason for re-review: Nowadays the spell section is packed full of missile systems from different authors, therefore a more qualified moderator comment than "this is neat stuff" is required. There is keen...
Level 12
Joined
Sep 4, 2007
Messages
407
I know! xD It's part of the process of learning vJass, thinks tend to descomplicate a lot while using it, as far as i noticed, vJass creates just a bunch of jass functions in WE and connect them in a very clear way =P (and allows me to declare globals when i want xD, among other features)
 
Level 12
Joined
Sep 4, 2007
Messages
407
you gotta work on haze, you disabled it.

also here's an idea. maybe you could apply an option in each arrows obey a physic law that i can't really remember the name right now.... it's makes the arrow resist direction change in it's tragetory.

it's like this. when a object is heavy and is moving too fast it have some trouble changing it's movement's speed. it tends to continue moving to the direction it is moving, unless some bigger energy change it's tragetory. (smaller energies SLIGHTLY change i'ts tragerories.

This would make a homing missile not over the top cuz it would have trouble following target unit's evasive movement's manuevers. it's also very well nice to see it xD

also, when the object reverse or nearby reverse it's tragetory, instead of doing it instantly, it slightly looses it's movement speend until it becomes zeroed, then until it starts accelerating towards the reversed direction. (this does not apply if the missile "collide" because physics action/reaction would just apply an amount of energy equal to the amount the missile already have, if the missile does not have an elastic factor (a thing that i've made with your system, elastic coeficiency), it would just stop)

to make this thing possible you wold have to simulate acceleration (a thing that I already done with your system aswell, and that's really easy to do.)

EDIT: i think it's name is inertia, but i'm not sure. in portuguese the word for it is very close to inertia, so i'm just guessing.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
I think what you're referring to is inertia, or momentum.

Either way, this would greatly affect the mechanics behind how projectiles are assumed to work. A projectile is a relatively linear representation of object motion due to the fact that it has a determined target, and a determined point of impact based on distance and speed. Arc is merely a calculated method of determining height, so it doesn't affect the calculations used for time.

What you're suggesting would probably be better handled by a physics engine, since projectiles don't really follow exact laws of physics and don't work as properly when they are treated like actual physical particles, rather than imitations of an in-game mechanic. In order for inertia to be properly portrayed, the projectile (particle) would need to have a dynamic acceleration towards the target which would be added to it's movement velocity.

To sum it up this idea is best served with a physics engine, because projectiles just don't follow strict enough physics laws to be reproduced as if they did.
 
Level 12
Joined
Sep 4, 2007
Messages
407
I understand. some physics laws might be simulated using anachron's system, be that might be just too much, right? ^^

(i've simulated elastic factor and accelerating, as well as energy dissipation (which is not a law but whatever)

haze still needs fixing though! xD
 
Level 12
Joined
Sep 4, 2007
Messages
407
JASS:
    function DistBetweenUnits takes unit first, unit sec returns real
        local real zFirst = GetLocZ(GetUnitX(first), GetUnitY(first)) + GetUnitFlyHeight(first)
        local real zSec = GetLocZ(GetUnitX(sec), GetUnitY(sec)) + GetUnitFlyHeight(sec)
        local Loc lFirst = Loc.create(GetUnitX(first), GetUnitY(first), zFirst, GetUnitFacing(first))
        local Loc lSec = Loc.create(GetUnitX(sec), GetUnitY(sec), zSec, GetUnitFacing(sec))
        local real dist = 0.
        
        if first == null or sec == null then
            set dist = -1.
        else
            set dist = lFirst.distanceTo(lSec)
        endif
        
        call lFirst.destroy()
        call lSec.destroy()
        return dist
    endfunction

would be best if you made this for widgets? that way you use it for units and desctructables and in your system destructables are importante (since they have their own "onDestTouch".)

just a sugestion. I don't really need this, i can make my own, just saying it is useful.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
There's no reason to bog the system down with more function calls. Internally this can be done without much effort and much more efficiently than this. Also, the only height that you can reference from a destructable is the "occluder height" which isn't of much use when it comes to coordinates (though it is possible that you could determine the "height" of the destructable by using this value).
 
Level 12
Joined
Sep 4, 2007
Messages
407
i know, but we can assume they will always be stuck on the ground. even those destructables that "fly" occupy "collision" when they are placed. So just extrate the LocZ of the location of the destructable and you are done o_O

but anyway, is just a sugestion. I know is easy to make this externally (i can do it myself...) but the purpose of systems is to make hard things simple for less experienced mapmakers.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Yea, that's what I do in mine (that is, the only collision I worry about with destructables is 2-dimensional) but it works fine. The function you stated would not work for widgets because there is no way of determining whether a widget is a unit/destructable/item, the functions must be separated to include all possibilities.
 
Level 12
Joined
Sep 4, 2007
Messages
407
the function i stated is the one anachron uses. just saying that could be improved, that's all. o_O

there's no need to make them separate, you only need the real value of the distance between the widgets. no need to specify the kind of widget

Edit:found a good use for it. in the module-specific i designed (the collision detector) i could use the function for multipurposes instead of making one for each kind of widget.
 
Last edited:
Level 12
Joined
Sep 4, 2007
Messages
407
all destructable's, no matter how high they are, act like they were in the ground o_O

you can "simulate" height by inserting occluderheight exacly the height the destructable is (or 10 times smaller or 100 points smaller, whatever) (I don't do this)

anyway, besides some flying rocks, waht destructable "flies?"
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
lelyanra said:
no need to specify the kind of widget

And how exactly are you planning on determining whether to use GetUnitX(), GetItemX, or GetDestructableX? You would have to be able to differentiate between unit widgets and the others, which you cannot.

Anachron said:
The destructable can be played somewhere above the ground and the collision check will fail.

The way I handle destructable collision is to ignore Z, since it really has no purpose and it can safely be assumed as 0.00. If it is not 0.00, then there's nothing you can really do (and in which case ignoring Z would be the right way to handle it).

lelyanra said:
all destructable's, no matter how high they are, act like they were in the ground o_O

The only function that allows the enumeration of destructables is EnumDestructablesInRect, which enumerates them within a rect. No matter how you plan on enumerating the destructables (for collision) their Z will never be a factor. Even within the enumeration callback there is only the location Z of the destructable, but since the WarCraft III team didn't put any work into destructables it would be pointless to simulate destructable Z where there isn't any.

What I did was just pass any destructable that is enumerated (and within the appropriate circular area) to the user-end function, so that if the user doesn't care about the destructable Z it is ignored, and if they do they can use their own method of determining whether or not the projectile struck a tree.

lelyanra said:
anyway, besides some flying rocks, waht destructable "flies?"

Well destructables only lay on the surface of the terrain (with height offsets, but no real "z") which means that even if they are in the sky they will have a pathing that is applied to the terrain below (or no pathing), no matter how high they are.
 
And how exactly are you planning on determining whether to use GetUnitX() , GetItemX , or GetDestructableX ? You would have to be able to differentiate between unit widgets and the others, which you cannot.
Typecasting ftw.

The way I handle destructable collision is to ignore Z, since it really has no purpose and it can safely be assumed as 0.00. If it is not 0.00, then there's nothing you can really do (and in which case ignoring Z would be the right way to handle it).
Well yes, that is correct.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Typecasting in JASS isn't available anymore at all. The return bug was the only real means of typecasting (unless I am out of tune with it, but I have found no need to typecast).
 
Level 12
Joined
Sep 4, 2007
Messages
407
so, if it loads, there you've found the child type?

JASS:
function testwidget takes widget theWidget returns string
    local unit u
    local item i
    local destructable d     

call SaveWidgetHandle(hash, 0, GetHandleId(theWidget), theWidget)
set u = LoadUnitHandle(hash, 0, GetHandleId(theWidget))
set i = LoadUnitHandle(hash, 0, GetHandleId(theWidget))
set d = LoadUnitHandle(hash, 0, GetHandleId(theWidget))

if u != null then
return "it's an unit!"

if i != null then
return "it's an item!"

if d != null then
return "it's a destructable!"

endfunction
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Oh. I thought it would be neat and clever to be honest. This is kind of slow and bulky.

At first I laughed, actually, because had a gut-feeling that converting the type would be dealt through hashtable loading.
 
Level 12
Joined
Sep 4, 2007
Messages
407
I think i've found a bug: when you setTargetPosZ() the missile should end at some point above the ground (the z you defined). instead, the missile will always hit the ground, just like setTargetPos. you using the same update functions for both?
 
Level 12
Joined
Sep 4, 2007
Messages
407
you don't get it. IF i set the end.z to 75 using .setTargetLocZ, the arrows ends in the ground like if i imputed 0.00.

i think the arrow makes "an zarc of 0.00" and finish it's moviment in the ground.

there's nothing to do with calcules. it's the system. o_O
 
That's wrong. Actually the whole system cares about end and start z:

JASS:
            if .zArc != 0. then
                set new.z = ParabolaZ2(.start.z, .end.z, (.start.z + .end.z) /2 + dist * .zArc, dist, toStart)
            endif

JASS:
        public method setTargetPosZ takes real x, real y, real z returns nothing
            set .end.x = x
            set .end.y = y
            set .end.z = z + .h / 2
So this should not be the fault.

Maybe you did something wrong with your calculations?

(Please note that the end-z is absolute)
 
Level 12
Joined
Sep 4, 2007
Messages
407
test this:
JASS:
scope MagicProjectile initializer init
    private keyword spell
    private struct spellMissile extends CustomMissile
        private spell   spellInst = 0
        private unit source = null
        
        public static method new takes spell ins, unit caster, real spellx, real spelly returns thistype
           local real x = GetUnitX(caster)           
           local real y = GetUnitY(caster)
           local real angle = Atan2(spelly - y, spellx - x)
           local real xloc = x + 1500. * Cos(angle)
           local real yloc = y + 1500. * Sin(angle)
           local thistype this = thistype.create(x, y, GetLocZ(x, y) + 75., angle)
           
           set .source = caster
           set .spellInst = ins
           
               call .setTargetPosZ(xloc, yloc, 75.)
           return this
        endmethod
        
        method onCreate takes nothing returns nothing
            set .sfx         = "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl"
            set .movespeed   = 800.
            set .hitrange    = 32.
            set .decay       = 15.
            set .height      = 75.
            set .hitunits    = true
            set .hitdests    = true
            set .autoface    = true
            set .checkheight = false
            set .hitwalls    = true
        endmethod
        
        method onStart takes nothing returns nothing
            call DestroyEffect(AddSpecialEffectTarget("Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl", .getEffUnit(), "origin")) 
        endmethod
        
        method onLoop takes nothing returns nothing
            set .scale = GetRandomReal(1.,3.)
        endmethod
        
        method onDestTouch takes destructable theDestructable returns nothing
            set .hitunits = false
            set .hitwalls = false
            set .hitdests = false 
            set .alive = false
            call BJDebugMsg("Miss!!!")
        endmethod
        
        method onWallHit takes nothing returns nothing
            set .hitunits = false
            set .hitwalls = false
            set .hitdests = false 
            set .alive = false
            call BJDebugMsg("Miss!!!")
        endmethod
        
        method onTargetReach takes nothing returns nothing
            set .hitunits = false
            set .hitwalls = false
            set .hitdests = false 
            set .alive = false
            call BJDebugMsg("Miss!!!")
        endmethod
        
        method onUnitTouch takes unit theUnit returns nothing
            local real realhigh = GetUnitFlyHeight(.getEffUnit())            
            if GetUnitState(theUnit, UNIT_STATE_LIFE) <= 0 then
                return
            endif
            if theUnit == .source then
                return
            endif
            if realhigh < 100 and realhigh > GetUnitFlyHeight(theUnit) then
                set .hitunits = false
                set .hitwalls = false
                set .hitdests = false
                call BJDebugMsg("Hit!!!")
                call DestroyEffect(AddSpecialEffectTarget("Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl", .getEffUnit(), "origin")) 
                call UnitDamageTarget(.source, theUnit, 100., false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
                set .alive = false
            endif
        endmethod
    endstruct
    
    private struct spell
        private spellMissile missile = 0
        
        public static method create takes unit caster, real x, real y returns thistype
            local thistype this = thistype.allocate()
            
            set .missile = spellMissile.new(this, caster, x, y)
            
            return this
        endmethod
    endstruct
    
    private function cast takes nothing returns boolean
        if GetSpellAbilityId() != 'A00B' then
            return false
        endif
        call spell.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY())
        return false
    endfunction

    private function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i = 0
        
        call TriggerAddCondition(t, Condition(function cast))
        
        loop
            exitwhen i >= bj_MAX_PLAYERS
            
            call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_CAST, null)
            set i = i +1
        endloop
    endfunction
endscope
 
Top