• 🏆 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!
  • ✅ The POLL for Hive's Texturing Contest #33 is OPEN! Vote for the TOP 3 SKINS! 🔗Click here to cast your vote!

[GUI-Friendly] Custom Missile System 1.4.1

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
  • Like
Reactions: ILH
Description

This system provides a way to create flying projectiles or existing units and move them periodically.
It allows you to easily set the properties of the affected units via GUI triggers with minimal custom script usage.
Another nice feature is that a missile can hit the same target multiple times but not multiple times between the given amount of time.
Another reason why I made this system is to have missiles that can collide with other missiles and can be targetable.

Installation Guide

Settings:
  1. Enable JassHelper in the menu JassHelper > Enable JassHelper
  2. Enable vJass if not already enabled in the menu JassHelper > Enable vJass
Imports:
  1. Import the BLACK.tga texture (export from the example map and import in your map)
  2. Import the DUMMY.mdx model (export from the example map and import in your map)
  3. Import the MISSILE_DUMMY.mdx model (export from the example map and import in your map)
  4. Make sure all resources are in the same paths (at the root) as the example map or adjust the paths in the object editor resources
Object Editor:
  1. Copy the DUMMY unit (in Custom Units > Human > Melee > Units)
    You can copy the unit [ctrl][c] in the example map and switch to your map and paste [ctrl][v].
  2. Copy the MISSILE unit (in Custom Units > Human > Melee > Units)
  3. Copy the CMS ABILITY GHOST ability (in Special > Units)
  4. Copy the CMS ABILITY MORPH ability (in Special > Units)
    Set the "Data - Alternate Form Unit" to DUMMY
    Set the "Data - Normal Form Unit" to MISSILE
Trigger Editor:
  1. Copy the "Variables" folder or all the variables that start with "CMS_" and paste them in your map.
  2. Copy the "Required Libraries" folder and paste it in your map.
  3. Copy the "Custom Missile System (CMS)" folder and paste it in your map.
  4. In the CMS Configuration trigger, set the variables to the correct units and abilities from the object editor.
The map should now be ready to use the system.

Usage Guide

Creating a missile is done in 3 steps:
  1. Set "CMS_Starting..." variables.
    These variables define the starting state of the missile.
  2. Invoke "CMS_CreateMissileEx" using a custom script.
    This function creates a missile using the starting variables and returns an integer that references the missile.
  3. Set "CMS_Param_..." variables.
    These variables define the current behavior of the missile.
Here is a description of every CMS_Starting field:
  • CMS_StartingAcceleration (real default 0.00 aka not accelerating)
    Defines how fast the missile should speed up in distance units per second per second.
    I.e. when acceleration is 500, the missile's speed will be 500 more after 1 second and 750 more after 1.5 second. (Applied each tick.)
  • CMS_StartingAngle (real default 0.00 aka facing east)
    Defines in which direction the missile should be going at the start.
    The value is in degrees (0-360).
  • CMS_StartingDefaultValues (boolean default true)
    When true, uses the default values for the "CMS_Param_..." fields on creation.
    Not sure why you would ever want to try to turn that off.
  • CMS_StartingHeight (real default 60.00)
    Defines the height of the missile at creation.
    When the missile should be on the ground, the default value is usually good enough.
    When the missile should be in the air, set this value to what it should be. For example when the creator is a flying unit or like the meteor strike in the example map.
  • CMS_StartingLocation (point default null aka center of map)
    Defines where the missile will be spawned.
    This location automatically gets removed after missile creation, so do not use this location again after the missile is created.
  • CMS_StartingLocust (boolean default true)
    If true, the created missile will be given the locust ability. (Read more on the locust ability at world-editor-tutorials.thehelper.net Abilities - Guide.)
  • CMS_StartingMissile (unit default null)
    When set, this unit is used instead of creating a new missile unit.
    This can be useful for when the missile logic needs to be applied to existing units (for example a knockback effect).
    When set, the CMS_StartingLocust has no effect. The trigger creating the missile can handle that behavior itself.
    This is by design because the default value of true is not preferred in most cases for existing units.
  • CMS_StartingPitch (real default 0.00 aka facing horizontal)
    Defines how high up the missile is aiming at the start.
    The value is in degrees (0-90).
  • CMS_StartingSource (unit mandatory to set)
    Defines the owner unit of the missile.
    This is used to define the owner player of the missile.
    It also gets passed on to the CMS_Param_Source allowing other triggers to know which unit created the missile for example for scoring or dealing damage, etc.
  • CMS_StartingSourceFilter (boolean default true)
    When true, the creator (CMS_StartingSource) of the missile is initially excluded from collision to prevent it from being collided with immediately when the missile is created at the location of the creator.
    The creator will be made available for collision again just like any other unit after a specified amount of time.
  • CMS_StartingSpeed (real default 0.00 aka not moving)
    Defines the speed of the missile in distance units per second.
    Making it similar to the "Combat - Attack # - Projectile Speed" or "Movement - Speed" values for units or "Art - Missile Speed" value for abilities.
    Keep in mind that this is the 3D speed, which means that for missiles that are moving in the Z axis (height), their horizontal speed will be less based on their pitch.
  • CMS_StartingType (integer default -1)
    The type is a number used to invoke triggers with CMS events.
    The value should be a positive number (1 or higher) or -1 for "no type".
    See CMS Events for more information.
Creating the missile.
This is done via a custom script to invoke the function that handles the creation of the missile:
  • Custom script: call CMS_CreateMissileEx()
After the missile has been created, a lot more properties become available, controlling the current state of the missile.
These properties are all arrays and need to be accessed by the index of the missile.
At creation, the index would be whatever variable you use to store the result of the CreateMissile function which has the same value as the CMS_Amount variable.
On collision events, the index of the colliding missile would be CMS_LoopIndex.
On destruction events, the index of the destroyed missile would be CMS_Index.
Here is a description of every CMS_Param field:
  • CMS_Param_Collision_Unit (boolean default true)
    Defines if the missile should have collision detection with units.
  • CMS_Param_Collision_Dest (boolean default false)
    Defines if the missile should have collision detection with destructables.
  • CMS_Param_Collision_Item (boolean default false)
    Defines if the missile should have collision detection with items.
  • CMS_Param_Collision_Cooldown (real default 1.00)
    When a missile collides with a target, and the missile is not destroyed in the process (for example a piercing missile).
    The missile would probably collide with the same unit in the next tick... and probably as well in the tick after that, depending on the speed and collision size.
    The collision cooldown defines how long (in seconds) collided units should not be triggering collision events again after a collision event.
    This allows the units to be hit by the same missile multiple times.
    See the "Fury Fire" ability in the test map as example use case.
  • CMS_Param_Collision_Type (integer default NONE)
    Defines which collision algorithm to use.
    Options are:
    • CMS_COLLISIONTYPE_NONE (collision is disabled)
    • CMS_COLLISIONTYPE_SIMPLE
      Uses a very fast collision detection system by using grid aligned distance checks.
      This does mean that the collision size varies depending on the angle of the missile.
    • CMS_COLLISIONTYPE_CIRCLE
      Uses a relatively fast collision detection system by using the absolute distance in the horizontal plane.
      This should be the default for most missile usages with collision.
    • CMS_COLLISIONTYPE_RECTANGLE
      Uses a collision detection system that creates a rectangle from the previous position of the missile to the current position of the missile, fixing the potential gap issue of the above two systems.
    • CMS_COLLISIONTYPE_POLYGON
      Uses a system with custom polygons to create virtually any shape for the collision detection.
Assume a missile is moving along a path, here we are showing 3 positions where that missile would be in 3 ticks of the system:
View attachment 482081
COLLISIONTYPE_SIMPLE will use a grid aligned system and use simple X and Y distances between the game widgets and the missile itself.
This means that when the missile is moving directly east, west, north or south, the collision detection is correct.
But when it is moving at an angle, the collision detection becomes wider, making it sometimes hit units that are further away than the missile's collision width.
This type is the most efficient, but least accurate.
View attachment 482082
COLLISIONTYPE_CIRCLE will use a horizontal distance check.
This is less performant than the SIMPLE type, but it is more accurate and suffices in most scenarios.
View attachment 482083
However, when the missile is moving very fast or when the collision size is very small, both SIMPLE and CIRCLE will leave gaps in between the iterations of the missile's movement, allowing units to be not collided with even though they are directly in the path of the missile.
View attachment 482084
COLLISIONTYPE_RECTANGLE uses an angled rectangle with as width, the collision with of the missile and as length the distance between the positions in each tick and the collision with as start and end margin.
This is the most accurate collision detection, but more CPU intensive than the SIMPLE or CIRCLE collision types.
This should only be used when the other two are insuffient to apply collision reliably.
View attachment 482085
COLLISIONTYPE_POLYGON uses a system where you can define your own collision detection shape.
This is by far the worst for performance, but allows for full customization.
For example, using a heart shape to make units directly in the path of the missile hit earlier than units a bit further to the side.
This should only make any significant different for very large area collisions
View attachment 482086
  • CMS_Param_Collision_Width (real default 50.00)
    Defines the size of the collision.
    (Has no effect on polygon collision type.)
  • CMS_Param_Collision_Width_Inc (real default 0.00)
    Defines how much the size of the collision should increase per tick.
    (Has no effect on polygon collision type.)
  • CMS_Param_Collision_Height (real default 0.00)
    Defines the height of the collision detection.
    When the value is 0.00, the collision detection will check for height.
    When the value is above 0.00, the collision detection will check if the height difference between the missile and collided unit is less than the Collision_Height.
    This means that COLLISIONTYPE_CIRCLE for example is actually a cylinder, not a sphere.
    If the height is 0.00, the cylinder has practically infinite height, if the height is a different value, the cylinder has that specific height.
    Polygon collision type does have the same rules for height as other collision types.
  • CMS_Param_Collision_Height_Inc (real default 0.00)
    Defines how much the height of the collision should increase per tick.
  • CMS_Param_Distance (real default 0.00)
    Tells how far the missile has been travelling.
    This should only be read by your triggers, pretty much never set.
    Unless you want to reset the maximum distance check for example.
  • CMS_Param_Duration (real default 0.00)
    Tells how long the missile has been travelling.
    This should only be read by your triggers, pretty much never set.
    Unless you want to reset the maximum duration check for example.
  • CMS_Param_Effect (effect default null)
    This is a variable for an attached special effect.
    The special effect in this value is automatically destroyed and cleaned up when the missile is destroyed.
  • CMS_Param_Gravity (real default 0.00)
    Defines the amount of gravity that should be applied per tick.
    Gravity will increase the velocity in the direction of the ground by this amount.
  • CMS_Param_Index (integer default <auto-generated>)
    Defines a unique index for each missile.
    This can be used with custom arrays to define additional fields per missile, such as additional special effects or linked missiles.
    Keep in mind that this value can go up to very high numbers.
    This value is auto generated by the system.
  • CMS_Param_IsAUnit (boolean default false)
    Defines if the missile is a unit.
    When true, the system will not remove the unit when the missile logic ends.
    This should be true for applying knockbacks or dashes for example.
  • CMS_Param_IsDestroyed (boolean default false)
    Defines if the missile is destroyed.
    Should be set to true in a trigger with a collision event if the missile should be destroyed after colliding with something.
  • CMS_Param_IsFlying (boolean default false)
    Defines if the missile is flying.
    Flying missiles are allowed to update the pitch during re-aiming of homing missiles.
  • CMS_Param_IsHoming (boolean default false)
    Defines if the missile is homing into a specific target.
    When true, it will update the angle and pitch (when flying) to try to aim more towards the target.
  • CMS_Param_MaxDistance (real default 0.00)
    Defines the maximum distance that the missile is allowed to travel.
    When this amount is reached, the missile is automatically destroyed.
    When the value is 0.00, there is no maximum distance.
  • CMS_Param_MaxDuration (real default 0.00)
    Defines the maximum time that the missile is allowed to travel in seconds.
    When this duration is reached, the missile is automatically destroyed.
    When the value is 0.00, there is no maximum duration.
  • CMS_Param_Missile (unit default <auto-set>)
    References the missile unit itself.
    This value is auto assigned from the CMS_StartingMissile or from the newly created missile.
  • CMS_Param_Source (unit default <auto-set>)
    References the source unit of the missile.
    This value is auto assigned from the CMS_StartingSource.
  • CMS_Param_Subtype (integer default 0)
    Defines a sub type that you can use in your triggers.
  • CMS_Param_Target_Type (integer default NONE)
    Defines what kind of targeting system the missile is using.
    Only has effect when IsHoming is true.
    Options are:
    • CMS_TARGETTYPE_NONE (collision is disabled)
    • CMS_TARGETTYPE_UNIT
      The missile is targeting a unit, aiming closer towards it each tick.
    • CMS_TARGETTYPE_LOCATION
      The missile is targeting the ground at a specific location, aiming closer towards it each tick.
  • CMS_Param_Target_Location (point default null)
    Defines the target location of the missile.
    Only has effect when IsHoming is true.
    (This location is automatically removed when the missile is destroyed.)
  • CMS_Param_Target_Unit (unit default null)
    Defines the target unit of the missile.
    Only has effect when IsHoming is true.
  • CMS_Param_TurnRate (real default 0.00)
    Defines how fast the missile is rotating to aim at the target in degrees per tick.
    180+ would be instant rotation.
    5.4 would allow it to only do a full reverse after one second.
    When the value is 0.00, there is no rotation and IsHoming has no effect.
  • CMS_Param_Type (integer default <auto-set>)
    Defines the type of missile, which is used in trigger events.
    This value is auto assigned from the CMS_StartingType.
  • CMS_Param_WalkingHeight (real default 0.00)
    Defines the walking height of a missile.
    At this height, a non-flying missile will retain it's height from the ground instead of falling down due to gravity.
    Flying units below this height will be destroyed.
  • Firebolt Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Firebolt (2)
    • Actions
      • Set VariableSet TempLocation = (Position of (Target unit of ability being cast))
      • Set VariableSet TempUnit = (Triggering unit)
      • -------- - --------
      • Set VariableSet CMS_StartingLocation = (Position of TempUnit)
      • Set VariableSet CMS_StartingAngle = (Angle from CMS_StartingLocation to TempLocation)
      • Set VariableSet CMS_StartingHeight = 50.00
      • Set VariableSet CMS_StartingSource = TempUnit
      • Set VariableSet CMS_StartingSpeed = 700.00
      • Set VariableSet CMS_StartingAcceleration = 200.00
      • Set VariableSet CMS_StartingType = 1
      • Custom script: set udg_TempInteger = CMS_CreateMissileEx()
      • -------- - --------
      • Set VariableSet CMS_Param_WalkingHeight[TempInteger] = 55.00
      • Set VariableSet CMS_Param_Gravity[TempInteger] = (-45.00 x 0.03)
      • Set VariableSet CMS_Param_MaxDistance[TempInteger] = 1700.00
      • Set VariableSet CMS_Param_Collision_Type[TempInteger] = CMS_COLLISIONTYPE_CIRCLE
      • Set VariableSet CMS_Param_Collision_Width[TempInteger] = 20.00
      • Set VariableSet CMS_Param_Collision_Height[TempInteger] = 75.00
      • Special Effect - Create a special effect attached to the origin of CMS_Param_Missile[TempInteger] using Abilities\Weapons\FireBallMissile\FireBallMissile.mdl
      • Set VariableSet CMS_Param_Effect[TempInteger] = (Last created special effect)
      • -------- - --------
      • Custom script: call RemoveLocation(udg_TempLocation)
The system knows the following events:
  • on Created (invoked when a new missile is created)
  • on Destroyed (invoked when a missile is destroyed)
  • on Tick (invoked for each missile on every tick)
  • on Collided with Destructible (invoked when a missile collides with a destructible)
  • on Collided with Item (invoked when a missile collides with an item)
  • on Collided with Terrain (invoked when a missile collides with the terrain)
  • on Collided with Unit (invoked when a missile collides with a unit)
To create a trigger with a missile event, add an event like one of the below:
  • Events
    • Game - CMS_Event_Missile_Created becomes Equal to 0.00
    • Game - CMS_Event_Missile_Destroyed becomes Equal to 0.00
    • Game - CMS_Event_Missile_Tick becomes Equal to 0.00
    • Game - CMS_Event_Missile_Collided_D becomes Equal to 0.00
    • Game - CMS_Event_Missile_Collided_I becomes Equal to 0.00
    • Game - CMS_Event_Missile_Collided_T becomes Equal to 0.00
    • Game - CMS_Event_Missile_Collided_U becomes Equal to 0.00
To create a trigger with a missile event specifically for a certain missile type, set the value in the event (i.e. the 0.00 in the above example) to the missile type number.
Triggers with "... becomes Equal to 0.00" will trigger for every missile.
Triggers with "... becomes Equal to 1.00" will trigger only for missiles with type 1.
Triggers with "... becomes Equal to 2.00" will trigger only for missiles with type 2.
etc.

In each event, the missile parameters are available via index.
In Created events, the index is CMS_Amount.
In Destroyed events, the index is CMS_Index.
In Tick events and Collided events, the index is CMS_LoopIndex.

In Collided events, the following variables are also available:
  • CMS_Collided_Dest (for collision with destructibles)
  • CMS_Collided_Item (for collision with items)
  • CMS_Collided_Unit (for collision with units)
When colliding with a target, the target will probably be collided with on next tick and after that as well because of the tiny ticks of the system.
So, when colliding, the collision trigger should probably do one of two things:
  1. Set CMS_Param_IsDestroyed[CMS_LoopIndex] to true
    This will destroy the missile after the collision, preventing it from colliding with anything.
  2. Set CMS_ValidTarget to true
    This will tell the system that the collided target is a valid target and will apply the collision cooldown on it, preventing it from being collided with again for a specified amount of time.
Keep in mind that any unit can be collided with (except locust units).
So, for most collision event triggers, you probably want to verify that:
  1. The collided unit is not a structure (unless the missile should hit buildings, like shockwave)
  2. The collided unit is either an enemy or ally (enemy for negative effects and ally for positive effects)
  3. The collided unit is not dead (unless the missile should hit dead units, like a resurrection wave)
For example:
  • If - Conditions
    • (CMS_Collided_Unit is A structure) Equal to False
    • (CMS_Collided_Unit belongs to an enemy of (Owner of CMS_Param_Source[CMS_LoopIndex]).) Equal to True
    • (CMS_Collided_Unit is dead) Equal to False
When an ability is used, creating a missile that flies towards the target, the user can still level up the ability that was used before the missile hits the target.
While this is an edge case and most users won't really care if the missile will deal the damage of the level it had when it was created or when it hit the target, the system does provide a way to attach extra information to the missile by providing a unique index to each missile in use.
This index can then be used to access data in either an array or hashtable to attach more information.
Take for example the triggers for Jaina's Blizzard in the example map.
In the "Blizzard Cast" trigger, the damage is calculated and set in the "Blizzard_Damage" variable using the index of the last created missile:
  • -------- attach damage value to the missile --------
  • Set VariableSet Blizzard_Damage[CMS_Param_Index[CMS_Amount]] = damage
In the "Blizzard Collide" trigger, the damage is read from that same variable using the index of the colliding missile:
  • -------- read the damage again as it was set on cast --------
  • Set VariableSet damage = Blizzard_Damage[CMS_Param_Index[CMS_LoopIndex]]
This way, the damage can be calculated on cast rather than on hit and would be more accurate.
This matters more if for example the damage would increase based on if the user has a buff or not.
Players would expect that the missile would receive that bonus damage if the user had the buff when the missile was created, not when it landed.
Especially if that should also remove the buff on use (on creation of the missile).
In the example map, there is a Paladin with 4 abilities using the missile system:
  1. Magic Missile:
    Launches a missile at the target unit, following it.
  2. Firebolt:
    Launches a missile at the target unit.
    If the missile hits, it knocks back the hit unit.
    If the unit being knocked back hits a cliff, it takes damage.
    Firebolt cannot hit flying units (unless terrain allows it to).
  3. Fury Fire
    Creates a missile that spins in a circle, hitting units in it's path.
    Fury Fire cannot hit flying units (unless terrain allows it to).
  4. Meteor Strike
    Calls down a giant meteor from the sky at the target location.
    When it lands, kills all (enemy and friendly) units in the target area.
There is also Jaina with 2 abilities using the missile system:
  1. Blizzard:
    Calls down a wave of freezing ice shards that damage units in a target area.
  2. Frost Nova:
    Creates an ice explosion, sending ice shards in every direction.
    (Which looks a lot like Gandalf's fireworks in Lord of the Rings: Gandalf's Fireworks )
There is also a Tinker with 2 abilities using the missile system:
  1. Rocket Barrage
    Launches 8 rockets at the target area. Damaging allies and enemy units.
  2. Seeker Missile
    Launches a rockets in the target direction. The rocket will search for enemies to engage on and will explode on impact.
The enemy units wont attack. They just act as dummy targets for target practice.

There is a chat command /test that will run all test scenarios.
There is a chat command /showcase that will run all abilities mentioned above.
The test and showcase commands also accept a filter for example /test collision which only runs the tests with "collision" in their name.
Tests will probably fail if the user is starting tests while other tests are still running.
Tests will probably fail if the user is creating other missiles while the tests run.

There are two videos showing the above scenarios:

Changelog:
- 1.4.1 - Added examples and fixed some bugs.
- 1.4 - Added additional features (detection, optimized events, acceleration).
- 1.3 - Redesigned data and calculation structure.
- 1.2 - Rewritten the system.
- 1.1 - Fixed Collision bugs.
- 1.0 - Official releas on te Hive

Feel free to post any feedback or suggestions.

Keywords:
Wietlol, CMS, Missile, Projectile, Knockback, Knockup, Dash, Jump, Leap, System, Firebolt, Advanced, OP, GUI.
Contents

Custom Missile System (Map)

Reviews
29.02.2016 BPower: Very well written. Very read-able as expected. The homing missile behaviour doesn't work for me in the uploaded test map. I think it has to to with radians to degree conversion. Can you check that? 21:56, 16th Jun 2015...

Moderator

M

Moderator

29.02.2016
BPower: Very well written. Very read-able as expected.
The homing missile behaviour doesn't work for me in the uploaded test map.
I think it has to to with radians to degree conversion.
Can you check that?

21:56, 16th Jun 2015
BPower: Missile systems are very complex and hard to review.
Here is a start for a TO-DO-LIST with mandatory
issues to be fixed or discussed.
I will continue my review upon your reaction to my review.

PS: I'm a fan of missile systems

14:27, 27th Apr 2015
IcemanBo: http://www.hiveworkshop.com/forums/...e-system-1-1-a-263257/index2.html#post2679209
 
Level 24
Joined
Aug 1, 2013
Messages
4,658
Does this provide an option for a missile to home in on target? Also does it give option to specify either location target or unit and if unit, home onto unit. For Dota players, you will know Spectre's Spectral Dagger is configured like what I just said.

Yes it does:
By default the missile doesn't chase a target.
You can set CMS_Param_Target_Type to either CMS_TT_Location or CMS_TT_Unit and specify the CMS_Param_Target_Location or CMS_Param_Target_Unit to make the target.
(Homing towards a location can help in making horizontal arcing or using it to change the location during the flight... or make a missile circle around a location or whatever you want.

You also have to set the turn rate to a value.
The turn rate is the turn speed of the missile.
Some games use the turn as instant (the missile would turn instantly to the target) which is a turn rate of 180 in this system.
A turn rate of 0 doesn't turn the missile at all.

Same here, as soon as I have time and will... Even though it doesn't take that much time :)
 
Level 13
Joined
Aug 19, 2014
Messages
1,111
Now that you mention what this system does, I have some spells that I wanted create. Looks good to use, I'll use this system as soon as its approved.
 
  • Please add some descriptive comments in config.
    As user I want to know why I need things like "Stun_Buff" in a missle system.
  • Sometimes it's worthy to write more letters to gain description for names.
    Just for example in this code I would not use something like CMS_TT_Unit. "TT" does not say anything to the reader.
    But most other names seems good.
    JASS:
    /*
    - CMS_Param_Force_Destroy           - boolean
    This boolean tells if the destruction of a missile must be done at all cost.
    It can also be used as an additional Destroy variable.
    ^What's the difference to using the destroy trigger? It has influence on it?
    JASS:
    //      - CMS_Player                        - player
    //          This variable is used as a tempvariable when deseleting missiles.
    //          
    //      - CMS_OldLocation                   - point (location in JASS)
    //          This variable is used as a tempvariable when checking for collision.
    //          
    //      - CMS_NewLocation                   - point (location in JASS)
    //          This variable is used as a tempvariable when checking for collision.
    ^In case user doesn't need to use them, temp variables should not be explained.
  • JASS:
    //      - CMS_Param_Lose_Control            - boolean
    //          This tells if the missile can still move and use abilities etc while it is flying.
    ^I'm not sure a real missle should be able walk around and cast abilities.
  • The maybe only function you really need a location is the GetLocationZ function.
    You can use one global location and work with the MoveLocation native instead of creating/destroying local locations.
  • Should udg_CMS_Param_Missile really be changeable? What happens if user changes the udg_CMS_Param_Missile and then Get/Save trigger gets fired? In my mind it would bug.
  • function CMS_Deselect_Missiles should not run when there is no missle.
    Also it could use global temporary unit group instead of creating/destroying local groups.
  • For de-select missles. Why you check for UnitTypeId, but not for unit itself?
    Can't it happen, that it has the same id, but is no missle?
  • "If cooridnates are in map bounds" could be an extra function for some readability.
    And the Min/Max coordinate values could be stored into variables once at game start.
  • In JASS you could keep RAD actually and only work with DEG when you need it for unit's facing angle.

In the variable explainations, the triggers are explained like "this is the function...".
No, it's not. That's the trigger, not the function.
 
Level 24
Joined
Aug 1, 2013
Messages
4,658
Update 1.2

I just released version 1.2

I think I now can actually say that it looks like a missile system :D

I am not quite sure if everything still works after the translation from a lot of variables into temp arrays but I think that everything works as intentioned.

Degrees are for users.
They rather use degrees when changing pitch or whatever... and I also find it easier to use.

--------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------

Aaaaand update 1.3:

This is the final concept so no more functionalities unless they are really a must.
I changed the data structure and moved the calculations into functions that store the last result in a variable.
The power of this system is therefor (and because of locust) increased to 298% (from 420 missiles to 1250 missiles).
Another change is that missile's acceleration is removed.
All variables are now only in array form.
Added explaination of all variables.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
First and foremost: GUI

You tagged the systems as GUI, but it is not GUI!

A GUI resource shouldn't use any vJass only features. I know your core argument
is that it is GUI friendly ( somehow ), but putting everything into structs, while having wrapper
functions is also "GUI friendly".

I expect from GUI submissions, that they are coded in GUI or custom script.
JASS coded in plain JASS

For me this is a normal vJass submission and has to be moderated like such.


Do not label your functions with a BJ suffix. BJ refers to the blizzard.j

Let's begin:
  • Library basicFunctions --> library BasicFunctions, better would be a name related to Missile like MissileFunctions.
    Why not include them into the core library in the first place. Another option would be an external requirement ( remember you are using vJass )
    .
  • Encapsulation: use the private keyword for functions and variables not ment to be accessed ( you are using vJass )
    This ensures users do not accidently break your code.
    .
  • GetIndex does not appear to be so effective, as it is O(n), is there a distinct reason when this function should be used?
    .
  • Your variables do not follow the JPAG at all ( you are using vJass )
    .
  • I have to check it again, but iirc KillUnit is not a good way of removing a dummy.
    The locust effect vanishes and the unit gets caught by GroupEnumUnitsInRange ( I will check this )
    .
  • You do not maintain the fly height when flying over canions.
    This is required, once you want to take z collision into account.
    Otherwise it is just eyecandy.
    .
  • call SaveTimerHandle(udg_CMS_HASHTABLE, collidedId, missileId, t) this handle is never removed properly.
    .
  • Use just one global rect handle, instead of creating one per missile per iteration.
    .
  • CrowForm is added in any case on missile creation. Exclude it from the if then conditions and just do it once.
    .
  • call BJDebugMsg("pitch = " + R2S(CMS_GetPitchBJ(udg_CMS_Amount)))
    call BJDebugMsg("V_Z = " + R2S(udg_CMS_Param_V_Z[udg_CMS_Amount]))
    should not be there in missile create.

There is more to discuss, but I stop here for now.

Set to Need Fix
 
Last edited:
Level 24
Joined
Aug 1, 2013
Messages
4,658
First and foremost: GUI

You tagged the systems as GUI, but it is not GUI!

A GUI resource shouldn't use any vJass only features. I know your core argument
is that it is GUI friendly ( somehow ), but putting everything into structs, while having wrapper
functions is also "GUI friendly".

I expect from GUI submissions, that they are coded in GUI or custom script.
JASS coded in plain JASS

For me this is a normal vJass submission and has to be moderated like such.
I'll call it vJASS then.

Do not label your functions with a BJ suffix. BJ refers to the blizzard.j
That answers a billion of my questions :D

Library basicFunctions --> library BasicFunctions, better would be a name related to Missile like MissileFunctions.
Why not include them into the core library in the first place. Another option would be an external requirement ( remember you are using vJass )
Those functions are usefull for anything and not only for this specific system.
They could even be part of Blizzard.j if that would be able to do.
However... I just place all functions that are like that inside a very basic library.

Encapsulation: use the private keyword for functions and variables not ment to be accessed ( you are using vJass )
This ensures users do not accidently break your code.
Still discovering vJASS :D

GetIndex does not appear to be so effective, as it is O(n), is there a distinct reason when this function should be used?
I think about replacing it with a linked list using the unit id by a unit indexer.
About the reason why to use it... you can get the index of the unit from the list... basically to use the destroy function or get the data of the arrays.

Your variables do not follow the JPAG at all ( you are using vJass )
JPAG?

I have to check it again, but iirc KillUnit is not a good way of removing a dummy.
The locust effect vanishes and the unit gets caught by GroupEnumUnitsInRange ( I will check this )
Well... the problem was that the special effect for example didnt play its death animation when the unit is removed.
Therefor, I thought to use KillUnit() instead of RemoveUnit() and that solved at least that problem.
Didn't think about other things that might occur.

You do not maintain the fly height when flying over canions.
This is required, once you want to take z collision into account.
Otherwise it is just eyecandy.
- (GetCoordinateZ(x2, y2) - GetCoordinateZ(x1, y1)
Really?

call SaveTimerHandle(udg_CMS_HASHTABLE, collidedId, missileId, t) this handle is never removed properly.
How do I not?
I null t, I remove the saved handle in that place, I never overwrite the handle...
What goes wrong?

Use just one global rect handle, instead of creating one per missile per iteration.
How can I change the size of an existing rect?

CrowForm is added in any case on missile creation. Exclude it from the if then conditions and just do it once.
Dunno if it gives problems with the order in which the missile setup is then but as far as I see, it should work.

call BJDebugMsg("pitch = " + R2S(CMS_GetPitchBJ(udg_CMS_Amount)))
call BJDebugMsg("V_Z = " + R2S(udg_CMS_Param_V_Z[udg_CMS_Amount]))
should not be there in missile create.
True.
 
Level 6
Joined
Feb 16, 2014
Messages
193
Just a question, can this missile system allow for multiple shooting for a single unit?
like, a unit can shoot 5 arrows at the same time.
 
Level 6
Joined
Feb 16, 2014
Messages
193
Wait, I have another question, if I have about 10 units firing about 20 projectiles every second would the game lag ? or crash ?
 
Level 24
Joined
Aug 1, 2013
Messages
4,658
I just released version 1.4

I fixed the issues based on BPower's post and added a few requested features such as acceleration, rectangular collision detection (for hyper speed missiles) and optimized the collision cooldown.

Wait, I have another question, if I have about 10 units firing about 20 projectiles every second would the game lag ? or crash ?
Sorry for not answering this question earlier :/
It will definately lagg... because you didn't specify the duration of how long the missiles will remain.
This means that after 5 minutes, there will be 60,000 missiles which is such a huge amount that nothing in your game works correctly any more.

But the game will not necessarily lagg when you have 10 units firing 20 projectiles every second if the missiles last for 2 seconds (which is pretty long for a regular missile).
The creation of missiles is so efficient that you can create more than 100 missiles and not even see any spike.
There can be at max 600-1000 missiles in the map (could be lower if you have some other stuff as well) but you can increase that by setting "udg_CMS_INTERVAL" to a higher value like "0.04" or "0.05" in the CMS_System trigger in the global block.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I went through the entire code.

Great coding and overall a very useful system.

1.) I don't like the way you allocate instances. ( index + occupied. )
Warcraft initializes variables to a default value ( null, "", 0, 0. and false ).
For me it would be logical if only arrays which are actually used get initialized.
Example: array_string[100] = "hi" would initialize 0 - 99 with "" and 100 with "hi".
It may be different for wc3, I can't tell you.

Arguments which support my assumption are this and this post.

However which was tested, is that if you raised the array size for variables generated in the GUI editor, the amount of memory space in kb also raises.

I would go with stacking, like the default vJass struct creator does.


2.) I really don't like "BasicFunctions". There are very useful functions, particularly for algebra and geometry, but there is sooooo much crap inside.
Nobody needs backwards handle to integer conversion. It just eats up memory space.
I don't want to say it's not useful for you, how could I say that, but it's surely useless
for the rest of the wc3 modding community.

It's probably the best missile system submitted in the spell section ( I don't know all of them ), but I'll not approve it, simply because of the basic function library.
Maybe one of the other moderators thinks different.

3.)
There can be at max 600-1000 missiles in the map
like the fuel consumption information of the new Merdeces S500. Pure overestimation. I say it's 350 - 450 in a realistic test.
 
Last edited:
Level 24
Joined
Aug 1, 2013
Messages
4,658
1, I still have to actually make it...

2, Yea, it is pretty much an overkill.
But every time when I use only a part of it, people ask me to upload the entire thing.

3, Ofcourse, there is a difference between some missiles.
If you have an arrow rain of 1000 arrows, you dont need all of them with collision detection... only one with the area of all.
It also depends on how much other things you have running, so for that, I lowered my "estimation" already.

Id say that you wont reach 350-450 without trying, but it is doable.
 
Level 7
Joined
Aug 11, 2010
Messages
269
I can't get homing missiles to work.

Here is the trigger that I've been fiddling with for over an hour now trying to get it to work; it just doesn't want to work...

  • HELR Aimed Shot Effect
    • Events
    • Unit - A unit Starts the effect of an ability
    • Conditions
    • (Ability being cast) Equal to [Hero - Elven Ranger] - Aimed Shot
    • Actions
    • Set TempPoint = (Position of (Target unit of ability being cast))
    • Set TempUnit = (Triggering unit)
    • -------- - --------
    • Set CMS_StartingLocation = (Position of TempUnit)
    • Set CMS_StartingAngle = (Angle from CMS_StartingLocation to TempPoint)
    • Set CMS_StartingHeight = 32.00
    • Set CMS_StartingSource = TempUnit
    • Set CMS_StartingSpeed = 12.00
    • Set CMS_StartingAcceleration = 200.00
    • Set CMS_StartingType = 1
    • Custom script: set udg_TempInteger = CMS_CreateMissileEx()
    • -------- - --------
    • Set CMS_Param_Gravity[TempInteger] = (-2.00 x 0.03)
    • Set CMS_Param_IsFlying[TempInteger] = True
    • Set CMS_Param_IsHoming[TempInteger] = True
    • Set CMS_Param_TurnRate[TempInteger] = 180.00
    • Set CMS_Param_Target_Type[TempInteger] = CMS_TARGETTYPE_UNIT
    • Set CMS_Param_Target_Unit[TempInteger] = (Target unit of ability being cast)
    • Set CMS_Param_Collision_Type[TempInteger] = CMS_COLLISIONTYPE_CIRCLE
    • Set CMS_Param_Collision_Width[TempInteger] = 100.00
    • Special Effect - Create a special effect attached to the origin of CMS_Param_Missile[TempInteger] using Abilities\Weapons\MoonPriestessMissile\MoonPriestessMissile.mdl
    • Set CMS_Param_Effect[TempInteger] = (Last created special effect)
    • -------- - --------
    • Custom script: call RemoveLocation(udg_TempPoint)
All it does is rotate 180 degrees and flies backwards. It seems to follow the unit but reversed of how it's supposed to. It's really irritating! It seems like no matter what value I change it won't do anything.
 
Level 7
Joined
Aug 11, 2010
Messages
269
Why on earth is this in the simple/substandard section of the hive? I still use the missile system. It's the best and easiest to customize missile system out there, with a maker that regularly replies to questions and concerns? I'm actually kind of disgusted that this isn't listed with the other spells...
 
Level 14
Joined
Jul 1, 2008
Messages
1,314
Hey Wietlol,

I tested your missile system, because the feature list seemed to be amazing.

Then I tested the map, but the firebolt cannot fly up to the gryphon rider. Also, it bounces on the ground, and I could not find a boolean to check, whether I want to to explode on ground or not.
How could I make that missile really follow a 3d movement, e.g. hit the gryphon?

Can you give more examples in the map for usage of your system?
 
Level 24
Joined
Aug 1, 2013
Messages
4,658
The bounce shouldnt happen at all.
It would be an interesting idea, but I didnt implement it in the system.

The missile should fly up to flying units as long as the "isFlying" boolean is set to true.
To make it follow the gryphons, the "isHoming" boolean should be true as well.
If these are set, then it will attempt to fly up to and follow flying units.
 
Level 14
Joined
Jul 1, 2008
Messages
1,314
thanks for your answer, but the bouncing happens in the test map, you provided. just shoot the firebolt from that platform to see what I meant.

If I set IsHoming and IsFlying both to true in the map, you uploaded, then the firebolt seems to explode right on the caster. It is strange, because you have set
  • (CMS_Collided_Unit belongs to an enemy of (Owner of CMS_Param_Source[CMS_LoopIndex])) Equal to (==) True
inn your test map.
I did not modify anything else, please see for yourself.
 
Top