Projectile
Version 0.2.6 (Beta)
Version 0.2.6 (Beta)
Requirements:
Optional:
Description:
Projectile is an advanced projectile movement system that can be utilised in maps to produce custom projectile spells and systems. It has a simple API making it easier to use, while giving users incredible control over projectiles. It allows access to functions that not many other projectile systems have, such as a [ljass]GroupEnumProjectilesInRange()[/ljass] function and provides interface events for a variety of different situations.
Projectile is perfect for small maps that want complete control over projectile movement, including normal wc3 ranged attacks, which are possible to simulate using this system (as shown in the test map). It is also a great system for larger maps that want a more powerful projectile system that allows projectile on projectile collision as well as complex projectile based spells, such as slow time (also shown in the test map).
Projectile is perfect for small maps that want complete control over projectile movement, including normal wc3 ranged attacks, which are possible to simulate using this system (as shown in the test map). It is also a great system for larger maps that want a more powerful projectile system that allows projectile on projectile collision as well as complex projectile based spells, such as slow time (also shown in the test map).
Documentation:
JASS:
.
//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\
//
// _____ _ _ _ _
// | __ \ (_) | | (_) |
// | |__) | __ ___ _ ___ ___| |_ _| | ___
// | ___/ '__/ _ \| |/ _ \/ __| __| | |/ _ \
// | | | | | (_) | | __/ (__| |_| | | __/
// |_| |_| \___/| |\___|\___|\__|_|_|\___|
// _/ |
// |__/
// By Kenny v0.2.6 (Beta)
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\
//
// What is Projectile?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • Projectile is an advanced projectile system that enables users to create and manipulate projectiles in game for
// use in spells and various map-wide systems.
// • Projectile as a system provides users with numerous basic and advanced methods in which they are able to completely
// change almost any aspect of a projectile.
// • Projectile is a tool that enables even those with little knowledge of vJass to create and develop extremely
// powerful gameplay mechanics that are usually unseen in the WC3 modding community.
// • The system itself has an easy and intuitive interface that is quick to learn and very flexible to a users needs, as
// many aspects of the projectile can be left alone or manipulated without consequences. Amongst many other things,
// this system provides:
// - Methods for creating, manipulating and destroying projectiles.
// - An extensive API that encompasses almost all aspects one would want for projectiles.
// - Projectile recycling and preloading.
// - Interface events for unit, destructable and ground collision events.
// - Interface events for projectile launching, destruction, iteration and life expiration.
// - Projectile grouping functions which closely follow the native WC3 group API.
// - Optional modules that allow for additional functionality, such as terrain bouncing and projectile shadows.
// - Built in BoundSentinel that destroys projectiles made by this system when they reach the boundaries.
// • As stated above, Projectile also has an internal boundary detection system, therefore users can delete
// BoundSentinel or any other similar systems if they use this.
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\
//
// Thanks and credits:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • To Jesus4Lyf for AIDS and T32.
// • To Anitarf for Vector and xemissile.
// • To Vexorian for BoundSentinel and JassHelper.
// • To Weep for discussing collision detection.
// • To BlackRose for ideas, testing and dicussions.
// • To Quraji for HandleGroups which gave me the idea for projgroups.
// • To Nestharus for Recycle and Rising_Dusk for GroupUtils.
// • To Berb (TheKid) for his help with projectile groups and the time scale idea.
// • To anyone else who may have helped me with this system (sorry if I forgot you).
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\
//
// How to implement:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • Simply create a new trigger object called Projectile, go to: 'Edit -> Convert to Custom Text', and replace
// everything that is there with this script. Or just copy this trigger object over.
// • Make sure you implement all the required systems for this system. That includes: AIDS, T32 and Linked List
// Module.
// • This system also optionally uses Recycle or GroupUtils, so if you want to implement one of them, you can. They just
// help recycle groups in the system.
// • There are two optional modules available to users if they wish for extra functionality. These modules are
// the ProjShadows and ProjBounces modules.
// • Next you have to make sure that you have the dummy.mdx model in your map before you continue importing this
// system. The dummy model can be found in the import section of this test map.
// • Now you must copy the dummy unit that is needed for the projectiles from the Object Editor. The dummy can be
// found in the units tab of the Object Editor, under 'Custom Units -> Neutral Passive'. It is labelled 'Proj Dummy'.
// • After you have done all that, make sure the raw code for the dummy unit in your map is the same as the raw code
// in the configurables section of this system. Once that is done, save the map and you are ready to go.
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\
//
// Projectile API:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • This is a quick over-view of all the methods, functions and members available to use with this system.
//
// Projectile methods:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • These methods are the main methods used by the system to create, manipulate and destroy projectiles. They
// include:
//
// - projectile.create() - this.addUnit()
//
// - this.projectNormal() - this.isUnitAdded()
//
// - this.projectArcing() - this.removeUnit()
//
// - this.setTargPoint() - this.removeAll()
//
// - this.setProjPoint() - this.attachData()
//
// - this.disjoint() - this.getData()
//
// - this.refresh() - this.hasData()
//
// - this.terminate() - this.detachData()
//
// Public Projectile members:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • These members are publically available to users at any time throughout the lifetime of a projectile. They can be
// changed and manipulated at any point. They include:
//
// - this.sourceUnit - this.currentSpeed - this.onExpire - this.allowDestCollisions
//
// - this.targetUnit - this.timedLife - this.onLand - this.allowTargetHoming
//
// - this.owningPlayer - this.damageDealt - this.onUnit - this.removalDelay
//
// - this.unitHitRadius - this.pauseProj - this.onDest
//
// - this.destHitRadius - this.effectPath - this.allowDeathSfx
//
// - this.scaleSize - this.onStart - this.allowExpiration
//
// - this.timeScale - this.onLoop - this.allowArcAnimReset
//
// - this.zOffset - this.onFinish - this.allowUnitCollisions
//
// Readonly Projectile members:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • These members are only available for users to read. They cannot be changed or manipulated (unless through the
// above methods, i.e. .setTargPoint), they are there for reference only. They include:
//
// - projetile[unit] - this.velX - this.strZ
//
// - this.dummy - this.velY - this.distanceMax
//
// - this.arcSize - this.velZ - this.distanceLeft
//
// - this.angle - this.tarX - this.distanceDone
//
// - this.isTerminated - this.tarY - this.previousSpeed
//
// - this.posX - this.tarZ - this.originalSpeed
//
// - this.posY - this.strX
//
// - this.posZ - this.strY
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\
//
// Detailed Projectile API descriptions:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Methods:
// ¯¯¯¯¯¯¯¯¯
// • projectile.create(real x, real y, real z, real angle) -> returns projectile
//
// - Above is the only method for creating projectiles. It takes four simple parameters: The starting x, y and z
// coordinates of the projectile you want to create, and the angle you want it to face. The angle for the
// facing direction of the projectile is taken in RADIANS. This method will return the created projectile for you
// to save into a variable.
//
// • this.projectNormal(real x, real y, real z, real speed) -> returns boolean
//
// - Above is one of the two different methods that can be chosen for launching a projectile. This method is the
// simpler of the two, which doesn't allow for an arcing movement. It takes the target x, y and z coordinates
// of the projectile and the speed at which you want the projectile to move. This method will return true if the
// projectile successfully launched and false if it didn't (possibly because it had already been launched).
//
// • this.projectArcing(real x, real y, real z, real speed, real arcSize) -> returns boolean
//
// - Similar to the above method, this method is also used for launching a projectile, however this method allows
// for the projectile to arc along the z axis. It takes the target x, y and z coordinates of the projectile, the
// speed at which the projectile will move and the size of the arc you want. Values for the arcSize should range
// from 0.00 to 1.00 (0.10-0.40 recommended). This method returns the same boolean as this.projectNormal().
//
// • this.setTargPoint(real x, real y, real z, boolean wantUpdate) -> returns nothing
//
// - The above method can be used to change the target coordinates of a projectile, effectively changing where the
// projectile will move to. The method takes the x, y and z coordinates of the new target destination, which will
// update the direction of the projectile in the next iteration of the timer. The boolean parameter at the end
// determines whether or not the system will update the projectile's new velocities according to the new target
// coordinates (there are some situations in which update and not updating are applicable).
//
// • this.setProjPoint(real x, real y, real z, boolean wantUpdate) -> returns nothing
//
// - The above method can be used to change the current position of a projectile, without changing its target
// coordinates or any other aspect of the projectile. The method takes the x, y and z coordinates of the new
// position of the projectile. This method does not return anthing (same with the above method). The boolean
// parameter at the end determines whether or not the system will update the projectile's new velocities
// according to the new position coordinates (there are some situations in which update and not updating are
// applicable).
//
// • this.disjoint() -> returns nothing
//
// - This method is basically an easy way for the user to remove the target unit of a projectile and effectively
// make it target the ground and discontinue its homing capabilities. The method has no parameters and can simply
// be used whenever the user wants. This works excellently if you want a projectile to disjoint when a unit blinks.
//
// • this.refresh(boolean fireOnStart) -> returns nothing
//
// - This method will stop a projectile from moving and enable it to be launched again via the .projectNormal and
// .projectArcing methods. It allows users to quickly stop and start a projectile with a different arc height as
// as well as target coordinates and speed. The boolean parameter is used to determine whether or not the
// .onStart function interface member should be executed again, or removed from the projectile. Using true as the
// parameter will allow the .onStart function to be executed again, while false will remove it completely.
//
// • this.terminate() -> returns nothing
//
// - The above method is used to terminate (AKA destroy) a projectile. This will in turn remove the projectile from
// the game as well as any data associated with it. Projectiles will be removed from any projgoups they are in.
// This method takes no parameters and can be used whenever the user wants.
//
// • this.addUnit(unit u) -> returns nothing
//
// - The above method was added for completeness. It will add a unit to the 'already passed through' group of a
// projectile without firing the unit impact event. This can be used to quickly add units to the group that you
// know won't be a target of a projectile. It takes a unit parameter, which is the unit that will be added.
//
// • this.isUnitAdded(unit u) -> returns boolean
//
// - The above method was also added for completeness. It will check to see if a given unit is already in the
// 'already passed through' group of a projectile. This can be used in conjunction with this.addUnit() and
// this.removeUnit() to do some interesting things. It takes a unit parameter.
//
// • this.removeUnit(unit u) -> returns nothing
//
// - The above method is the last of the functions added for completeness. This method will remove a given unit
// from a projectile's 'already passed through' group so that it may fire the unit impact event again (or maybe
// for the first time if this.addUnit() was used on it). It takes a unit parameter.
//
// • this.removeAll() -> returns nothing
//
// - This method can be used to completely clear all units that have been hit by a projectile. Doing so will make
// all previously hit units available to be hit once more. Can be used with projectiles that travel to a target
// point and return, so that damage can be dealt twice.
//
// • this.attachData(integer whichData) -> returns nothing
//
// - Users can attach extra data to a projectile via the above method. This enables extra data to be transfered via
// structs so that projectiles can be more inclusive in regards to user needs. The method takes one parameter,
// which can be any struct that the user wants to attach the the projectile.
//
// • this.getData() -> returns integer
//
// - The above method is used to retrieve data attached to a projectile. It has no parameters and can be used in a
// similar fashion to how TimerUtils and KeyTimers 2 works. The data can then be manipulated in the function
// interface members of a projectile.
//
// • this.hasData() -> returns boolean
//
// - This method will return true if a given projectile currently has some form of data attached to it and false
// if there is no data attached.
//
// • this.detachData() -> returns nothing
//
// - The above method will remove any currently attached data from a projectile. Users do not have to manually
// remove data as it will be automatically removed upon projectile destruction. Users can also just override the
// currently attached data by using this.attachData() again. This is just here for API completion.
//
// Public members:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • this.sourceUnit -> The unit that can be saved as the source of the projectile (for damage events).
// • this.targetUnit -> A unit can be saved as the target unit to enable homing capabilities.
// • this.owningPlayer -> Can be set to the owning player of the source unit.
// • this.damageDealt -> Damage amounts can be saved directly to a projectile for when it hits a unit.
// • this.unitHitRadius -> The radius in which a unit must be for it to be hit by a projectile.
// • this.destHitRadius -> The radius in which a destructable must be for it to be hit by a projectile.
// • this.zOffset -> Can save an approximate height for projectile impacts on a target unit.
// • this.timeScale -> Time scale of a projectile. Can be used to slow down or speed up a projectile.
// • this.timedLife -> The life span of a projectile. Can be changed to anything. Defaults to dist / speed.
// • this.scaleSize -> Determines the size of the projectile (similar to scale size for units).
// • this.effectPath -> The model that will be attached to the dummy unit to create the projectile effect.
// • this.currentSpeed -> Determines the movement speed of a projectile.
// • this.removalDelay -> Determines how long until the projectile will be removed if .allowDeathSfx is true.
// • this.pauseProj -> Whether or not a projectile should be paused or not (collision works while paused).
// • this.allowDeathSfx -> Whether or not to show the death effect of a projectile. Defaults to false.
// • this.allowExpiration -> If a projectile will terminate once its timed life is up. Defaults to false.
// • this.allowArcAnimReset -> Whether or not to reset a projectile's arc animation on death. Helps display effects.
// • this.allowTargetHoming -> Whether or not to allow homing on a target unit for a projectile. Defaults to false.
// • this.allowUnitCollisions -> Whether or not to allow projectile and unit collision. Defaults to false.
// • this.allowDestCollisions -> Whether or not to allow projectile and destructable collisions. Defaults to false.
//
// Function interfaces as members:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - this.onStart -> Can be set to a function that will be executed when a projectile is launched.
// - this.onLoop -> Can be set to a function that will be executed periodically for a projectile.
// - this.onFinish -> Can be set to a function that will be executed when a projectile is terminated.
// - this.onExpire -> Can be set to a function that will be executed when a projectile's timed life expires.
// - this.onLand -> Can be set to a function that will be executed whenever a projectile hits terrain.
//
// • All of the above will execute a function that a user can specify to create specific event responses for
// different projectiles. These are higher end members that take a little more knowledge to master. For
// example:
//
// private function OnLandImpact takes projectile whichProj returns nothing
// call whichProj.terminate() <--- The function OnLandImpact must take a projectile parameter.
// endfunction
//
// private function Actions takes nothing returns nothing
// local projectile p = 0
// local unit u = GetTriggerUnit()
// local real ux = GetUnitX(u)
// local real uy = GetUnitY(u)
// local real tx = GetSpellTargetX()
// local real ty = GetSpellTargetY()
//
// set p = projectile.create(ux,uy,50.00,Atan2((ty - uy),(tx - ux)))
//
// set p.sourceUnit = u
// set p.owningPlayer = GetOwningPlayer(u)
// set p.effectPath = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl"
// set p.scaleSize = 0.75
// set p.zOffset = 0.00
//
// set p.allowUnitCollisions = false
// set p.allowProjCollisions = false
// set p.allowDestCollisions = false
//
// set p.onLand = OnLandImpact <--- Notice how p.onLand is set to the function OnLandImpact.
//
// call p.projectNormal(tx,ty,0.00,1024.00)
//
// set u = null
// endfunction
//
// • These five function interface members can be set to a function that takes a single projectile parameter and
// returns nothing. This is very important! The functions can be named whatever a user likes, but they must
// follow this rule. The single projectile parameter for the function refers to a projectile that has just
// completed an event (be it .onLand or .onStart), and can therefore be manipulated by the user to do something
// unique for that event.
//
// - this.onUnit -> Can be set to a function that will be executed when a projectile hits a unit.
// - this.onDest -> Can be set to a function that will be executed when a projectile hits a destructable.
//
// • The above are function interfaces that are executed when a projectile hits another object, such as a unit,
// projectile or destructable. These function in a very similar way to the other function interface members,
// however they take two parameters, one projectile and one of a specific type that relates to the event. For
// example:
//
// private function OnUnitImpact takes projectile p, unit u returns nothing
// call UnitDamageTarget(p.sourceUnit,u,p.damageDealt,false,fasle,null,null,null)
// call p.terminate()
// endfunction
//
// - As you can see, the above function is for the this.onUnit member. This function must take one
// projectile parameter (referring to the projectile hitting the unit) and one unit parameter (referring to
// the unit being hit). It must return nothing.
//
// private function OnDestImpact takes projectile p, destructable d returns nothing
// call KillDestructable(d)
// call p.terminate()
// endfunction
//
// - The above function is for the this.onDest member, used for projectile on destructable collision events.
// The function takes one projectile parameter and one destructable parameter, each referring to a specific
// object in the collision. This function must return nothing.
//
// • It is important to note that the functions and their parameters can be named whatever the user wants, but they
// must remain in the order stated above (projectile parameter always comes first) and the functions must return
// nothing.
// • For more examples of how this works, check out the example spells (specifically: Hurl Boulder) in the test
// map.
//
// Readonly members:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • this.dummy -> Refers to the dummy unit of a projectile (the actual projectile object).
// • this.angle -> Refers to the current facing angle of a projectile.
// • this.previousSpeed -> Retrieves the previous speed of a projectile.
// • this.originalSpeed -> Retrieves the original speed of a projectile.
// • this.isTerminated -> Refers to whether or not a projectile is currently terminated (true if it is).
// • this.posX/posY/posZ -> Refers to the current x, y and z coordinates of a projectile.
// • this.tarX/tarY/tarZ -> Refers to the current target x, y and z coordinates of a projectile.
// • this.velX/velY/velZ -> Refers to the current x, y and z velocities of a projectile.
// • this.strX/strY/strZ -> Refers to the starting x, y and z coordinates of a projectile.
// • this.distanceMax -> Maximum distance to be travelled by a projectile (start to finish) in a straight line.
// • this.distanceLeft -> Distance left to travel for a projectile (current to finish) in a straight line.
// • this.distanceDone -> Distance from a projectile's starting point to its current point (straight line).
// • this.arcSize -> Retrieves the arc size used for a projectile.
//
// Static method operator:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - projectile[unit u] -> Retrieves the projectile data attached to a unit. Unit -> projectile typecasting.
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\
//
// _____ _ _____
// | __ \ (_)/ ____|
// | |__) | __ ___ _| | __ _ __ ___ _ _ _ __
// | ___/ '__/ _ \| | | |_ | '__/ _ \| | | | '_ \
// | | | | | (_) | | |__| | | | (_) | |_| | |_) |
// |_| |_| \___/| |\_____|_| \___/ \__,_| .__/
// _/ | | |
// |__/ |_|
// By Kenny v0.1.3 (Beta)
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\
//
// What is ProjGroup?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • ProjGroup is a fully functional projectile group API for the Projectile system, designed to closely match the
// native WC3 unit groups.
// • ProjGroup provides common grouping functions for projectiles such as adding and removing projectiles from a group,
// creating, clearing and destroying groups and enumerating through groups.
// • ProjGroup was designed to make projectile manipulation easier for users, therefore it has a simplistic interface
// that is easy to understand. It also comes with a WC3 alternative interface that resembles native WC3 functions.
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\
//
// ProjGroup API:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • This is a quick over-view of all the methods and globals available to users with the projgroup struct.
//
// ProjGroup methods:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • These methods are the main methods used by the system to create, manipulate and destroy projgroups. They
// include:
//
// - projgroup.create() - this.forNearby()
//
// - this.add() - this.forNearbyEx()
//
// - this.remove() - this.forGroup()
//
// - this.isInGroup() - this.forGroupEx()
//
// - this.getCount() - this.destroy()
//
// - this.getFirst()
//
// - this.clear()
//
// - this.enumNearby()
//
// ProjGroup globals:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • These globals are to be used in conjunction with the this.enumNearby and this.forGroup() methods of projgroups
// to access projectiles. They include:
//
// - EnumProjectile
//
// - ParentProjGroup
//
// - ForProjGroupData
//
// - GlobalProjGroup
//
// Alternate ProjGroup API:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • This is an alternate interface for projgroups that closely resembles native WC3 group API, for nostalgia's sake.
//
// ProjGroup functions:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • These functions are the alternate interface for projgroups that encompass both projgroup methods and globals.
// They include:
//
// - CreateProjGroup() - ForNearbyProjectilesInRange() - GetGlobalProjGroup()
//
// - ProjGroupAddProjectile() - ForNearbyProjectilesInRangeEx()
//
// - ProjGroupRemoveProjectile() - ForProjGroup()
//
// - IsProjectileInProjGroup() - ForProjGroupEx()
//
// - CountProjectilesInProjGroup() - DestroyProjGroup()
//
// - FirstOfProjGroup() - GetEnumProjectile()
//
// - ProjGroupClear() - GetParentProjGroup()
//
// - GroupEnumProjectilesInRange() - GetForProjGroupData()
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\
//
// Detailed ProjGroup API descriptions:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Methods:
// ¯¯¯¯¯¯¯¯¯
// • projgroup.create() -> returns projgroup
// or
// • CreateProjGroup() -> returns projgroup
//
// - The above method enables users to create a projgroup, which is basically a group for projectiles (similar to
// WC3 native groups for units). This method will returns a projgroup to be saved into a variable.
// - The alternative WC3 naming for this requires no parameters as well.
//
// • this.add(projectile p) -> returns boolean
// or
// • ProjGroupAddProjectile(projgroup g, projectile p) -> returns boolean
//
// - The above method adds a projectile to a projgroup (similar to GroupAddUnit()). This method will return true
// if the projectile was successfully added and false if it wasn't.
// - The alternative WC3 naming for this requires a projgroup parameter before the projectile parameter.
//
// • this.remove(projectile p) -> returns boolean
// or
// • ProjGroupRemoveProjectile(projgroup g, projectile p) -> returns boolean
//
// - This method removes a projectile from a projgroup (similar to GroupRemoveUnit()). This method will return true
// if the projectile was successfully removed and false if it wasn't.
// - The alternative WC3 naming for this requires a projgroup parameter before the projectile parameter.
//
// • this.isInGroup(projectile p) -> returns boolean
// or
// • IsProjectileInProjGroup(projgroup g, projectile p) -> returns boolean
//
// - This method checks to see if a specified projectile is in a projgroup (similar to IsUnitInGroup()). This method
// will return true if the projectile is in the group and false if it isn't.
// - The alternative WC3 naming for this requires a projgroup parameter before the projectile parameter.
//
// • this.getCount() -> returns integer
// or
// • CountProjectilesInProjGroup(projgroup g) -> returns integer
//
// - This method will return an integer that represents the number of projectiles currently in a group (similar to
// CountUnitsInGroup()).
// - The alternative WC3 naming for this requires a projgroup parameter.
//
// • this.getFirst() -> returns projectile
// or
// • FirstOfProjGroup(projgroup g) -> returns projectile
//
// - This method attempts to simulate the WC3 native FirstOfGroup(). It will return a projectile that is at the
// start of the internal projectile array of a projgroup. Useful for first-of-group-loops.
// - The alternative WC3 naming for this requires a projgroup parameter.
//
// • this.clear() -> returns nothing
// or
// • ProjGroupClear(projgroup g) -> returns nothing
//
// - This method will clear all projectiles from a projgroup, emptying it of all data completely (similar to
// GroupClear()). It returns nothing.
// - The alternative WC3 naming for this requires a projgroup parameter.
//
// • this.enumNearby(real x, real y, real z, real radius) -> returns nothing
// or
// • GroupEnumProjectilesInRange(projgroup g, real x, real y, real z, real radius) -> returns nothing
//
// - This method will group all projectiles in a specified radius around given coordinates and add them to a
// projgroup. Before adding projectiles to the projgroup it will clear to projgroup of all existing projectiles,
// to better simulate the WC3 GroupEnumUnitsInRange(). It takes four parameters, the first three being the x, y and
// z coordinates around which you want projectiles to be added, and the fourth being the radius in which
// projectiles need to be to be added to the projgroup.
// - The alternative WC3 naming for this requires a projgroup parameter before other parameters.
//
// • this.forGroup(enumFunc e) -> returns nothing
// or
// • ForProjGroup(projgroup g, enumFunc e) -> returns nothing
//
// - This method will perform a function for every projectile in a projgroup. It has one parameter, which is the
// function that will be executed for all projectiles, it is similar to the function interface members for the
// projectile struct. The function that is to be passed through this method as an parameter must take nothing and
// return nothing. For example:
//
// private function EnumFunctionExample takes nothing and returns nothing
// call EnumProjectile.terminate()
// endfunction
//
// - As you can see above the function takes nothing and returns nothing, and inside this function users can use
// the EnumProjectile global to access the current projectile the function is being executed for.
// - The alternative WC3 naming for this requires a projgroup parameter before the enumFunc parameter.
//
// • this.forGroupEx(enumFunc e, integer i) -> returns nothing
// or
// • ForProjGroupEx(projgroup g, enumFunc e, integer i) -> returns nothing
//
// - This method works in the exact same way as the above-mentioned .forGroup() method, except that it takes one
// more parameter. This last parameter (that comes after the enumFunc parameter) refers to any temporary data
// that a user wants to access in the enumFunc provided by the user. It also provides access to the parent
// projgroup that the method was used for. For example:
//
// private function EnumFunctionExample takes nothing and returns nothing
// call Data(ForProjGroupData).destroy() <-- Needs to be typecasted, as with some timer data systems.
// call EnumProjectile.terminate()
// call ParentProjGroup.destroy()
// endfunction
//
// - As you can see above the function takes nothing and returns nothing, and inside this function users can
// access not only the projectile the function is being executed for, but also any temporary data and the
// parent projgroup that the .forGroupEx() method was called for.
// - The alternative WC3 naming for this requires a projgroup parameter before the other parameters.
//
// • this.forNearby(real x, real y, real z, real radius, integer i, enumFunc e)
// or
// • ForNearbyProjectilesInRange(projgroup p, real x, real y, real z, real r, integer i, enumFunc e)
//
// - The above method works similarly to .enumNearby(), however instead of just adding the projectiles to the group
// it will also execute the enumFunc for each projectile added. Think of it as a mix between .enumNearby() and
// .forGroupEx(). This method also takes an integer parameter before the enumFunc that can be set to temporary
// data for use.
// - The alternative WC3 naming for this requires a projgroup parameter before the other parameters.
//
// • this.forNearbyEx(real x, real y, real z, real radius, integer i, enumFunc e1, enumFunc e2)
// or
// • ForNearbyProjectilesInRangeEx(projgroup p, real x, real y, real z, real r, integer i, enumFunc e1, enumFunc e2)
//
// - The above method works in the same way as .forNearby(), however it has an extra enumFunc parameter that will
// be executed when any projectiles that are still in the projgroup are removed from the group before the
// enumerating begins. The first enumFunc parameter is for when the projectiles get removed, while the second
// enumFunc parameter is for when a projectile is added to the projgroup. This method can also save and access
// temporary data and parent projgroups for .forGroupEx().
// - The alternative WC3 naming for this requires a projgroup parameter before the other parameters.
//
// • this.destroy() -> returns nothing
// or
// • DestroyProjGroup(projgroup g) -> returns nothing
//
// - This method destroys a projgroup, clearing it of all data (similar to DestroyGroup()). This method returns
// nothing.
// - The alternative WC3 naming for this requires a projgroup parameter.
//
// Globals:
// ¯¯¯¯¯¯¯¯¯
// • EnumProjectile -> returns projectile
// or
// • GetEnumProjectile() -> returns projectile
//
// - This global returns the most recent projectile a function from .forGroup() has been called for. It is to be
// used like GetEnumUnit() in a normal ForGroup() call.
// - The alternative WC3 naming for this is a function not a global.
//
// • ParentProjGroup -> returns projectile
// or
// • GetParentProjGroup() -> returns projectile
//
// - This global returns the most recent projgroup taht .forGroupEx() has been called for. It could come in handy
// for some situations.
// - The alternative WC3 naming for this is a function not a global.
//
// • ForProjGroupData -> returns projectile
// or
// • GetForProjGroupData() -> returns projectile
//
// - This global returns the most recent temporary data that a user wanted to access in the .forGroupEx() method.
// This can be used to access extra data needed for the function.
// - The alternative WC3 naming for this is a function not a global.
//
// • GlobalProjGroup -> returns projgroup
// or
// • GetGlobalProjGroup() -> returns projgroup
//
// - This global is to be used like the temporary group from GroupUtils. It saves the user from creating and
// destroying a projgroup if they need to use one .enumNearby() and .forGroup() call.
// - The alternative WC3 naming for this is a function not a global.
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\
//
// Miscellaneous API:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • This is a quick over-view of all the other miscellaneous functions available to users that haven't been covered.
//
// Various functions:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • These functions have been added to the system for completeness, and also offer some use for both debugging and
// gameplay purposes. They include:
//
// - GetTotalProjectiles()
//
// - IsUnitProjectile()
//
// - GetUnitProjectileData()
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\
//
// Detailed miscellaneous API descriptions:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Functions:
// ¯¯¯¯¯¯¯¯¯¯¯
// • GetTotalProjectiles() -> returns integer
//
// - This function returns the total amount of projectiles that are currently active in the map. This can be useful
// for debugging purposes.
//
// • IsUnitProjectile(unit u) -> returns boolean
//
// - This function takes a unit parameter (referring to which unit the user wants to check) and checks whether or not
// it is a projectile.
//
// • GetUnitProjectileData(unit u) -> returns projectile
//
// - This function retrieves the projectile data that is attached to the unit passed through the function. Be sure
// to check if the unit is a projectile beforehand.
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\
//
// Configurables:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Public configurables:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • DUMMY_ID -> The raw code of the dummy unit used in this system. Make sure you change this to the raw
// code of the dummy unit in your map.
// • CROW_FORM -> The raw code for the crow form ability that allows for unit heights to be changed. If you
// have changed this ability in any way, you may want to make another and change the raw code.
// • OWNER_ID -> The owning player of the dummy units used for the projectiles in this system. Can be used
// for debugging purposes.
// • RECYCLE -> Whether or not the system is set to recycle dummy units when they are no longer being used.
// Useful for keeping lag to a minimum.
// Private configurables:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// • DEFAULT_UNIT_COLL -> The default unit collision radius for projectiles on unit collisions.
// • DEFAULT_DEST_COLL -> The default destructable collision radius for projectile on destructable collisions.
//
// • REMOVAL_DELAY -> Default time taken to remove a projectile if .allowDeathSfx is true (can be changed dynamically).
//
// • MAX_PROJECTILES -> How many projectiles can be active at any one time. This number will never need to change.
// • MAX_PROJGROUPS -> How many projgroups can be active at any given time. This number will never need to change.
//
// • PRELOAD_PROJECTILES -> Whether or not to allow preloading of projectiles. This only matters if RECYCLE is true.
// • PRELOAD_AMOUNT -> How many projectiles should be preloaded if preloading is allowed.
//
//------------------------------------------------------------------------------------------------------------------------\\
Notes:
This system is basically finished as of version 0.2.6 (Beta). If no seriously bugs or issues are found with the system, the next update will be version 1.0.0, the official non-beta version. I will still attempt to work on this as much as possible if need be.
I welcome all ideas and feedback.
I welcome all ideas and feedback.
The Test Map:
A fair bit of work has gone into showing you just how powerful this system is. In combination with a few other systems, it is capable of recreating some of the more complex spells in wc3, some of which you may have seen in Diablo III for warcraft and Tides of Blood. The test map includes:
- Hurl Boulder: A simple spell that shows the simplicity of the projectile system and the additional bouncing module.
- Slow Projectiles: A thought-to-be complex spell, recreated quite easily using this system.
- Blink: Normal blink that gives the ability to 'disjoint' close incoming projectiles, making them lose their homing capabilities.
- Time Warp: As seen in Archer Wars: Legacy. Gives the ability to make projectiles 'skip' ahead in time.
- Defend: Gives a chance to redirect incoming projectiles before they hit a unit.
- Double Shot: Gives a chance to fire two ranged projectiles at once with the archers.
- Gravity Ball: A simple linear projectile demonstrating both unit and projectile collision.
- Cluster Rocket: An ability that makes one rocket split into many, making use of data attachment for coordinates.
- Life Steal: This just shows that simulating wc3 ranged projectiles is possible with this system. Even when using triggered orb effects.
- Simulation of wc3 ranged projectiles: Shows the power that this system holds and how simple these things really are.
- Version 0.2.6 (Beta): Major updates and added features.
- Too many changes to remember, only the ones I can remember and the most important are below.
- Removed projectile on projectile collision as it was far too buggy and I didn't want that in the final product.
- Reverted unit collision detection from trigger based detection to group based. Performance hit should be negligible.
- Added extra methods and functions to ProjGroups for added grouping functionality. They are confusing but helpful in terms of efficiency.
- Added a projectile member that will determine the death animation duration of a projectile.
- System now internally checks if a target unit is alive, via the [ljass]UnitAlive()[/ljass] native, to stop some bugging.
- Due to above updates, projectile structs now use up much less handles, slightly improving projectile efficiency in multiple areas.
- Slight scripting updates to the projectile script as well as the ProjBounces script.
- Documentation updates and changes.
- Some slight polishing off.
- Removal of event as a required library, this also means that the global projectile collision event is gone (for obvious reasons).
- The system is now safer and smoother overall. Allowing for much more consistent projectile usage.
- Version 0.2.5 (Beta): Minor updates and added features.
- An efficiency increase that allows my laptop to handle 25+ more projectiles than the previous versions with a similar FPS drops.
- Added ProjBounces as an optional module, allowing projectiles to bounce off terrain! Fun stuff.
- Fixed ProjShadows so that shadows aren't displayed through fog of war.
- Fixed an oversight on my behalf that lead to the unit leaves rect event firing every time a projectile was recycled.
- Implemented xombie's fix for projectile removal (thanks xombie).
- Renamed a fair few members for a more consistent interface.
- The removal of Vector as a required library.
- Faster projectile grouping (via [ljass]this.enumNearby[/ljass]).
- Collision triggers will no longer be disabled and destroyed if they are not active.
- Removal of the linked list implementation from the projgroup struct.
- Small scripting changes and polishing.
- Updated documentation.
- More polishing off.
- Version 0.2.4 (Beta): Minor updates and added features.
- Added two new general methods: [ljass]this.refresh()[/ljass] and [ljass]this.removeAll()[/ljass]. Read documentation for details.
- Added projectile data attachment. Basically just hashtable wrappers, but they give an easy interface for attaching extra data to a projectile.
- Data attachment methods: [ljass]this.attachData()[/ljass], [ljass]this.getData()[/ljass], [ljass]this.hasData()[/ljass] and [ljass]this.detachData()[/ljass]. Details in documentation.
- Added support for ProjShadows, an optional module that allows users to attach shadows to projectiles.
- Removed some unneeded multiplications, divisions and variable sets/retrievals in the periodic method (tiny, tiny efficiency increase).
- Added safety to [ljass]this.addUnit()[/ljass] and [ljass]this.removeUnit()[/ljass].
- Added [ljass]this.arcSize[/ljass] as a readonly member to retrieve a projectiles arc size when it was created.
- Added [ljass]this.resetAnimOnDeath[/ljass] as a public member so users can reset the projectile to the correct pitch for effects to display properly.
- Changed around how projectile targeting works so that there are no more visual glitches that rarely occured.
- Removed the use of local variables from the periodic method (just want to see how it goes).
- Renamed [ljass]this.setTargetPoint()[/ljass] to [ljass]this.setTargPoint()[/ljass] and [ljass]this.setProjectilePoint()[/ljass] to [ljass]this.setProjPoint()[/ljass].
- Added a user event for when a projectiles timed life expires ([ljass]this.onExpire[/ljass]). This allows for some pretty cool things to be done.
- The user event for when a projectile hits the ground will only when it first hits the ground, not repeatedly as it use to.
- Inlined a method used in the core of the system for a small efficiency gain.
- Added a max projectile limit and max projgroup limit (8190 each) for the sake of it. They will never need to be changed.
- Completely rewrote projgroups. Same interface, just more efficient. There is now no limit to how many projectiles can be in a projgroup.
- Changed around the [ljass]this.projectNormal()[/ljass] and [ljass]this.projectArcing()[/ljass] methods to make them work properly with [ljass]this.refresh()[/ljass].
- Added two more configurables for the max projectile and projgroup limits. Again, these won't need to be changed.
- Removed some useless function calls and stuff from the [ljass]this.targetUnit[/ljass] method operator.
- Removed [ljass]this.setTargPoint()[/ljass] from [ljass]this.disjoint()[/ljass], as it was pointless and looked bad.
- Renamed [ljass]this.projectileSpeed[/ljass] to [ljass]this.currentSpeed[/ljass].
- Now uses a global location to get z coordinate instead of a static struct member location.
- Region for projectile on projectile collision is now cleared of the last added rect.
- Regions used for projectile on projectile collision are now recycled internally.
- Cleaned up some parts of the system script.
- Updated documentation.
- Version 0.2.3 (Beta): Minor updates and added features.
- Added three new methods: [ljass]this.addUnit()[/ljass], [ljass]this.isUnitAdded()[/ljass] and [ljass]this.removeUnit()[/ljass].
- Above methods were added for system completeness. Read documentation for reasons.
- Made it so that projectiles still collide with destructables even when paused.
- Made it so that projectile coordinates are still update for projectile on projectile collision when paused.
- All collision should now work properly when a projectile is paused.
- Cleaned up some parts of the system script.
- Added GroupUtils as an optional requirement. Users can now choose to have Recycle, GroupUtils or nothing, and everything will work fine.
- Updated documentation.
- Cleaned up test map examples.
- Version 0.2.2 (Beta): Added features and removed requirements.
- Removed AutoFly as a requirement.
- Removed GroupUtils as a requirement.
- Added Recycle as an optional requirement.
- Added distance retrieval operators, including: [ljass]this.distanceMax[/ljass], [ljass]this.distanceLeft[/ljass] and [ljass]this.distanceDone[/ljass] (all of which will only calculate distance in a straight line on the X and Y axes).
- Added two new public members: [ljass]this.timeScale[/ljass] and [ljass]this.timedLife[/ljass].
- Renamed .expireOnReach to [ljass]this.expireOnTimeUp[/ljass] to better represent what it does.
- Added an new parameter to [ljass]this.setTargetPoint()[/ljass] to set a projectile’s new facing direction instantly. This is only a test run so far.
- Updated documentation to include all changes (plus some things I forgot).
- Various small changes to the system script.
- Version 0.2.1 (Beta): Complete system rewrite.
- Cleaned up projectile interface.
- Reduced the number of methods available to users, increased number of members available.
- Standardised method, member and operator names.
- Cleaned up user safety mechanisms (they are no longer dodgy hacks).
- Rewrote documentation and cleaned up the system's script.
- Fixed homing arcing projectile movement.
- Fixed hashtable values not being removed.
- Added destructable collision detection.
- Made it so that all collision detection can be enabled and disabled.
- Fixed unit collision radius so that it can be adjusted multiple times without bugging.
- Neatened up periodic method (uses different maths for same result). Possibly more efficient now.
- Got rid of temporary vector usage in periodic function to reduce function calls.
- Got rid of unused variables and members.
- Removed the [ljass]RegisterProjectileRecycleEvent()[/ljass], as it was useless.
- Added projgroups (A complete interface for projectile grouping).
- Added a WC3 alternative interface for projgroups.
- Added a [ljass]GetTotalProjectiles()[/ljass] function for debugging.
- Cleaned up function interface usage.
- Now 200% more awesome.
- Version 0.1.1 (Beta): Bug fixes and added features.
- Implemented projectile recycling. Users can now choose to recycle projectiles by setting the RECYCLE boolean to true. Users can also preload projectiles and set the amount of preloaded projectiles.
- Changed how timed life works for projectiles. This was done to reduce the work done by the system in the periodic method. Users can now only set the timed life and not access it.
- Fixed how projectiles worked with terrain height changes (thanks PurgeandFire).
- Added user safety to the [ljass]this.projectNormal()[/ljass] and [ljass]this.projectArcing()[/ljass] methods to ensure terrain height changes are taken into account and projectiles will still work properly if users forget to adjust for terrain themselves.
- Now destroys the .start and .targ vectors (completely forgot about them).
- Reduced the number of function interfaces defined by the system by four. Also made them private.
- Minor coding updates and readability changes.
- Updated documentation.
- Version 0.1.0 (Beta): Initial Release.