- Joined
- Sep 26, 2009
- Messages
- 9,534
JASS:
//---------------------------------------------------------------------------
// Projectile Utilities by Bribe, for use with Berb's Projectiles Library.
//---------------------------------------------------------------------------
//
// ProjectileUtils makes launching projectiles between units a user-friendly,
// automatic process. The main functions are listed under Users' API below.
//
//---------------------------------------------------------------------------
// Thanks
//---------------------------------------------------------------------------
//
// Berb for Custom Projectiles - the library that inspired me to learn vJass.
// &
// Vexorian for JassHelper and the dummy.mdx model.
// &
// Nestharus for inspiring ideas that add to the dynamics of this library.
//
//---------------------------------------------------------------------------
// Requirements
//---------------------------------------------------------------------------
//
// Projectile and ProjectileArt by Berb
// - http://www.hiveworkshop.com/forums/showthread.php?t=162121
//
// Table by Bribe
// - http://www.hiveworkshop.com/forums/showthread.php?t=188084
//
// xebasic by Vexorian
// - http://www.wc3c.net/showthread.php?t=101150
//
//---------------------------------------------------------------------------
// Dummy-unit settings to produce accurate and optimal projectile simulations
//---------------------------------------------------------------------------
//
// Art - Model File
// - Dummy.mdx
//
// Art - Special (unit explodes)
// - None
//
// Animation - Blend Time
// - 0.00
//
// Pitch/Roll - Maximum Rotation
// - 0.00
//
// Abilities
// - 'Aloc' (Locust)
//
//---------------------------------------------------------------------------
// Users' API
//---------------------------------------------------------------------------
//
// FireProjectile(unit, unit, real, real, real, boolean, string) -> projectile
//
// unit source,
// : The unit firing the projectile.
//
// unit target,
// : The target unit of the projectile.
//
// real damage,
// : The amount of damage the source deals to the target upon on-impact.
//
// real speed,
// : Projectile's speed.
//
// real arc,
// : Projectile's arc.
//
// boolean homing,
// : The projectile will home in on the target unit.
//
// string modelpath,
// : The file path of what you want the projectile to look like.
//
// returns projectile,
// : A new projectile each time the function is called.
//
//---------------------------------------------------------------------------
//
// FireProjectileXY(unit, real, real, real, real, string) -> projectile
//
// unit source,
// : Same as FireProjectile.
//
// real x, y,
// : Coodinates to fire the missile towards.
//
// real speed, arc, string modelpath, returns projectile
// : Same as FireProjectile.
//
//---------------------------------------------------------------------------
//
// SetProjectileVisualsById(integer, real, integer, integer, integer, integer)
//
// integer unitid,
// : units of this id will have projectiles with these visual settings.
//
// real scale,
// : adjusts the projectile's visual size. 1.0 == 100% (default).
//
// integer alpha, red, green, blue,
// : adjusts projectiles' vertex colors. 255 is 100% (default). 0 alpha is
// invisible.
//
//---------------------------------------------------------------------------
//
// SetProjectileImpactEvent(projectile, function)
//
// projectile proj,
// : This parameter can be easily filled using GetLastCreatedProjectile().
//
// function <name>,
// : The name of the function you want to call when the projectile impacts
// with the target unit. The function should take a projectile instance
// and return nothing.
// '''
// function exampleFunc takes projectile proj returns nothing
// call UnitDamageTarget(proj.source, proj.target, GetProjectileDamage(proj), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
// call BJDebugMsg(GetUnitName(proj.target) + " was hit by " + GetUnitName(proj.source) + " for " + I2S(R2I(GetProjectileDamage(proj))) + " damage!")
// endfunction
// '''
// That example-function would be called like this:
// '''
// call SetProjectileImpactEvent(GetLastCreatedProjectile(), exampleFunc)
// '''
// WARNING: This function overrides the normal damaging method used by
// the internal system. You can use this to your advantage if you want
// an area-of-effect attack or if you want to have your own, customized
// attack-type, damage type or the like.
//
//---------------------------------------------------------------------------
//
// GetLastCreatedProjectile() -> projectile
//
// returns projectile,
// : the last projectile-instance created by the FireProjectile function.
//
//---------------------------------------------------------------------------
//
// GetProjectileDamage(projectile) -> real
//
// projectile proj,
// : The projectile instance to retrieve damage from.
//
// returns real,
// : the damage specified by FireProjectile, else the damage set by...
//
//---------------------------------------------------------------------------
//
// SetProjectileDamage(projectile, real)
//
// projectile proj,
// : The projectile instance to be assigned the value of...
//
// real damage,
// : Can be set for a projectile instance of any type, retrievable via
// GetProjectileDamage.
//
//---------------------------------------------------------------------------
//
// SetProjectileOffsetsById(integer, real, real, real, real)
//
// integer unitid,
// : units of this unit-type id will use these offsets for FireProjectile.
//
// real distance,
// : project an offset from the source unit's position to create & launch
// the projectile; works like PolarProjectionBJ, offset by...
//
// real angle,
// : value taken in radians (must be between 0.00 and 2PI (~ 6.2831853)).
//
// real launchz,
// : directly offsets the z-coordinate of launch and is unaffected by the
// given angle.
//
// real impactz,
// : projectiles will hit their targets from this height-offset.
//
//---------------------------------------------------------------------------
library ProjectileUtils requires Projectile, ProjectileArt, Table, xebasic
globals
//-----------------------------------------------------------------------
// Internal-use
//
// Configurable values
private constant real DEFAULT_LAUNCH_DISTANCE = 30.0 // Specify the default distance and angle to launch the projectile. These
private constant real DEFAULT_LAUNCH_ANGLE = 0.00 // values offset the source x/y values and create the projectile unit there.
// Angle must be in radians (between 0 and 2 * PI). Multiply by bj_DEGTORAD if
// you are unsure how to calculate it.
private constant real DEFAULT_LAUNCH_HEIGHT = 60.0 // Launch-height is not offset by launch-angle.
private constant real DEFAULT_IMPACT_HEIGHT = 60.0 // When the projectile hits its target, it will be offset by this height.
// Readability
private constant real PI2 = 6.2831853
// Scalar variables
private player s_owner = Player(15)
private unit s_unit = null
private integer s_size = 0
private real s_height = 0.00
private vector s_launchVec
private vector s_impactVec
private projectile s_proj
private Table s_hash
// Arrays
private boolean array a_offsets
private real array a_distance
private real array a_angle
private real array a_launchz
private real array a_impactz
private boolean array a_visuals
private real array a_scale
private integer array a_alpha
private integer array a_red
private integer array a_green
private integer array a_blue
endglobals
//===========================================================================
// A user-customizable on-impact function.
// - Re-set via SetProjectileImpactEvent
//
function interface ProjectileImpactFunc takes projectile p returns nothing
//===========================================================================
private module m
private static method onInit takes nothing returns nothing
set s_hash=Table.create()
set s_launchVec=vector.create(0., 0., 0.)
set s_impactVec=vector.create(0., 0., 0.)
endmethod
endmodule
//===========================================================================
private struct object extends projectile
// Instance members
real damage=0.
ProjectileImpactFunc func=0
//=======================================================================
// This transforms the struct requirement into a user-defined function.
//
method onFinish takes nothing returns nothing
if (this.func != 0) then
call this.func.evaluate(this)
endif
endmethod
//=======================================================================
// The default damaging function used by FireProjectile.
//
method damager takes nothing returns nothing
if (this.damage != 0.) then
call UnitDamageTarget(this.source, this.target, this.damage, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
endif
endmethod
//=======================================================================
// Data processing
//
static method operator [] takes integer unitid returns integer
local integer i=s_hash[unitid]
if (i==0) then
set i=s_size+1
set s_size=i
set s_hash[unitid]=i
endif
return i
endmethod
//=======================================================================
// Projectile creator...
// - Creates a new projectile instance from a new projectile unit
// - Attaches dummy-skin
// - Assigns target/impact vectors
// - Adjusts for customized offsets
//
static method create takes unit source, real x2, real y2, string modelpath returns thistype
local integer i=s_hash[GetUnitTypeId(source)]
local real x=GetUnitX(source)
local real y=GetUnitY(source)
local real angle=Atan2(y2 - y, x2 - x)
local real tempang=DEFAULT_LAUNCH_ANGLE
local real launchz=DEFAULT_LAUNCH_HEIGHT + GetUnitFlyHeight(source)
local real launchd=DEFAULT_LAUNCH_DISTANCE
if a_offsets[i] then
set tempang=a_angle[i]
set launchz=a_launchz[i]
set launchd=a_distance[i]
set s_height=a_impactz[i]
else
set s_height=DEFAULT_IMPACT_HEIGHT
endif
if (tempang != 0.) then
set angle = angle + tempang
if (angle > PI2) then
set angle = angle - PI2
elseif (angle < 0.) then
set angle = angle + PI2
endif
endif
call s_impactVec.getTerrainPoint(x2, y2)
call s_launchVec.getTerrainPoint(x + launchd * Cos(angle), y + launchd * Sin(angle))
set s_launchVec.z=s_launchVec.z + launchz
set s_unit=CreateUnit(s_owner, XE_DUMMY_UNITID, s_launchVec.x, s_launchVec.y, bj_RADTODEG * angle)
set s_proj=thistype.allocate(s_unit)
set s_proj.source=source
if (a_visuals[i]) then
call SetUnitScale(s_unit, a_scale[i], 0., 0.)
call SetUnitVertexColor(s_unit, a_red[i], a_green[i], a_blue[i], a_alpha[i])
endif
call SetUnitExploded(s_unit, true)
call s_proj.setModel(modelpath)
return s_proj
endmethod
implement m
endstruct
//***************************************************************************
//*
//* Users' API
//*
//***************************************************************************
//=======================================================================
function GetLastCreatedProjectile takes nothing returns projectile
return s_proj
endfunction
//=======================================================================
function GetProjectileDamage takes object proj returns real
return proj.damage
endfunction
//=======================================================================
function SetProjectileDamage takes object proj, real damage returns nothing
set proj.damage=damage
endfunction
//=======================================================================
function SetProjectileImpactEvent takes object proj, ProjectileImpactFunc func returns nothing
set proj.func=func
endfunction
//=======================================================================
// Streamlined projectile-firing utility. Passes basic arguments to the
// object.create method and then processes the additional arguments.
//
function FireProjectile takes unit source, unit target, real damage, real speed, real arc, boolean homing, string modelpath returns projectile
set object.create(source, GetUnitX(target), GetUnitY(target), modelpath).target=target
set s_impactVec.z=s_impactVec.z + GetUnitFlyHeight(target) + s_height
if (homing) and GetUnitAbilityLevel(target, 'Amov')!=0 then
set s_proj.targetZOffset=s_height
else
set s_proj.activeTargetFollow=false
endif
call SetProjectileDamage(s_proj, damage)
call SetProjectileImpactEvent(s_proj, object.damager)
call s_proj.launch(s_launchVec, s_impactVec, speed, arc)
return s_proj
endfunction
//=======================================================================
// Targetless projectile-firing utility. Passes basic arguments to the
// object.create method and then processes the additional arguments.
//
function FireProjectileXY takes unit source, real x, real y, real speed, real arc, string modelpath returns projectile
call object.create(source, x, y, modelpath).launch(s_launchVec, s_impactVec, speed, arc)
return s_proj
endfunction
//=======================================================================
function SetProjectileVisualsById takes integer unitid, real scale, integer alpha, integer red, integer green, integer blue returns nothing
local integer i=object[unitid]
set a_scale[i]=scale
set a_alpha[i]=alpha
set a_red[i]=red
set a_green[i]=green
set a_blue[i]=blue
set a_visuals[i]=true
endfunction
//=======================================================================
function SetProjectileOffsetsById takes integer unitid, real distance, real angle, real launchz, real impactz returns nothing
local integer i=object[unitid]
if (angle <= PI2) and (angle >= 0) then
set a_angle[i]=angle
endif
set a_distance[i]=distance
set a_launchz[i]=launchz
set a_impactz[i]=impactz
set a_offsets[i]=true
endfunction
endlibrary
JASS:
library SplashingProjectile requires ProjectileUtils
globals
// Configurables
private constant integer MED_DAMAGE_FACTOR = 60 // deals 60% of normal damage
private constant integer MIN_DAMAGE_FACTOR = 20 // deals 20% of normal damage
private constant integer MED_DAMAGE_POINT = 25 // med. damage applied for units past 25% of radius
private constant integer MIN_DAMAGE_POINT = 50 // min. damage applied for units past 50% of radius
// Data storage
private unit s_source
private unit s_dummy
private real s_radius
private real s_damage
private real array a_radius
private string array a_fx
endglobals
//=======================================================================
// Configurable damage function.
//
private function inflict takes real damage returns nothing
call UnitDamageTarget(s_source, GetFilterUnit(), damage, false, false, null, null, null)
endfunction
//=======================================================================
private function enum takes nothing returns boolean
if GetWidgetLife(GetFilterUnit()) > 0.405 then
if (IsUnitInRange(GetFilterUnit(), s_dummy, s_radius * MED_DAMAGE_POINT)) then
call inflict(s_damage)
elseif (IsUnitInRange(GetFilterUnit(), s_dummy, s_radius * MIN_DAMAGE_POINT)) then
call inflict(s_damage * MED_DAMAGE_FACTOR)
elseif (IsUnitInRange(GetFilterUnit(), s_dummy, s_radius)) then
call inflict(s_damage * MIN_DAMAGE_FACTOR)
endif
endif
return false
endfunction
//=======================================================================
private function splash takes projectile proj returns nothing
set s_source=proj.source
set s_radius=a_radius[proj]
set s_damage=GetProjectileDamage(proj)
set s_dummy=proj.toUnit
call DestroyEffect(AddSpecialEffect(a_fx[proj], proj.x, proj.y))
call GroupEnumUnitsInRange(bj_lastCreatedGroup, proj.x, proj.y, s_radius + 90.0, Filter(function enum))
endfunction
//=======================================================================
function SplashingProjectile takes projectile proj, real damage, real radius, string fx returns nothing
set a_fx[proj]=fx
set a_radius[proj]=radius
call SetProjectileDamage(proj, damage)
call SetProjectileImpactEvent(proj, splash)
endfunction
endlibrary
Example use of SplashingProjectile:
JASS:
library Example initializer init requires SplashingProjectile, SpellEffectEvent
// Example response to fire a missile
private function OnCast takes nothing returns boolean
// Fire it
call FireProjectileXY(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY(), 600.0, 0.20, "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl")
// Make it a splashing projectile
call SplashingProjectile(GetLastCreatedProjectile(), 100.0, 150.0, "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl")
return false
endfunction
// Initialize
private function init takes nothing returns nothing
call RegisterSpellEffectEvent(Condition(function OnCast), 'A000')
endfunction
endlibrary
Last edited: