2017-07-26 08:35:44 +00:00
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//
// afxMagicMissile is a heavily modified variation of the stock Projectile class. In
// addition to numerous AFX customizations, it also incorporates functionality based on
// the following TGE resources:
//
// Guided or Seeker Projectiles by Derk Adams
// http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=6778
//
// Projectile Ballistic Coefficients (drag factors) by Mark Owen
// http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=5128
//
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
# include "afx/arcaneFX.h"
# include "scene/sceneRenderState.h"
# include "scene/sceneManager.h"
# include "core/resourceManager.h"
# include "ts/tsShapeInstance.h"
# include "sfx/sfxTrack.h"
# include "sfx/sfxSource.h"
# include "sfx/sfxSystem.h"
# include "sfx/sfxTypes.h"
# include "math/mathUtils.h"
# include "math/mathIO.h"
# include "sim/netConnection.h"
# include "T3D/fx/particleEmitter.h"
# include "T3D/fx/splash.h"
# include "T3D/physics/physicsPlugin.h"
# include "T3D/physics/physicsWorld.h"
# include "gfx/gfxTransformSaver.h"
# include "T3D/containerQuery.h"
# include "T3D/lightDescription.h"
# include "console/engineAPI.h"
# include "lighting/lightManager.h"
# include "afx/util/afxEase.h"
# include "afx/afxMagicMissile.h"
# include "afx/afxMagicSpell.h"
# include "afx/afxChoreographer.h"
class ObjectDeleteEvent : public SimEvent
{
public :
void process ( SimObject * object )
{
object - > deleteObject ( ) ;
}
} ;
IMPLEMENT_CO_DATABLOCK_V1 ( afxMagicMissileData ) ;
ConsoleDocClass ( afxMagicMissileData ,
" @brief Defines a particular magic-missile type. (Use with afxMagicSpellData.) \n "
" @tsexample \n "
" datablock afxMagicMissileData(Fireball_MM) \n "
" { \n "
" muzzleVelocity = 50; \n "
" velInheritFactor = 0; \n "
" lifetime = 20000; \n "
" isBallistic = true; \n "
" ballisticCoefficient = 0.85; \n "
" gravityMod = 0.05; \n "
" isGuided = true; \n "
" precision = 30; \n "
" trackDelay = 7; \n "
" launchOffset = \" 0 0 43.7965 \" ; \n "
" launchOnServerSignal = true; \n "
" }; \n "
" @endtsexample \n "
" @ingroup AFX \n "
) ;
IMPLEMENT_CO_NETOBJECT_V1 ( afxMagicMissile ) ;
ConsoleDocClass ( afxMagicMissile ,
" @brief Magic-missile class used internally by afxMagicSpell. Properties of individual missile types are defined using afxMagicMissileData. \n "
" @ingroup AFX \n "
) ;
/* From stock Projectile code...
IMPLEMENT_CALLBACK ( ProjectileData , onExplode , void , ( Projectile * proj , Point3F pos , F32 fade ) ,
( proj , pos , fade ) ,
" Called when a projectile explodes. \n "
" @param proj The projectile exploding. \n "
" @param pos The position of the explosion. \n "
" @param fade The currently fadeValue of the projectile, affects its visibility. \n "
" @see Projectile, ProjectileData \n "
) ;
IMPLEMENT_CALLBACK ( ProjectileData , onCollision , void , ( Projectile * proj , SceneObject * col , F32 fade , Point3F pos , Point3F normal ) ,
( proj , col , fade , pos , normal ) ,
" Called when a projectile collides with another object. \n "
" @param proj The projectile colliding. \n "
" @param col The object hit by the projectile. \n "
" @param fade The current fadeValue of the projectile, affects its visibility. \n "
" @param pos The collision position. \n "
" @param normal The collision normal. \n "
" @see Projectile, ProjectileData \n "
) ;
const U32 Projectile : : csmStaticCollisionMask = TerrainObjectType |
InteriorObjectType |
StaticObjectType ;
const U32 Projectile : : csmDynamicCollisionMask = PlayerObjectType |
VehicleObjectType |
DamagableItemObjectType ;
const U32 Projectile : : csmDamageableMask = Projectile : : csmDynamicCollisionMask ;
U32 Projectile : : smProjectileWarpTicks = 5 ;
*/
//--------------------------------------------------------------------------
//
afxMagicMissileData : : afxMagicMissileData ( )
{
projectileShapeName = ST_NULLSTRING ;
sound = NULL ;
/* From stock Projectile code...
explosion = NULL ;
explosionId = 0 ;
waterExplosion = NULL ;
waterExplosionId = 0 ;
*/
/* From stock Projectile code...
faceViewer = false ;
*/
scale . set ( 1.0f , 1.0f , 1.0f ) ;
isBallistic = false ;
/* From stock Projectile code...
velInheritFactor = 1.0f ;
*/
muzzleVelocity = 50 ;
/* From stock Projectile code...
impactForce = 0.0f ;
armingDelay = 0 ;
fadeDelay = 20000 / 32 ;
lifetime = 20000 / 32 ;
activateSeq = - 1 ;
maintainSeq = - 1 ;
*/
gravityMod = 1.0 ;
/* From stock Projectile code...
bounceElasticity = 0.999f ;
bounceFriction = 0.3f ;
*/
particleEmitter = NULL ;
particleEmitterId = 0 ;
particleWaterEmitter = NULL ;
particleWaterEmitterId = 0 ;
splash = NULL ;
splashId = 0 ;
/* From stock Projectile code...
decal = NULL ;
decalId = 0 ;
*/
lightDesc = NULL ;
lightDescId = 0 ;
starting_vel_vec . zero ( ) ;
isGuided = false ;
precision = 0 ;
trackDelay = 0 ;
ballisticCoefficient = 1.0f ;
followTerrain = false ;
followTerrainHeight = 0.1f ;
followTerrainAdjustRate = 20.0f ;
followTerrainAdjustDelay = 0 ;
lifetime = MaxLifetimeTicks ;
collision_mask = arcaneFX : : sMissileCollisionMask ;
acceleration = 0 ;
accelDelay = 0 ;
accelLifetime = 0 ;
launch_node = ST_NULLSTRING ;
launch_offset . zero ( ) ;
launch_offset_server . zero ( ) ;
launch_offset_client . zero ( ) ;
launch_node_offset . zero ( ) ;
launch_pitch = 0 ;
launch_pan = 0 ;
launch_cons_s_spec = ST_NULLSTRING ;
launch_cons_c_spec = ST_NULLSTRING ;
echo_launch_offset = false ;
wiggle_axis_string = ST_NULLSTRING ;
wiggle_num_axis = 0 ;
wiggle_axis = 0 ;
hover_altitude = 0 ;
hover_attack_distance = 0 ;
hover_attack_gradient = 0 ;
hover_time = 0 ;
reverse_targeting = false ;
caster_safety_time = U32_MAX ;
}
afxMagicMissileData : : afxMagicMissileData ( const afxMagicMissileData & other , bool temp_clone ) : GameBaseData ( other , temp_clone )
{
projectileShapeName = other . projectileShapeName ;
projectileShape = other . projectileShape ; // -- TSShape loads using projectileShapeName
sound = other . sound ;
splash = other . splash ;
splashId = other . splashId ; // -- for pack/unpack of splash ptr
lightDesc = other . lightDesc ;
lightDescId = other . lightDescId ; // -- for pack/unpack of lightDesc ptr
scale = other . scale ;
isBallistic = other . isBallistic ;
muzzleVelocity = other . muzzleVelocity ;
gravityMod = other . gravityMod ;
particleEmitter = other . particleEmitter ;
particleEmitterId = other . particleEmitterId ; // -- for pack/unpack of particleEmitter ptr
particleWaterEmitter = other . particleWaterEmitter ;
particleWaterEmitterId = other . particleWaterEmitterId ; // -- for pack/unpack of particleWaterEmitter ptr
collision_mask = other . collision_mask ;
starting_vel_vec = other . starting_vel_vec ;
isGuided = other . isGuided ;
precision = other . precision ;
trackDelay = other . trackDelay ;
ballisticCoefficient = other . ballisticCoefficient ;
followTerrain = other . followTerrain ;
followTerrainHeight = other . followTerrainHeight ;
followTerrainAdjustRate = other . followTerrainAdjustRate ;
followTerrainAdjustDelay = other . followTerrainAdjustDelay ;
lifetime = other . lifetime ;
fadeDelay = other . fadeDelay ;
acceleration = other . acceleration ;
accelDelay = other . accelDelay ;
accelLifetime = other . accelLifetime ;
launch_node = other . launch_node ;
launch_offset = other . launch_offset ;
launch_offset_server = other . launch_offset_server ;
launch_offset_client = other . launch_offset_client ;
launch_node_offset = other . launch_node_offset ;
launch_pitch = other . launch_pitch ;
launch_pan = other . launch_pan ;
launch_cons_s_spec = other . launch_cons_s_spec ;
launch_cons_c_spec = other . launch_cons_c_spec ;
launch_cons_s_def = other . launch_cons_s_def ;
launch_cons_c_def = other . launch_cons_c_def ;
echo_launch_offset = other . echo_launch_offset ;
wiggle_magnitudes = other . wiggle_magnitudes ;
wiggle_speeds = other . wiggle_speeds ;
wiggle_axis_string = other . wiggle_axis_string ;
wiggle_num_axis = other . wiggle_num_axis ;
wiggle_axis = other . wiggle_axis ;
hover_altitude = other . hover_altitude ;
hover_attack_distance = other . hover_attack_distance ;
hover_attack_gradient = other . hover_attack_gradient ;
hover_time = other . hover_time ;
reverse_targeting = other . reverse_targeting ;
caster_safety_time = other . caster_safety_time ;
}
afxMagicMissileData : : ~ afxMagicMissileData ( )
{
if ( wiggle_axis )
delete [ ] wiggle_axis ;
}
afxMagicMissileData * afxMagicMissileData : : cloneAndPerformSubstitutions ( const SimObject * owner , S32 index )
{
if ( ! owner | | getSubstitutionCount ( ) = = 0 )
return this ;
afxMagicMissileData * sub_missile_db = new afxMagicMissileData ( * this , true ) ;
performSubstitutions ( sub_missile_db , owner , index ) ;
return sub_missile_db ;
}
//--------------------------------------------------------------------------
# define myOffset(field) Offset(field, afxMagicMissileData)
FRangeValidator muzzleVelocityValidator ( 0 , 10000 ) ;
FRangeValidator missilePrecisionValidator ( 0.f , 100.f ) ;
FRangeValidator missileTrackDelayValidator ( 0 , 100000 ) ;
FRangeValidator missileBallisticCoefficientValidator ( 0 , 1 ) ;
void afxMagicMissileData : : initPersistFields ( )
{
static IRangeValidatorScaled ticksFromMS ( TickMs , 0 , MaxLifetimeTicks ) ;
addField ( " particleEmitter " , TYPEID < ParticleEmitterData > ( ) , Offset ( particleEmitter , afxMagicMissileData ) ) ;
addField ( " particleWaterEmitter " , TYPEID < ParticleEmitterData > ( ) , Offset ( particleWaterEmitter , afxMagicMissileData ) ) ;
addField ( " projectileShapeName " , TypeFilename , Offset ( projectileShapeName , afxMagicMissileData ) ) ;
addField ( " scale " , TypePoint3F , Offset ( scale , afxMagicMissileData ) ) ;
addField ( " sound " , TypeSFXTrackName , Offset ( sound , afxMagicMissileData ) ) ;
/* From stock Projectile code...
addField ( " explosion " , TYPEID < ExplosionData > ( ) , Offset ( explosion , ProjectileData ) ) ;
addField ( " waterExplosion " , TYPEID < ExplosionData > ( ) , Offset ( waterExplosion , ProjectileData ) ) ;
*/
addField ( " splash " , TYPEID < SplashData > ( ) , Offset ( splash , afxMagicMissileData ) ) ;
/* From stock Projectile code...
addField ( " decal " , TYPEID < DecalData > ( ) , Offset ( decal , ProjectileData ) ) ;
*/
addField ( " lightDesc " , TYPEID < LightDescription > ( ) , Offset ( lightDesc , afxMagicMissileData ) ) ;
addField ( " isBallistic " , TypeBool , Offset ( isBallistic , afxMagicMissileData ) ) ;
/* From stock Projectile code...
addField ( " velInheritFactor " , TypeF32 , Offset ( velInheritFactor , ProjectileData ) ) ;
*/
addNamedFieldV ( muzzleVelocity , TypeF32 , afxMagicMissileData , & muzzleVelocityValidator ) ;
/* From stock Projectile code...
addField ( " impactForce " , TypeF32 , Offset ( impactForce , ProjectileData ) ) ;
*/
addNamedFieldV ( lifetime , TypeS32 , afxMagicMissileData , & ticksFromMS ) ;
/* From stock Projectile code...
addProtectedField ( " armingDelay " , TypeS32 , Offset ( armingDelay , ProjectileData ) , & setArmingDelay , & getScaledValue ,
" The time in milliseconds before the projectile is armed and will cause damage or explode on impact. " ) ;
addProtectedField ( " fadeDelay " , TypeS32 , Offset ( fadeDelay , ProjectileData ) , & setFadeDelay , & getScaledValue ,
" The time in milliseconds when the projectile begins to fade out. Must be less than the lifetime to have an effect. " ) ;
addField ( " bounceElasticity " , TypeF32 , Offset ( bounceElasticity , ProjectileData ) ) ;
addField ( " bounceFriction " , TypeF32 , Offset ( bounceFriction , ProjectileData ) ) ;
*/
addField ( " gravityMod " , TypeF32 , Offset ( gravityMod , afxMagicMissileData ) ) ;
// FIELDS ADDED BY MAGIC-MISSILE
addField ( " missileShapeName " , TypeFilename , myOffset ( projectileShapeName ) ) ;
addField ( " missileShapeScale " , TypePoint3F , myOffset ( scale ) ) ;
addField ( " startingVelocityVector " , TypePoint3F , myOffset ( starting_vel_vec ) ) ;
addNamedField ( isGuided , TypeBool , afxMagicMissileData ) ;
addNamedFieldV ( precision , TypeF32 , afxMagicMissileData , & missilePrecisionValidator ) ;
addNamedFieldV ( trackDelay , TypeS32 , afxMagicMissileData , & missileTrackDelayValidator ) ;
addNamedFieldV ( ballisticCoefficient , TypeF32 , afxMagicMissileData , & missileBallisticCoefficientValidator ) ;
addField ( " collisionMask " , TypeS32 , myOffset ( collision_mask ) ) ;
addField ( " followTerrain " , TypeBool , myOffset ( followTerrain ) ) ;
addField ( " followTerrainHeight " , TypeF32 , myOffset ( followTerrainHeight ) ) ;
addField ( " followTerrainAdjustRate " , TypeF32 , myOffset ( followTerrainAdjustRate ) ) ;
addFieldV ( " followTerrainAdjustDelay " , TypeS32 , myOffset ( followTerrainAdjustDelay ) , & ticksFromMS ) ;
addNamedField ( acceleration , TypeF32 , afxMagicMissileData ) ;
addNamedFieldV ( accelDelay , TypeS32 , afxMagicMissileData , & ticksFromMS ) ;
addNamedFieldV ( accelLifetime , TypeS32 , afxMagicMissileData , & ticksFromMS ) ;
addField ( " launchNode " , TypeString , myOffset ( launch_node ) ) ;
addField ( " launchOffset " , TypePoint3F , myOffset ( launch_offset ) ) ;
addField ( " launchOffsetServer " , TypePoint3F , myOffset ( launch_offset_server ) ) ;
addField ( " launchOffsetClient " , TypePoint3F , myOffset ( launch_offset_client ) ) ;
addField ( " launchNodeOffset " , TypePoint3F , myOffset ( launch_node_offset ) ) ;
addField ( " launchAimPitch " , TypeF32 , myOffset ( launch_pitch ) ) ;
addField ( " launchAimPan " , TypeF32 , myOffset ( launch_pan ) ) ;
addField ( " launchConstraintServer " , TypeString , myOffset ( launch_cons_s_spec ) ) ;
addField ( " launchConstraintClient " , TypeString , myOffset ( launch_cons_c_spec ) ) ;
//
addField ( " echoLaunchOffset " , TypeBool , myOffset ( echo_launch_offset ) ) ;
addField ( " wiggleMagnitudes " , TypeF32Vector , myOffset ( wiggle_magnitudes ) ) ;
addField ( " wiggleSpeeds " , TypeF32Vector , myOffset ( wiggle_speeds ) ) ;
addField ( " wiggleAxis " , TypeString , myOffset ( wiggle_axis_string ) ) ;
addField ( " hoverAltitude " , TypeF32 , myOffset ( hover_altitude ) ) ;
addField ( " hoverAttackDistance " , TypeF32 , myOffset ( hover_attack_distance ) ) ;
addField ( " hoverAttackGradient " , TypeF32 , myOffset ( hover_attack_gradient ) ) ;
addFieldV ( " hoverTime " , TypeS32 , myOffset ( hover_time ) , & ticksFromMS ) ;
addField ( " reverseTargeting " , TypeBool , myOffset ( reverse_targeting ) ) ;
addFieldV ( " casterSafetyTime " , TypeS32 , myOffset ( caster_safety_time ) , & ticksFromMS ) ;
Parent : : initPersistFields ( ) ;
// disallow some field substitutions
onlyKeepClearSubstitutions ( " particleEmitter " ) ; // subs resolving to "~~", or "~0" are OK
onlyKeepClearSubstitutions ( " particleWaterEmitter " ) ;
onlyKeepClearSubstitutions ( " sound " ) ;
onlyKeepClearSubstitutions ( " splash " ) ;
}
//--------------------------------------------------------------------------
bool afxMagicMissileData : : onAdd ( )
{
if ( ! Parent : : onAdd ( ) )
return false ;
// ADDED BY MAGIC-MISSILE
// Wiggle axes ////////////////////////////////////////////////////////////
if ( wiggle_axis_string ! = ST_NULLSTRING & & wiggle_num_axis = = 0 )
{
// Tokenize input string and convert to Point3F array
//
Vector < char * > dataBlocks ( __FILE__ , __LINE__ ) ;
// make a copy of points_string
2018-03-09 01:59:40 +00:00
dsize_t tokCopyLen = dStrlen ( wiggle_axis_string ) + 1 ;
char * tokCopy = new char [ tokCopyLen ] ;
dStrcpy ( tokCopy , wiggle_axis_string , tokCopyLen ) ;
2017-07-26 08:35:44 +00:00
// extract tokens one by one, adding them to dataBlocks
char * currTok = dStrtok ( tokCopy , " \t " ) ;
while ( currTok ! = NULL )
{
dataBlocks . push_back ( currTok ) ;
currTok = dStrtok ( NULL , " \t " ) ;
}
// bail if there were no tokens in the string
if ( dataBlocks . size ( ) = = 0 )
{
Con : : warnf ( ConsoleLogEntry : : General , " afxMagicMissileData(%s) invalid wiggle axis string. No tokens found " , getName ( ) ) ;
delete [ ] tokCopy ;
return false ;
}
// Find wiggle_num_axis (round up to multiple of 3) // WARNING here if not multiple of 3?
for ( U32 i = 0 ; i < dataBlocks . size ( ) % 3 ; i + + )
{
dataBlocks . push_back ( " 0.0 " ) ;
}
wiggle_num_axis = dataBlocks . size ( ) / 3 ;
wiggle_axis = new Point3F [ wiggle_num_axis ] ;
U32 p_i = 0 ;
for ( U32 i = 0 ; i < dataBlocks . size ( ) ; i + = 3 , p_i + + )
{
F32 x , y , z ;
x = dAtof ( dataBlocks [ i ] ) ; // What about overflow?
y = dAtof ( dataBlocks [ i + 1 ] ) ;
z = dAtof ( dataBlocks [ i + 2 ] ) ;
wiggle_axis [ p_i ] . set ( x , y , z ) ;
wiggle_axis [ p_i ] . normalizeSafe ( ) ; // sufficient????
}
delete [ ] tokCopy ;
}
launch_cons_s_def . parseSpec ( launch_cons_s_spec , true , false ) ;
launch_cons_c_def . parseSpec ( launch_cons_c_spec , false , true ) ;
return true ;
}
bool afxMagicMissileData : : preload ( bool server , String & errorStr )
{
if ( Parent : : preload ( server , errorStr ) = = false )
return false ;
if ( ! server )
{
if ( ! particleEmitter & & particleEmitterId ! = 0 )
if ( Sim : : findObject ( particleEmitterId , particleEmitter ) = = false )
Con : : errorf ( ConsoleLogEntry : : General , " afxMagicMissileData::preload: Invalid packet, bad datablockId(particleEmitter): %d " , particleEmitterId ) ;
if ( ! particleWaterEmitter & & particleWaterEmitterId ! = 0 )
if ( Sim : : findObject ( particleWaterEmitterId , particleWaterEmitter ) = = false )
Con : : errorf ( ConsoleLogEntry : : General , " afxMagicMissileData::preload: Invalid packet, bad datablockId(particleWaterEmitter): %d " , particleWaterEmitterId ) ;
/* From stock Projectile code...
if ( ! explosion & & explosionId ! = 0 )
if ( Sim : : findObject ( explosionId , explosion ) = = false )
Con : : errorf ( ConsoleLogEntry : : General , " ProjectileData::preload: Invalid packet, bad datablockId(explosion): %d " , explosionId ) ;
if ( ! waterExplosion & & waterExplosionId ! = 0 )
if ( Sim : : findObject ( waterExplosionId , waterExplosion ) = = false )
Con : : errorf ( ConsoleLogEntry : : General , " ProjectileData::preload: Invalid packet, bad datablockId(waterExplosion): %d " , waterExplosionId ) ;
*/
if ( ! splash & & splashId ! = 0 )
if ( Sim : : findObject ( splashId , splash ) = = false )
Con : : errorf ( ConsoleLogEntry : : General , " afxMagicMissileData::preload: Invalid packet, bad datablockId(splash): %d " , splashId ) ;
/* From stock Projectile code...
if ( ! decal & & decalId ! = 0 )
if ( Sim : : findObject ( decalId , decal ) = = false )
Con : : errorf ( ConsoleLogEntry : : General , " ProjectileData::preload: Invalid packet, bad datablockId(decal): %d " , decalId ) ;
*/
String errorStr ;
if ( ! sfxResolve ( & sound , errorStr ) )
Con : : errorf ( ConsoleLogEntry : : General , " afxMagicMissileData::preload: Invalid packet: %s " , errorStr . c_str ( ) ) ;
if ( ! lightDesc & & lightDescId ! = 0 )
if ( Sim : : findObject ( lightDescId , lightDesc ) = = false )
Con : : errorf ( ConsoleLogEntry : : General , " afxMagicMissileData::preload: Invalid packet, bad datablockid(lightDesc): %d " , lightDescId ) ;
}
if ( projectileShapeName ! = ST_NULLSTRING )
{
projectileShape = ResourceManager : : get ( ) . load ( projectileShapeName ) ;
if ( bool ( projectileShape ) = = false )
{
errorStr = String : : ToString ( " afxMagicMissileData::load: Couldn't load shape \" %s \" " , projectileShapeName ) ;
return false ;
}
/* From stock Projectile code...
activateSeq = projectileShape - > findSequence ( " activate " ) ;
maintainSeq = projectileShape - > findSequence ( " maintain " ) ;
*/
}
if ( bool ( projectileShape ) ) // create an instance to preload shape data
{
TSShapeInstance * pDummy = new TSShapeInstance ( projectileShape , ! server ) ;
delete pDummy ;
}
return true ;
}
//--------------------------------------------------------------------------
// Modified from floorPlanRes.cc
// Read a vector of items
template < class T >
bool readVector ( Vector < T > & vec , Stream & stream , const char * msg )
{
U32 num , i ;
bool Ok = true ;
stream . read ( & num ) ;
vec . setSize ( num ) ;
for ( i = 0 ; i < num & & Ok ; i + + ) {
Ok = stream . read ( & vec [ i ] ) ;
AssertISV ( Ok , avar ( " math vec read error (%s) on elem %d " , msg , i ) ) ;
}
return Ok ;
}
// Write a vector of items
template < class T >
bool writeVector ( const Vector < T > & vec , Stream & stream , const char * msg )
{
bool Ok = true ;
stream . write ( vec . size ( ) ) ;
for ( U32 i = 0 ; i < vec . size ( ) & & Ok ; i + + ) {
Ok = stream . write ( vec [ i ] ) ;
AssertISV ( Ok , avar ( " vec write error (%s) on elem %d " , msg , i ) ) ;
}
return Ok ;
}
//--------------------------------------------------------------------------
void afxMagicMissileData : : packData ( BitStream * stream )
{
Parent : : packData ( stream ) ;
stream - > writeString ( projectileShapeName ) ;
/* From stock Projectile code...
stream - > writeFlag ( faceViewer ) ;
*/
if ( stream - > writeFlag ( scale . x ! = 1 | | scale . y ! = 1 | | scale . z ! = 1 ) )
{
stream - > write ( scale . x ) ;
stream - > write ( scale . y ) ;
stream - > write ( scale . z ) ;
}
stream - > write ( collision_mask ) ;
if ( stream - > writeFlag ( particleEmitter ! = NULL ) )
stream - > writeRangedU32 ( particleEmitter - > getId ( ) , DataBlockObjectIdFirst ,
DataBlockObjectIdLast ) ;
if ( stream - > writeFlag ( particleWaterEmitter ! = NULL ) )
stream - > writeRangedU32 ( particleWaterEmitter - > getId ( ) , DataBlockObjectIdFirst ,
DataBlockObjectIdLast ) ;
/* From stock Projectile code...
if ( stream - > writeFlag ( explosion ! = NULL ) )
stream - > writeRangedU32 ( explosion - > getId ( ) , DataBlockObjectIdFirst ,
DataBlockObjectIdLast ) ;
if ( stream - > writeFlag ( waterExplosion ! = NULL ) )
stream - > writeRangedU32 ( waterExplosion - > getId ( ) , DataBlockObjectIdFirst ,
DataBlockObjectIdLast ) ;
*/
if ( stream - > writeFlag ( splash ! = NULL ) )
stream - > writeRangedU32 ( splash - > getId ( ) , DataBlockObjectIdFirst ,
DataBlockObjectIdLast ) ;
/* From stock Projectile code...
if ( stream - > writeFlag ( decal ! = NULL ) )
stream - > writeRangedU32 ( decal - > getId ( ) , DataBlockObjectIdFirst ,
DataBlockObjectIdLast ) ;
*/
sfxWrite ( stream , sound ) ;
if ( stream - > writeFlag ( lightDesc ! = NULL ) )
stream - > writeRangedU32 ( lightDesc - > getId ( ) , DataBlockObjectIdFirst ,
DataBlockObjectIdLast ) ;
/* From stock Projectile code...
stream - > write ( impactForce ) ;
*/
stream - > write ( lifetime ) ;
/* From stock Projectile code...
stream - > write ( armingDelay ) ;
stream - > write ( fadeDelay ) ;
*/
if ( stream - > writeFlag ( isBallistic ) )
{
stream - > write ( gravityMod ) ;
/* From stock Projectile code...
stream - > write ( bounceElasticity ) ;
stream - > write ( bounceFriction ) ;
*/
stream - > write ( ballisticCoefficient ) ;
}
if ( stream - > writeFlag ( isGuided ) )
{
stream - > write ( precision ) ;
stream - > write ( trackDelay ) ;
}
stream - > write ( muzzleVelocity ) ;
mathWrite ( * stream , starting_vel_vec ) ;
stream - > write ( acceleration ) ;
stream - > write ( accelDelay ) ;
stream - > write ( accelLifetime ) ;
stream - > writeString ( launch_node ) ;
mathWrite ( * stream , launch_offset ) ;
mathWrite ( * stream , launch_offset_server ) ;
mathWrite ( * stream , launch_offset_client ) ;
mathWrite ( * stream , launch_node_offset ) ;
stream - > write ( launch_pitch ) ;
stream - > write ( launch_pan ) ;
stream - > writeString ( launch_cons_c_spec ) ;
stream - > writeFlag ( echo_launch_offset ) ;
writeVector ( wiggle_magnitudes , * stream , " afxMagicMissile: wiggle_magnitudes " ) ;
writeVector ( wiggle_speeds , * stream , " afxMagicMissile: wiggle_speeds " ) ;
stream - > write ( wiggle_num_axis ) ;
for ( U32 i = 0 ; i < wiggle_num_axis ; i + + )
mathWrite ( * stream , wiggle_axis [ i ] ) ;
stream - > write ( hover_altitude ) ;
stream - > write ( hover_attack_distance ) ;
stream - > write ( hover_attack_gradient ) ;
stream - > writeRangedU32 ( hover_time , 0 , MaxLifetimeTicks ) ;
stream - > writeFlag ( reverse_targeting ) ;
stream - > write ( caster_safety_time ) ;
}
void afxMagicMissileData : : unpackData ( BitStream * stream )
{
Parent : : unpackData ( stream ) ;
projectileShapeName = stream - > readSTString ( ) ;
/* From stock Projectile code...
faceViewer = stream - > readFlag ( ) ;
*/
if ( stream - > readFlag ( ) )
{
stream - > read ( & scale . x ) ;
stream - > read ( & scale . y ) ;
stream - > read ( & scale . z ) ;
}
else
scale . set ( 1 , 1 , 1 ) ;
stream - > read ( & collision_mask ) ;
if ( stream - > readFlag ( ) )
particleEmitterId = stream - > readRangedU32 ( DataBlockObjectIdFirst , DataBlockObjectIdLast ) ;
if ( stream - > readFlag ( ) )
particleWaterEmitterId = stream - > readRangedU32 ( DataBlockObjectIdFirst , DataBlockObjectIdLast ) ;
/* From stock Projectile code...
if ( stream - > readFlag ( ) )
explosionId = stream - > readRangedU32 ( DataBlockObjectIdFirst , DataBlockObjectIdLast ) ;
if ( stream - > readFlag ( ) )
waterExplosionId = stream - > readRangedU32 ( DataBlockObjectIdFirst , DataBlockObjectIdLast ) ;
*/
if ( stream - > readFlag ( ) )
splashId = stream - > readRangedU32 ( DataBlockObjectIdFirst , DataBlockObjectIdLast ) ;
/* From stock Projectile code...
if ( stream - > readFlag ( ) )
decalId = stream - > readRangedU32 ( DataBlockObjectIdFirst , DataBlockObjectIdLast ) ;
*/
sfxRead ( stream , & sound ) ;
if ( stream - > readFlag ( ) )
lightDescId = stream - > readRangedU32 ( DataBlockObjectIdFirst , DataBlockObjectIdLast ) ;
/* From stock Projectile code...
stream - > read ( & impactForce ) ;
*/
stream - > read ( & lifetime ) ;
/* From stock Projectile code...
stream - > read ( & armingDelay ) ;
stream - > read ( & fadeDelay ) ;
*/
isBallistic = stream - > readFlag ( ) ;
if ( isBallistic )
{
stream - > read ( & gravityMod ) ;
/* From stock Projectile code...
stream - > read ( & bounceElasticity ) ;
stream - > read ( & bounceFriction ) ;
*/
stream - > read ( & ballisticCoefficient ) ;
}
isGuided = stream - > readFlag ( ) ;
if ( isGuided )
{
stream - > read ( & precision ) ;
stream - > read ( & trackDelay ) ;
}
stream - > read ( & muzzleVelocity ) ;
mathRead ( * stream , & starting_vel_vec ) ;
stream - > read ( & acceleration ) ;
stream - > read ( & accelDelay ) ;
stream - > read ( & accelLifetime ) ;
launch_node = stream - > readSTString ( ) ;
mathRead ( * stream , & launch_offset ) ;
mathRead ( * stream , & launch_offset_server ) ;
mathRead ( * stream , & launch_offset_client ) ;
mathRead ( * stream , & launch_node_offset ) ;
stream - > read ( & launch_pitch ) ;
stream - > read ( & launch_pan ) ;
launch_cons_c_spec = stream - > readSTString ( ) ;
echo_launch_offset = stream - > readFlag ( ) ;
readVector ( wiggle_magnitudes , * stream , " afxMagicMissile: wiggle_magnitudes " ) ;
readVector ( wiggle_speeds , * stream , " afxMagicMissile: wiggle_speeds " ) ;
if ( wiggle_axis )
delete [ ] wiggle_axis ;
wiggle_axis = 0 ;
wiggle_num_axis = 0 ;
stream - > read ( & wiggle_num_axis ) ;
if ( wiggle_num_axis > 0 )
{
wiggle_axis = new Point3F [ wiggle_num_axis ] ;
for ( U32 i = 0 ; i < wiggle_num_axis ; i + + )
mathRead ( * stream , & wiggle_axis [ i ] ) ;
}
stream - > read ( & hover_altitude ) ;
stream - > read ( & hover_attack_distance ) ;
stream - > read ( & hover_attack_gradient ) ;
hover_time = stream - > readRangedU32 ( 0 , MaxLifetimeTicks ) ;
reverse_targeting = stream - > readFlag ( ) ;
stream - > read ( & caster_safety_time ) ;
}
/* From stock Projectile code...
bool ProjectileData : : setLifetime ( void * obj , const char * index , const char * data )
{
S32 value = dAtoi ( data ) ;
value = scaleValue ( value ) ;
ProjectileData * object = static_cast < ProjectileData * > ( obj ) ;
object - > lifetime = value ;
return false ;
}
bool ProjectileData : : setArmingDelay ( void * obj , const char * index , const char * data )
{
S32 value = dAtoi ( data ) ;
value = scaleValue ( value ) ;
ProjectileData * object = static_cast < ProjectileData * > ( obj ) ;
object - > armingDelay = value ;
return false ;
}
bool ProjectileData : : setFadeDelay ( void * obj , const char * index , const char * data )
{
S32 value = dAtoi ( data ) ;
value = scaleValue ( value ) ;
ProjectileData * object = static_cast < ProjectileData * > ( obj ) ;
object - > fadeDelay = value ;
return false ;
}
const char * ProjectileData : : getScaledValue ( void * obj , const char * data )
{
S32 value = dAtoi ( data ) ;
value = scaleValue ( value , false ) ;
String stringData = String : : ToString ( value ) ;
char * strBuffer = Con : : getReturnBuffer ( stringData . size ( ) ) ;
dMemcpy ( strBuffer , stringData , stringData . size ( ) ) ;
return strBuffer ;
}
S32 ProjectileData : : scaleValue ( S32 value , bool down )
{
S32 minV = 0 ;
S32 maxV = Projectile : : MaxLivingTicks ;
// scale down to ticks before we validate
if ( down )
value / = TickMs ;
if ( value < minV | | value > maxV )
{
Con : : errorf ( " ProjectileData::scaleValue(S32 value = %d, bool down = %b) - Scaled value must be between %d and %d " , value , down , minV , maxV ) ;
if ( value < minV )
value = minV ;
else if ( value > maxV )
value = maxV ;
}
// scale up from ticks after we validate
if ( ! down )
value * = TickMs ;
return value ;
}
*/
void afxMagicMissileData : : gather_cons_defs ( Vector < afxConstraintDef > & defs )
{
if ( launch_cons_s_def . isDefined ( ) )
defs . push_back ( launch_cons_s_def ) ;
if ( launch_cons_c_def . isDefined ( ) )
defs . push_back ( launch_cons_c_def ) ;
} ;
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// afxMagicMissile
afxMagicMissile : : afxMagicMissile ( )
{
init ( true , true ) ;
}
afxMagicMissile : : afxMagicMissile ( bool on_server , bool on_client )
{
init ( on_server , on_client ) ;
}
//--------------------------------------------------------------------------
//--------------------------------------
//
void afxMagicMissile : : init ( bool on_server , bool on_client )
{
mPhysicsWorld = NULL ;
mTypeMask | = ProjectileObjectType | LightObjectType ;
mLight = LightManager : : createLightInfo ( ) ;
mLight - > setType ( LightInfo : : Point ) ;
mLightState . clear ( ) ;
mLightState . setLightInfo ( mLight ) ;
mCurrPosition . zero ( ) ;
mCurrVelocity . set ( 0 , 0 , 1 ) ;
mCurrTick = 0 ;
mParticleEmitter = NULL ;
mParticleWaterEmitter = NULL ;
mSound = 0 ;
mProjectileShape = NULL ;
mDataBlock = NULL ;
choreographer = NULL ;
if ( on_server ! = on_client )
{
client_only = on_client ;
server_only = on_server ;
mNetFlags . clear ( Ghostable | ScopeAlways ) ;
if ( client_only )
mNetFlags . set ( IsGhost ) ;
}
else
{
// note -- setting neither server or client makes no sense so we
// treat as if both are set.
mNetFlags . set ( Ghostable | ScopeAlways ) ;
client_only = server_only = false ;
}
mCurrDeltaBase . zero ( ) ;
mCurrBackDelta . zero ( ) ;
collision_mask = 0 ;
prec_inc = 0.0f ;
did_launch = false ;
did_impact = false ;
missile_target = NULL ;
collide_exempt = NULL ;
use_accel = false ;
hover_attack_go = false ;
hover_attack_tick = 0 ;
starting_velocity = 0.0 ;
starting_vel_vec . zero ( ) ;
ss_object = 0 ;
ss_index = 0 ;
}
afxMagicMissile : : ~ afxMagicMissile ( )
{
SAFE_DELETE ( mLight ) ;
delete mProjectileShape ;
mProjectileShape = NULL ;
}
//--------------------------------------------------------------------------
void afxMagicMissile : : initPersistFields ( )
{
addGroup ( " Physics " ) ;
addField ( " initialPosition " , TypePoint3F , Offset ( mCurrPosition , afxMagicMissile ) ,
" Initial starting position for this missile. " ) ;
addField ( " initialVelocity " , TypePoint3F , Offset ( mCurrVelocity , afxMagicMissile ) ,
" Initial starting velocity for this missile. " ) ;
endGroup ( " Physics " ) ;
/* From stock Projectile code...
addGroup ( " Source " ) ;
addField ( " sourceObject " , TypeS32 , Offset ( mSourceObjectId , Projectile ) , " The object that fires this projectile. If this projectile was fired by a WeaponImage, it will be the object that owns the WeaponImage, usually the player. " ) ;
addField ( " sourceSlot " , TypeS32 , Offset ( mSourceObjectSlot , Projectile ) , " Which weapon slot on the sourceObject that this projectile originates from. " ) ;
endGroup ( " Source " ) ;
*/
Parent : : initPersistFields ( ) ;
}
/* From stock Projectile code...
bool Projectile : : calculateImpact ( float ,
Point3F & pointOfImpact ,
float & impactTime )
{
Con : : warnf ( ConsoleLogEntry : : General , " Projectile::calculateImpact: Should never be called " ) ;
impactTime = 0 ;
pointOfImpact . set ( 0 , 0 , 0 ) ;
return false ;
}
//--------------------------------------------------------------------------
F32 Projectile : : getUpdatePriority ( CameraScopeQuery * camInfo , U32 updateMask , S32 updateSkips )
{
F32 ret = Parent : : getUpdatePriority ( camInfo , updateMask , updateSkips ) ;
// if the camera "owns" this object, it should have a slightly higher priority
if ( mSourceObject = = camInfo - > camera )
return ret + 0.2 ;
return ret ;
}
*/
bool afxMagicMissile : : onAdd ( )
{
if ( ! Parent : : onAdd ( ) )
return false ;
if ( isServerObject ( ) )
{
/* From stock Projectile code...
ShapeBase * ptr ;
if ( Sim : : findObject ( mSourceObjectId , ptr ) )
{
mSourceObject = ptr ;
// Since we later do processAfter( mSourceObject ) we must clearProcessAfter
// if it is deleted. SceneObject already handles this in onDeleteNotify so
// all we need to do is register for the notification.
deleteNotify ( ptr ) ;
}
else
{
if ( mSourceObjectId ! = - 1 )
Con : : errorf ( ConsoleLogEntry : : General , " Projectile::onAdd: mSourceObjectId is invalid " ) ;
mSourceObject = NULL ;
}
// If we're on the server, we need to inherit some of our parent's velocity
//
mCurrTick = 0 ;
*/
}
else
{
if ( bool ( mDataBlock - > projectileShape ) )
{
mProjectileShape = new TSShapeInstance ( mDataBlock - > projectileShape , true ) ;
/* From stock Projectile code...
if ( mDataBlock - > activateSeq ! = - 1 )
{
mActivateThread = mProjectileShape - > addThread ( ) ;
mProjectileShape - > setTimeScale ( mActivateThread , 1 ) ;
mProjectileShape - > setSequence ( mActivateThread , mDataBlock - > activateSeq , 0 ) ;
}
*/
}
if ( mDataBlock - > particleEmitter ! = NULL )
{
ParticleEmitter * pEmitter = new ParticleEmitter ;
//pEmitter->setDataBlock(mDataBlock->particleEmitter->cloneAndPerformSubstitutions(ss_object, ss_index));
pEmitter - > onNewDataBlock ( mDataBlock - > particleEmitter - > cloneAndPerformSubstitutions ( ss_object , ss_index ) , false ) ;
if ( pEmitter - > registerObject ( ) = = false )
{
Con : : warnf ( ConsoleLogEntry : : General , " Could not register particle emitter for particle of class: %s " , mDataBlock - > getName ( ) ) ;
delete pEmitter ;
pEmitter = NULL ;
}
mParticleEmitter = pEmitter ;
}
if ( mDataBlock - > particleWaterEmitter ! = NULL )
{
ParticleEmitter * pEmitter = new ParticleEmitter ;
pEmitter - > onNewDataBlock ( mDataBlock - > particleWaterEmitter - > cloneAndPerformSubstitutions ( ss_object , ss_index ) , false ) ;
if ( pEmitter - > registerObject ( ) = = false )
{
Con : : warnf ( ConsoleLogEntry : : General , " Could not register particle emitter for particle of class: %s " , mDataBlock - > getName ( ) ) ;
delete pEmitter ;
pEmitter = NULL ;
}
mParticleWaterEmitter = pEmitter ;
}
}
/* From stock Projectile code...
if ( mSourceObject . isValid ( ) )
processAfter ( mSourceObject ) ;
*/
// detect for acceleration
use_accel = ( mDataBlock - > acceleration ! = 0 & & mDataBlock - > accelLifetime > 0 ) ;
// Setup our bounding box
if ( bool ( mDataBlock - > projectileShape ) = = true )
2018-03-13 06:05:15 +00:00
mObjBox = mDataBlock - > projectileShape - > mBounds ;
2017-07-26 08:35:44 +00:00
else
mObjBox = Box3F ( Point3F ( 0 , 0 , 0 ) , Point3F ( 0 , 0 , 0 ) ) ;
resetWorldBox ( ) ;
addToScene ( ) ;
if ( PHYSICSMGR )
mPhysicsWorld = PHYSICSMGR - > getWorld ( isServerObject ( ) ? " server " : " client " ) ;
return true ;
}
void afxMagicMissile : : onRemove ( )
{
if ( mParticleEmitter )
{
mParticleEmitter - > deleteWhenEmpty ( ) ;
mParticleEmitter = NULL ;
}
if ( mParticleWaterEmitter )
{
mParticleWaterEmitter - > deleteWhenEmpty ( ) ;
mParticleWaterEmitter = NULL ;
}
SFX_DELETE ( mSound ) ;
removeFromScene ( ) ;
Parent : : onRemove ( ) ;
}
bool afxMagicMissile : : onNewDataBlock ( GameBaseData * dptr , bool reload )
{
mDataBlock = dynamic_cast < afxMagicMissileData * > ( dptr ) ;
if ( ! mDataBlock | | ! Parent : : onNewDataBlock ( dptr , reload ) )
return false ;
starting_velocity = mDataBlock - > muzzleVelocity ;
starting_vel_vec = mDataBlock - > starting_vel_vec ;
collision_mask = mDataBlock - > collision_mask ;
if ( isGhost ( ) )
{
// Create the sound ahead of time. This reduces runtime
// costs and makes the system easier to understand.
SFX_DELETE ( mSound ) ;
if ( mDataBlock - > sound )
mSound = SFX - > createSource ( mDataBlock - > sound ) ;
}
return true ;
}
//--------------------------------------------------------------------------
void afxMagicMissile : : submitLights ( LightManager * lm , bool staticLighting )
{
if ( staticLighting | | isHidden ( ) | | ! mDataBlock - > lightDesc )
return ;
mDataBlock - > lightDesc - > submitLight ( & mLightState , getRenderTransform ( ) , lm , this ) ;
}
bool afxMagicMissile : : pointInWater ( const Point3F & point )
{
// This is pretty much a hack so we can use the existing ContainerQueryInfo
// and findObject router.
// We only care if we intersect with water at all
// so build a box at the point that has only 1 z extent.
// And test if water coverage is anything other than zero.
Box3F boundsBox ( point , point ) ;
boundsBox . maxExtents . z + = 1.0f ;
ContainerQueryInfo info ;
info . box = boundsBox ;
info . mass = 0.0f ;
// Find and retreive physics info from intersecting WaterObject(s)
if ( mContainer ! = NULL )
{
mContainer - > findObjects ( boundsBox , WaterObjectType , findRouter , & info ) ;
}
else
{
// Handle special case where the projectile has exploded prior to having
// called onAdd() on the client. This occurs when the projectile on the
// server is created and then explodes in the same network update tick.
// On the client end in NetConnection::ghostReadPacket() the ghost is
// created and then Projectile::unpackUpdate() is called prior to the
// projectile being registered. Within unpackUpdate() the explosion
// is triggered, but without being registered onAdd() isn't called and
// the container is not set. As all we're doing is checking if the
// given explosion point is within water, we should be able to use the
// global container here. We could likely always get away with this,
// but using the actual defined container when possible is the right
// thing to do. DAW
AssertFatal ( isClientObject ( ) , " Server projectile has not been properly added " ) ;
gClientContainer . findObjects ( boundsBox , WaterObjectType , findRouter , & info ) ;
}
return ( info . waterCoverage > 0.0f ) ;
}
//----------------------------------------------------------------------------
void afxMagicMissile : : emitParticles ( const Point3F & from , const Point3F & to , const Point3F & vel , const U32 ms )
{
/* From stock Projectile code...
if ( isHidden ( ) )
return ;
*/
Point3F axis = - vel ;
if ( axis . isZero ( ) )
axis . set ( 0.0 , 0.0 , 1.0 ) ;
else
axis . normalize ( ) ;
bool fromWater = pointInWater ( from ) ;
bool toWater = pointInWater ( to ) ;
if ( ! fromWater & & ! toWater & & bool ( mParticleEmitter ) ) // not in water
mParticleEmitter - > emitParticles ( from , to , axis , vel , ms ) ;
else if ( fromWater & & toWater & & bool ( mParticleWaterEmitter ) ) // in water
mParticleWaterEmitter - > emitParticles ( from , to , axis , vel , ms ) ;
else if ( ! fromWater & & toWater ) // entering water
{
// cast the ray to get the surface point of the water
RayInfo rInfo ;
if ( gClientContainer . castRay ( from , to , WaterObjectType , & rInfo ) )
{
create_splash ( rInfo . point ) ;
// create an emitter for the particles out of water and the particles in water
if ( mParticleEmitter )
mParticleEmitter - > emitParticles ( from , rInfo . point , axis , vel , ms ) ;
if ( mParticleWaterEmitter )
mParticleWaterEmitter - > emitParticles ( rInfo . point , to , axis , vel , ms ) ;
}
}
else if ( fromWater & & ! toWater ) // leaving water
{
// cast the ray in the opposite direction since that point is out of the water, otherwise
// we hit water immediately and wont get the appropriate surface point
RayInfo rInfo ;
if ( gClientContainer . castRay ( to , from , WaterObjectType , & rInfo ) )
{
create_splash ( rInfo . point ) ;
// create an emitter for the particles out of water and the particles in water
if ( mParticleEmitter )
mParticleEmitter - > emitParticles ( rInfo . point , to , axis , vel , ms ) ;
if ( mParticleWaterEmitter )
mParticleWaterEmitter - > emitParticles ( from , rInfo . point , axis , vel , ms ) ;
}
}
}
void afxMagicMissile : : processTick ( const Move * move )
{
Parent : : processTick ( move ) ;
// only active from launch to impact
if ( ! is_active ( ) )
return ;
mCurrTick + + ;
// missile fizzles out by exceeding lifetime
if ( ( isServerObject ( ) | | client_only ) & & mCurrTick > = mDataBlock - > lifetime )
{
did_impact = true ;
setMaskBits ( ImpactMask ) ;
if ( choreographer )
{
Point3F n = mCurrVelocity ; n . normalizeSafe ( ) ;
choreographer - > impactNotify ( mCurrPosition , n , 0 ) ;
}
Sim : : postEvent ( this , new ObjectDeleteEvent , Sim : : getCurrentTime ( ) + 500 ) ;
return ;
}
static F32 dT = F32 ( TickMs ) * 0.001f ;
Point3F old_pos = mCurrPosition ;
// adjust missile velocity from gravity and drag influences
if ( mDataBlock - > isBallistic )
{
F32 dV = ( 1 - mDataBlock - > ballisticCoefficient ) * dT ;
Point3F d ( mCurrVelocity . x * dV , mCurrVelocity . y * dV , 9.81f * mDataBlock - > gravityMod * dT ) ;
mCurrVelocity - = d ;
}
// adjust missile velocity from acceleration
if ( use_accel )
{
if ( mCurrTick > mDataBlock - > accelDelay & &
mCurrTick < = mDataBlock - > accelDelay + mDataBlock - > accelLifetime )
{
Point3F d = mCurrVelocity ; d . normalizeSafe ( ) ;
mCurrVelocity + = d * mDataBlock - > acceleration * dT ;
}
}
// adjust mCurrVelocity from guidance system influences
if ( mDataBlock - > isGuided & & missile_target & & mCurrTick > mDataBlock - > trackDelay )
{
// get the position tracked by the guidance system
Point3F target_pos = missile_target - > getBoxCenter ( ) ;
Point3F target_vec = target_pos - mCurrPosition ;
F32 target_dist_sq = target_vec . lenSquared ( ) ;
if ( target_dist_sq < 4.0f )
prec_inc + = 1.0f ;
// hover
if ( mDataBlock - > hover_altitude > 0.0f )
{
Point3F target_vec_xy ( target_vec . x , target_vec . y , 0 ) ;
F32 xy_dist = target_vec_xy . len ( ) ;
if ( xy_dist > mDataBlock - > hover_attack_distance )
{
hover_attack_go = false ;
if ( xy_dist > mDataBlock - > hover_attack_distance + mDataBlock - > hover_attack_gradient )
{
target_pos . z + = mDataBlock - > hover_altitude ;
}
else
{
target_pos . z + = afxEase : : eq ( ( xy_dist - mDataBlock - > hover_attack_distance ) / mDataBlock - > hover_attack_gradient ,
0.0f , mDataBlock - > hover_altitude ,
0.25f , 0.75f ) ;
}
target_vec = target_pos - mCurrPosition ;
}
else
{
if ( ! hover_attack_go )
{
hover_attack_go = true ;
hover_attack_tick = 0 ;
}
hover_attack_tick + + ;
if ( hover_attack_tick < mDataBlock - > hover_time )
{
target_pos . z + = mDataBlock - > hover_altitude ;
target_vec = target_pos - mCurrPosition ;
}
}
}
// apply precision
// extract speed
F32 speed = mCurrVelocity . len ( ) ;
// normalize vectors
target_vec . normalizeSafe ( ) ;
mCurrVelocity . normalize ( ) ;
F32 prec = mDataBlock - > precision ;
// fade in precision gradually to avoid sudden turn
if ( mCurrTick < mDataBlock - > trackDelay + 16 )
prec * = ( mCurrTick - mDataBlock - > trackDelay ) / 16.0f ;
prec + = prec_inc ;
if ( prec > 100 )
prec = 100 ;
// apply precision weighting
target_vec * = prec ;
mCurrVelocity * = ( 100 - prec ) ;
mCurrVelocity + = target_vec ;
mCurrVelocity . normalize ( ) ;
mCurrVelocity * = speed ;
}
// wiggle
for ( U32 i = 0 ; i < mDataBlock - > wiggle_num_axis ; i + + )
{
if ( i > = mDataBlock - > wiggle_magnitudes . size ( ) | | i > = mDataBlock - > wiggle_speeds . size ( ) )
break ;
F32 wiggle_mag = mDataBlock - > wiggle_magnitudes [ i ] ;
F32 wiggle_speed = mDataBlock - > wiggle_speeds [ i ] ;
Point3F wiggle_axis = mDataBlock - > wiggle_axis [ i ] ;
//wiggle_axis.normalizeSafe(); // sufficient????
F32 theta = wiggle_mag * mSin ( wiggle_speed * ( mCurrTick * TickSec ) ) ;
//Con::printf( "theta: %f", theta );
AngAxisF thetaRot ( wiggle_axis , theta ) ;
MatrixF temp ( true ) ;
thetaRot . setMatrix ( & temp ) ;
temp . mulP ( mCurrVelocity ) ;
}
Point3F new_pos = old_pos + mCurrVelocity * dT ;
// conform to terrain
if ( mDataBlock - > followTerrain & & mCurrTick > = mDataBlock - > followTerrainAdjustDelay )
{
U32 mask = TerrainObjectType | TerrainLikeObjectType ; // | InteriorObjectType;
F32 ht = mDataBlock - > followTerrainHeight ;
F32 ht_rate = mDataBlock - > followTerrainAdjustRate ;
F32 ht_min = 0.05f ;
if ( ht < ht_min )
ht = ht_min ;
SceneContainer * container = ( isServerObject ( ) ) ? & gServerContainer : & gClientContainer ;
Point3F above_pos = new_pos ; above_pos . z + = 10000 ;
Point3F below_pos = new_pos ; below_pos . z - = 10000 ;
RayInfo rInfo ;
if ( container & & container - > castRay ( above_pos , below_pos , mask , & rInfo ) )
{
F32 terrain_z = rInfo . point . z ;
F32 seek_z = terrain_z + ht ;
if ( new_pos . z < seek_z )
{
new_pos . z + = ht_rate * dT ;
if ( new_pos . z > seek_z )
new_pos . z = seek_z ;
}
else if ( new_pos . z > seek_z )
{
new_pos . z - = ht_rate * dT ;
if ( new_pos . z < seek_z )
new_pos . z = seek_z ;
}
if ( new_pos . z < terrain_z + ht_min )
new_pos . z = terrain_z + ht_min ;
}
}
// only check for impacts on server
if ( isServerObject ( ) )
{
// avoid collision with the spellcaster
if ( collide_exempt & & mCurrTick < = mDataBlock - > caster_safety_time )
collide_exempt - > disableCollision ( ) ;
// check for collision along ray from old to new position
RayInfo rInfo ;
bool did_hit = false ;
if ( mPhysicsWorld )
{
// did_hit = mPhysicsWorld->castRay(old_pos, new_pos, &rInfo, Point3F(new_pos - old_pos) * mDataBlock->impactForce );
// Impulse currently hardcoded for testing purposes
did_hit = mPhysicsWorld - > castRay ( old_pos , new_pos , & rInfo , Point3F ( new_pos - old_pos ) * 1000.0f ) ;
}
else
{
did_hit = getContainer ( ) - > castRay ( old_pos , new_pos , collision_mask , & rInfo ) ;
}
// restore collisions on spellcaster
if ( collide_exempt & & mCurrTick < = mDataBlock - > caster_safety_time )
collide_exempt - > enableCollision ( ) ;
// process impact
if ( did_hit )
{
MatrixF xform ( true ) ;
xform . setColumn ( 3 , rInfo . point ) ;
setTransform ( xform ) ;
mCurrPosition = rInfo . point ;
mCurrVelocity = Point3F ( 0 , 0 , 0 ) ;
did_impact = true ;
setMaskBits ( ImpactMask ) ;
if ( choreographer )
{
choreographer - > impactNotify ( rInfo . point , rInfo . normal , rInfo . object ) ;
Sim : : postEvent ( this , new ObjectDeleteEvent , Sim : : getCurrentTime ( ) + 500 ) ;
}
}
}
else // if (isClientObject())
{
emitParticles ( mCurrPosition , new_pos , mCurrVelocity , TickMs ) ;
updateSound ( ) ;
}
// interp values used in interpolateTick()
mCurrDeltaBase = new_pos ;
mCurrBackDelta = mCurrPosition - new_pos ;
mCurrPosition = new_pos ;
MatrixF xform ( true ) ;
xform . setColumn ( 3 , mCurrPosition ) ;
setTransform ( xform ) ;
}
void afxMagicMissile : : interpolateTick ( F32 delta )
{
Parent : : interpolateTick ( delta ) ;
// only active from launch to impact
if ( ! is_active ( ) )
return ;
Point3F interpPos = mCurrDeltaBase + mCurrBackDelta * delta ;
Point3F dir = mCurrVelocity ;
if ( dir . isZero ( ) )
dir . set ( 0 , 0 , 1 ) ;
else
dir . normalize ( ) ;
MatrixF xform ( true ) ;
xform = MathUtils : : createOrientFromDir ( dir ) ;
xform . setPosition ( interpPos ) ;
setRenderTransform ( xform ) ;
updateSound ( ) ;
}
//--------------------------------------------------------------------------
U32 afxMagicMissile : : packUpdate ( NetConnection * con , U32 mask , BitStream * stream )
{
U32 retMask = Parent : : packUpdate ( con , mask , stream ) ;
const bool isInitalUpdate = mask & GameBase : : InitialUpdateMask ;
// InitialUpdateMask
if ( stream - > writeFlag ( isInitalUpdate ) )
{
Point3F pos ;
getTransform ( ) . getColumn ( 3 , & pos ) ;
stream - > writeCompressedPoint ( pos ) ;
F32 len = mCurrVelocity . len ( ) ;
if ( stream - > writeFlag ( len > 0.02 ) )
{
Point3F outVel = mCurrVelocity ;
outVel * = 1 / len ;
stream - > writeNormalVector ( outVel , 10 ) ;
len * = 32.0 ; // 5 bits for fraction
if ( len > 8191 )
len = 8191 ;
stream - > writeInt ( ( S32 ) len , 13 ) ;
}
stream - > writeRangedU32 ( mCurrTick , 0 , afxMagicMissileData : : MaxLifetimeTicks ) ;
if ( choreographer )
{
S32 ghostIndex = con - > getGhostIndex ( choreographer ) ;
if ( stream - > writeFlag ( ghostIndex ! = - 1 ) )
{
stream - > writeRangedU32 ( U32 ( ghostIndex ) ,
0 ,
NetConnection : : MaxGhostCount ) ;
}
else
// have not recieved the ghost for the source object yet, try again later
retMask | = GameBase : : InitialUpdateMask ;
}
else
stream - > writeFlag ( false ) ;
}
// impact update
if ( stream - > writeFlag ( mask & ImpactMask ) )
{
mathWrite ( * stream , mCurrPosition ) ;
mathWrite ( * stream , mCurrVelocity ) ;
stream - > writeFlag ( did_impact ) ;
}
// guided update
if ( stream - > writeFlag ( mask & GuideMask ) )
{
mathWrite ( * stream , mCurrPosition ) ;
mathWrite ( * stream , mCurrVelocity ) ;
}
return retMask ;
}
void afxMagicMissile : : unpackUpdate ( NetConnection * con , BitStream * stream )
{
Parent : : unpackUpdate ( con , stream ) ;
if ( stream - > readFlag ( ) ) // InitialUpdateMask
{
Point3F pos ;
stream - > readCompressedPoint ( & pos ) ;
if ( stream - > readFlag ( ) )
{
stream - > readNormalVector ( & mCurrVelocity , 10 ) ;
mCurrVelocity * = stream - > readInt ( 13 ) / 32.0f ;
}
else
mCurrVelocity . zero ( ) ;
mCurrDeltaBase = pos ;
mCurrBackDelta = mCurrPosition - pos ;
mCurrPosition = pos ;
setPosition ( mCurrPosition ) ;
mCurrTick = stream - > readRangedU32 ( 0 , afxMagicMissileData : : MaxLifetimeTicks ) ;
if ( stream - > readFlag ( ) )
{
U32 id = stream - > readRangedU32 ( 0 , NetConnection : : MaxGhostCount ) ;
choreographer = dynamic_cast < afxChoreographer * > ( con - > resolveGhost ( id ) ) ;
if ( choreographer )
{
deleteNotify ( choreographer ) ;
}
}
else
{
if ( choreographer )
clearNotify ( choreographer ) ;
choreographer = 0 ;
}
}
// impact update
if ( stream - > readFlag ( ) )
{
mathRead ( * stream , & mCurrPosition ) ;
mathRead ( * stream , & mCurrVelocity ) ;
did_impact = stream - > readFlag ( ) ;
}
if ( stream - > readFlag ( ) ) // guided update
{
mathRead ( * stream , & mCurrPosition ) ;
mathRead ( * stream , & mCurrVelocity ) ;
}
}
//--------------------------------------------------------------------------
void afxMagicMissile : : prepRenderImage ( SceneRenderState * state )
{
if ( ! is_active ( ) )
return ;
/*
if ( isHidden ( ) | | mFadeValue < = ( 1.0 / 255.0 ) )
return ;
*/
if ( mDataBlock - > lightDesc )
{
mDataBlock - > lightDesc - > prepRender ( state , & mLightState , getRenderTransform ( ) ) ;
}
/*
if ( mFlareData )
{
mFlareState . fullBrightness = mDataBlock - > lightDesc - > mBrightness ;
mFlareState . scale = mFlareScale ;
mFlareState . lightInfo = mLight ;
mFlareState . lightMat = getTransform ( ) ;
mFlareData - > prepRender ( state , & mFlareState ) ;
}
*/
prepBatchRender ( state ) ;
}
void afxMagicMissile : : prepBatchRender ( SceneRenderState * state )
{
if ( ! mProjectileShape )
return ;
GFXTransformSaver saver ;
// Set up our TS render state.
TSRenderState rdata ;
rdata . setSceneState ( state ) ;
// We might have some forward lit materials
// so pass down a query to gather lights.
LightQuery query ;
query . init ( getWorldSphere ( ) ) ;
rdata . setLightQuery ( & query ) ;
MatrixF mat = getRenderTransform ( ) ;
mat . scale ( mObjScale ) ;
mat . scale ( mDataBlock - > scale ) ;
GFX - > setWorldMatrix ( mat ) ;
mProjectileShape - > setDetailFromPosAndScale ( state , mat . getPosition ( ) , mObjScale ) ;
mProjectileShape - > animate ( ) ;
mProjectileShape - > render ( rdata ) ;
}
void afxMagicMissile : : onDeleteNotify ( SimObject * obj )
{
ShapeBase * shape_test = dynamic_cast < ShapeBase * > ( obj ) ;
if ( shape_test = = collide_exempt )
{
collide_exempt = NULL ;
Parent : : onDeleteNotify ( obj ) ;
return ;
}
SceneObject * target_test = dynamic_cast < SceneObject * > ( obj ) ;
if ( target_test = = missile_target )
{
missile_target = NULL ;
Parent : : onDeleteNotify ( obj ) ;
return ;
}
afxChoreographer * ch = dynamic_cast < afxChoreographer * > ( obj ) ;
if ( ch = = choreographer )
{
choreographer = NULL ;
Parent : : onDeleteNotify ( obj ) ;
return ;
}
Parent : : onDeleteNotify ( obj ) ;
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// private:
void afxMagicMissile : : create_splash ( const Point3F & pos )
{
if ( ! mDataBlock | | ! mDataBlock - > splash )
return ;
MatrixF xfm = getTransform ( ) ;
xfm . setPosition ( pos ) ;
Splash * splash = new Splash ( ) ;
splash - > onNewDataBlock ( mDataBlock - > splash , false ) ;
splash - > setTransform ( xfm ) ;
splash - > setInitialState ( xfm . getPosition ( ) , Point3F ( 0.0 , 0.0 , 1.0 ) ) ;
if ( ! splash - > registerObject ( ) )
{
delete splash ;
splash = NULL ;
}
}
void afxMagicMissile : : get_launch_constraint_data ( Point3F & pos , Point3F & vel )
{
// need a choreographer
if ( ! choreographer )
{
Con : : errorf ( " afxMagicMissile::get_launch_constraint_data(): missing choreographer. " ) ;
return ;
}
// need a constraint manager
afxConstraintMgr * cons_mgr = choreographer - > getConstraintMgr ( ) ;
if ( ! cons_mgr )
{
Con : : errorf ( " afxMagicMissile::get_launch_constraint_data(): missing constriant manager. " ) ;
return ;
}
// need a valid constraint
afxConstraintID launch_cons_id ;
if ( isServerObject ( ) )
launch_cons_id = cons_mgr - > getConstraintId ( mDataBlock - > launch_cons_s_def ) ;
else
launch_cons_id = cons_mgr - > getConstraintId ( mDataBlock - > launch_cons_c_def ) ;
afxConstraint * launch_cons = cons_mgr - > getConstraint ( launch_cons_id ) ;
if ( ! launch_cons )
{
Con : : errorf ( " afxMagicMissile::get_launch_constraint_data(): constraint undefined. " ) ;
return ;
}
MatrixF launch_xfm ;
launch_cons - > getTransform ( launch_xfm ) ;
Point3F launch_pos ;
launch_cons - > getPosition ( launch_pos ) ;
pos = launch_pos ;
// echo the actual launch position to the console
if ( mDataBlock - > echo_launch_offset )
{
SceneObject * default_launcher = get_default_launcher ( ) ;
if ( default_launcher )
{
MatrixF launcher_xfm_inv = default_launcher - > getWorldTransform ( ) ;
VectorF offset = pos - default_launcher - > getRenderPosition ( ) ;
launcher_xfm_inv . mulV ( offset ) ;
if ( isServerObject ( ) )
Con : : printf ( " launchOffsetServer = \" %g %g %g \" ; " , offset . x , offset . y , offset . z ) ;
else
Con : : printf ( " launchOffsetClient = \" %g %g %g \" ; " , offset . x , offset . y , offset . z ) ;
}
}
// setup aiming matrix to straight forward and level
MatrixF aim_mtx ;
AngAxisF aim_aa ( Point3F ( 0 , 1 , 0 ) , 0 ) ;
aim_aa . setMatrix ( & aim_mtx ) ;
// calculate final aiming vector
MatrixF aim2_mtx ;
aim2_mtx . mul ( launch_xfm , aim_mtx ) ;
VectorF aim_vec ;
aim2_mtx . getColumn ( 1 , & aim_vec ) ;
aim_vec . normalizeSafe ( ) ;
// give velocity vector a magnitude
vel = aim_vec * mDataBlock - > muzzleVelocity ;
}
// resolve the launch constraint object. normally it's the caster, but for
// reverse_targeting the target object us used.
SceneObject * afxMagicMissile : : get_default_launcher ( ) const
{
SceneObject * launch_cons_obj = 0 ;
if ( mDataBlock - > reverse_targeting )
{
if ( dynamic_cast < afxMagicSpell * > ( choreographer ) )
launch_cons_obj = ( ( afxMagicSpell * ) choreographer ) - > target ;
if ( ! launch_cons_obj )
{
Con : : errorf ( " afxMagicMissile::get_launch_data(): missing target constraint object for reverse targeted missile. " ) ;
return 0 ;
}
}
else
{
if ( dynamic_cast < afxMagicSpell * > ( choreographer ) )
launch_cons_obj = ( ( afxMagicSpell * ) choreographer ) - > caster ;
if ( ! launch_cons_obj )
{
Con : : errorf ( " afxMagicMissile::get_launch_data(): missing launch constraint object missile. " ) ;
return 0 ;
}
}
return launch_cons_obj ;
}
void afxMagicMissile : : get_launch_data ( Point3F & pos , Point3F & vel )
{
bool use_constraint = ( isServerObject ( ) ) ? mDataBlock - > launch_cons_s_def . isDefined ( ) : mDataBlock - > launch_cons_c_def . isDefined ( ) ;
if ( use_constraint )
{
get_launch_constraint_data ( pos , vel ) ;
return ;
}
// a choreographer pointer is required
if ( ! choreographer )
{
Con : : errorf ( " afxMagicMissile::get_launch_data(): missing choreographer. " ) ;
return ;
}
SceneObject * launch_cons_obj = get_default_launcher ( ) ;
if ( ! launch_cons_obj )
return ;
MatrixF launch_xfm = launch_cons_obj - > getRenderTransform ( ) ;
// calculate launch position
Point3F offset_override = ( isClientObject ( ) ) ? mDataBlock - > launch_offset_client :
mDataBlock - > launch_offset_server ;
// override
if ( ! offset_override . isZero ( ) )
{
launch_xfm . mulV ( offset_override ) ;
pos = launch_cons_obj - > getRenderPosition ( ) + offset_override ;
}
// no override
else
{
// get transformed launch offset
VectorF launch_offset = mDataBlock - > launch_offset ;
launch_xfm . mulV ( launch_offset ) ;
StringTableEntry launch_node = mDataBlock - > launch_node ;
// calculate position of missile at launch
if ( launch_node ! = ST_NULLSTRING )
{
ShapeBase * launch_cons_shape = dynamic_cast < ShapeBase * > ( launch_cons_obj ) ;
TSShapeInstance * shape_inst = ( launch_cons_shape ) ? launch_cons_shape - > getShapeInstance ( ) : 0 ;
if ( ! shape_inst | | ! shape_inst - > getShape ( ) )
launch_node = ST_NULLSTRING ;
else
{
S32 node_ID = shape_inst - > getShape ( ) - > findNode ( launch_node ) ;
MatrixF node_xfm = launch_cons_obj - > getRenderTransform ( ) ;
node_xfm . scale ( launch_cons_obj - > getScale ( ) ) ;
if ( node_ID > = 0 )
node_xfm . mul ( shape_inst - > mNodeTransforms [ node_ID ] ) ;
VectorF node_offset = mDataBlock - > launch_node_offset ;
node_xfm . mulV ( node_offset ) ;
pos = node_xfm . getPosition ( ) + launch_offset + node_offset ;
}
}
// calculate launch position without launch node
else
pos = launch_cons_obj - > getRenderPosition ( ) + launch_offset ;
}
// echo the actual launch position to the console
if ( mDataBlock - > echo_launch_offset )
{
VectorF offset = pos - launch_cons_obj - > getRenderPosition ( ) ;
MatrixF caster_xfm_inv = launch_xfm ;
caster_xfm_inv . affineInverse ( ) ;
caster_xfm_inv . mulV ( offset ) ;
if ( isServerObject ( ) )
Con : : printf ( " launchOffsetServer = \" %g %g %g \" ; " , offset . x , offset . y , offset . z ) ;
else
Con : : printf ( " launchOffsetClient = \" %g %g %g \" ; " , offset . x , offset . y , offset . z ) ;
}
// calculate launch velocity vector
if ( starting_vel_vec . isZero ( ) )
{
// setup aiming matrix to straight forward and level
MatrixF aim_mtx ;
AngAxisF aim_aa ( Point3F ( 0 , 1 , 0 ) , 0 ) ;
aim_aa . setMatrix ( & aim_mtx ) ;
// setup pitch matrix
MatrixF pitch_mtx ;
AngAxisF pitch_aa ( Point3F ( 1 , 0 , 0 ) , mDegToRad ( mDataBlock - > launch_pitch ) ) ;
pitch_aa . setMatrix ( & pitch_mtx ) ;
// setup pan matrix
MatrixF pan_mtx ;
AngAxisF pan_aa ( Point3F ( 0 , 0 , 1 ) , mDegToRad ( mDataBlock - > launch_pan ) ) ;
pan_aa . setMatrix ( & pan_mtx ) ;
// calculate adjusted aiming matrix
aim_mtx . mul ( pitch_mtx ) ;
aim_mtx . mul ( pan_mtx ) ;
// calculate final aiming vector
MatrixF aim2_mtx ;
aim2_mtx . mul ( launch_xfm , aim_mtx ) ;
VectorF aim_vec ;
aim2_mtx . getColumn ( 1 , & aim_vec ) ;
aim_vec . normalizeSafe ( ) ;
// give velocity vector a magnitude
vel = aim_vec * mDataBlock - > muzzleVelocity ;
}
else
{
vel = starting_vel_vec * starting_velocity ;
}
}
void afxMagicMissile : : updateSound ( )
{
if ( ! mDataBlock - > sound )
return ;
if ( mSound )
{
if ( ! mSound - > isPlaying ( ) )
mSound - > play ( ) ;
mSound - > setVelocity ( getVelocity ( ) ) ;
mSound - > setTransform ( getRenderTransform ( ) ) ;
}
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// public:
void afxMagicMissile : : launch ( )
{
get_launch_data ( mCurrPosition , mCurrVelocity ) ;
mCurrDeltaBase = mCurrPosition ;
mCurrBackDelta . zero ( ) ;
did_launch = true ;
setPosition ( mCurrPosition ) ;
afxMagicSpell * spell = dynamic_cast < afxMagicSpell * > ( choreographer ) ;
if ( spell )
{
if ( mDataBlock - > reverse_targeting )
{
missile_target = spell - > caster ;
collide_exempt = spell - > target ;
}
else
{
missile_target = spell - > target ;
collide_exempt = spell - > caster ;
}
if ( spell - > caster )
processAfter ( spell - > caster ) ;
if ( missile_target )
deleteNotify ( missile_target ) ;
if ( collide_exempt )
deleteNotify ( collide_exempt ) ;
}
else
{
missile_target = 0 ;
collide_exempt = 0 ;
}
}
void afxMagicMissile : : setChoreographer ( afxChoreographer * chor )
{
if ( choreographer )
clearNotify ( choreographer ) ;
choreographer = chor ;
if ( choreographer )
deleteNotify ( choreographer ) ;
}
void afxMagicMissile : : setStartingVelocityVector ( const Point3F & vel_vec )
{
starting_vel_vec = vel_vec ;
}
void afxMagicMissile : : setStartingVelocity ( const F32 vel )
{
starting_velocity = vel ;
}
void afxMagicMissile : : getStartingVelocityValues ( F32 & vel , Point3F & vel_vec )
{
vel = starting_velocity ;
vel_vec = starting_vel_vec ;
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
/* From stock Projectile code...
DefineEngineMethod ( Projectile , presimulate , void , ( F32 seconds ) , ( 1.0f ) , " Updates velocity and position, and performs collision testing. \n "
" @param seconds Amount of time, in seconds, since the simulation began, to start the simulation at. \n "
" @tsexample \n "
" // Tell the projectile object to process a simulation event, and provide the amount of time \n "
" in seconds that has passed since the simulation began. \n "
" %seconds = 2000; \n "
" %projectile.presimulate(%seconds); \n "
" @endtsexample \n " )
{
object - > simulate ( seconds ) ;
}
*/
DefineEngineMethod ( afxMagicMissile , setStartingVelocityVector , void , ( Point3F velocityVec ) , ,
" Set the starting velocity-vector for a magic-missile. \n \n "
" @ingroup AFX " )
{
object - > setStartingVelocityVector ( velocityVec ) ;
}
DefineEngineMethod ( afxMagicMissile , setStartingVelocity , void , ( float velocity ) , ,
" Set the starting velocity for a magic-missile. \n \n "
" @ingroup AFX " )
{
object - > setStartingVelocity ( velocity ) ;
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//