afxMagicMissile.cpp
Engine/source/afx/afxMagicMissile.cpp
Classes:
class
Public Defines
define
myOffset(field) (field, )
Public Variables
missileTrackDelayValidator (0, 100000)
muzzleVelocityValidator (0, 10000)
Public Functions
ConsoleDocClass(afxMagicMissile , "@brief Magic-missile class used internally by afxMagicSpell. Properties of individual missile types are defined using <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxMagicMissileData.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )
ConsoleDocClass(afxMagicMissileData , "@brief Defines <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> particular magic-missile type. (Use with afxMagicSpellData.)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/classafxmagicmissiledata/">afxMagicMissileData</a>(Fireball_MM)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " muzzleVelocity = 50;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " velInheritFactor = 0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lifetime = 20000;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " isBallistic = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " ballisticCoefficient = 0.85;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " gravityMod = 0.05;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " isGuided = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " precision = 30;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " trackDelay = 7;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " launchOffset = \"0 0 43.7965\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " launchOnServerSignal = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )
DefineEngineMethod(afxMagicMissile , setStartingVelocity , void , (float velocity) , "Set the starting velocity <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> magic-<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">missile.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxMagicMissile , setStartingVelocityVector , void , (Point3F velocityVec) , "Set the starting velocity-vector <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> magic-<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">missile.\n\n</a>" "@ingroup AFX" )
missilePrecisionValidator(0. f, 100. f)
bool
readVector(Vector< T > & vec, Stream & stream, const char * msg)
Detailed Description
Public Defines
myOffset(field) (field, )
Public Variables
FRangeValidator missileBallisticCoefficientValidator (0, 1)
FRangeValidator missileTrackDelayValidator (0, 100000)
FRangeValidator muzzleVelocityValidator (0, 10000)
Public Functions
ConsoleDocClass(afxMagicMissile , "@brief Magic-missile class used internally by afxMagicSpell. Properties of individual missile types are defined using <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxMagicMissileData.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )
ConsoleDocClass(afxMagicMissileData , "@brief Defines <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> particular magic-missile type. (Use with afxMagicSpellData.)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/classafxmagicmissiledata/">afxMagicMissileData</a>(Fireball_MM)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " muzzleVelocity = 50;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " velInheritFactor = 0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lifetime = 20000;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " isBallistic = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " ballisticCoefficient = 0.85;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " gravityMod = 0.05;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " isGuided = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " precision = 30;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " trackDelay = 7;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " launchOffset = \"0 0 43.7965\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " launchOnServerSignal = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )
DefineEngineMethod(afxMagicMissile , setStartingVelocity , void , (float velocity) , "Set the starting velocity <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> magic-<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">missile.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxMagicMissile , setStartingVelocityVector , void , (Point3F velocityVec) , "Set the starting velocity-vector <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> magic-<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">missile.\n\n</a>" "@ingroup AFX" )
IMPLEMENT_CO_DATABLOCK_V1(afxMagicMissileData )
IMPLEMENT_CO_NETOBJECT_V1(afxMagicMissile )
missilePrecisionValidator(0. f, 100. f)
readVector(Vector< T > & vec, Stream & stream, const char * msg)
writeVector(const Vector< T > & vec, Stream & stream, const char * msg)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 25// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames 26// Copyright (C) 2015 Faust Logic, Inc. 27// 28// afxMagicMissile is a heavily modified variation of the stock Projectile class. In 29// addition to numerous AFX customizations, it also incorporates functionality based on 30// the following TGE resources: 31// 32// Guided or Seeker Projectiles by Derk Adams 33// http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=6778 34// 35// Projectile Ballistic Coefficients (drag factors) by Mark Owen 36// http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=5128 37// 38//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 39 40#include "afx/arcaneFX.h" 41 42#include "scene/sceneRenderState.h" 43#include "scene/sceneManager.h" 44#include "core/resourceManager.h" 45#include "ts/tsShapeInstance.h" 46#include "sfx/sfxTrack.h" 47#include "sfx/sfxSource.h" 48#include "sfx/sfxSystem.h" 49#include "sfx/sfxTypes.h" 50#include "math/mathUtils.h" 51#include "math/mathIO.h" 52#include "sim/netConnection.h" 53#include "T3D/fx/particleEmitter.h" 54#include "T3D/fx/splash.h" 55#include "T3D/physics/physicsPlugin.h" 56#include "T3D/physics/physicsWorld.h" 57#include "gfx/gfxTransformSaver.h" 58#include "T3D/containerQuery.h" 59#include "T3D/lightDescription.h" 60#include "console/engineAPI.h" 61#include "lighting/lightManager.h" 62 63#include "afx/util/afxEase.h" 64#include "afx/afxMagicMissile.h" 65#include "afx/afxMagicSpell.h" 66#include "afx/afxChoreographer.h" 67 68class ObjectDeleteEvent : public SimEvent 69{ 70public: 71 void process(SimObject *object) 72 { 73 object->deleteObject(); 74 } 75}; 76 77IMPLEMENT_CO_DATABLOCK_V1(afxMagicMissileData); 78 79ConsoleDocClass( afxMagicMissileData, 80 "@brief Defines a particular magic-missile type. (Use with afxMagicSpellData.)\n" 81 "@tsexample\n" 82 "datablock afxMagicMissileData(Fireball_MM)\n" 83 "{\n" 84 " muzzleVelocity = 50;\n" 85 " velInheritFactor = 0;\n" 86 " lifetime = 20000;\n" 87 " isBallistic = true;\n" 88 " ballisticCoefficient = 0.85;\n" 89 " gravityMod = 0.05;\n" 90 " isGuided = true;\n" 91 " precision = 30;\n" 92 " trackDelay = 7;\n" 93 " launchOffset = \"0 0 43.7965\";\n" 94 " launchOnServerSignal = true;\n" 95 "};\n" 96 "@endtsexample\n" 97 "@ingroup AFX\n" 98); 99 100IMPLEMENT_CO_NETOBJECT_V1(afxMagicMissile); 101 102ConsoleDocClass( afxMagicMissile, 103 "@brief Magic-missile class used internally by afxMagicSpell. Properties of individual missile types are defined using afxMagicMissileData.\n" 104 "@ingroup AFX\n" 105); 106 107/* From stock Projectile code... 108IMPLEMENT_CALLBACK( ProjectileData, onExplode, void, ( Projectile* proj, Point3F pos, F32 fade ), 109 ( proj, pos, fade ), 110 "Called when a projectile explodes.\n" 111 "@param proj The projectile exploding.\n" 112 "@param pos The position of the explosion.\n" 113 "@param fade The currently fadeValue of the projectile, affects its visibility.\n" 114 "@see Projectile, ProjectileData\n" 115 ); 116 117IMPLEMENT_CALLBACK( ProjectileData, onCollision, void, ( Projectile* proj, SceneObject* col, F32 fade, Point3F pos, Point3F normal ), 118 ( proj, col, fade, pos, normal ), 119 "Called when a projectile collides with another object.\n" 120 "@param proj The projectile colliding.\n" 121 "@param col The object hit by the projectile.\n" 122 "@param fade The current fadeValue of the projectile, affects its visibility.\n" 123 "@param pos The collision position.\n" 124 "@param normal The collision normal.\n" 125 "@see Projectile, ProjectileData\n" 126 ); 127 128const U32 Projectile::csmStaticCollisionMask = TerrainObjectType | 129 InteriorObjectType | 130 StaticObjectType; 131 132const U32 Projectile::csmDynamicCollisionMask = PlayerObjectType | 133 VehicleObjectType | 134 DamagableItemObjectType; 135 136const U32 Projectile::csmDamageableMask = Projectile::csmDynamicCollisionMask; 137 138U32 Projectile::smProjectileWarpTicks = 5; 139*/ 140 141//-------------------------------------------------------------------------- 142// 143afxMagicMissileData::afxMagicMissileData() 144{ 145 projectileShapeName = ST_NULLSTRING; 146 147 sound = NULL; 148 149 /* From stock Projectile code... 150 explosion = NULL; 151 explosionId = 0; 152 153 waterExplosion = NULL; 154 waterExplosionId = 0; 155 */ 156 157 /* From stock Projectile code... 158 faceViewer = false; 159 */ 160 scale.set( 1.0f, 1.0f, 1.0f ); 161 162 isBallistic = false; 163 164 /* From stock Projectile code... 165 velInheritFactor = 1.0f; 166 */ 167 muzzleVelocity = 50; 168 /* From stock Projectile code... 169 impactForce = 0.0f; 170 171 armingDelay = 0; 172 173 activateSeq = -1; 174 maintainSeq = -1; 175 */ 176 177 lifetime = 20000 / 32; 178 fadeDelay = 20000 / 32; 179 180 gravityMod = 1.0; 181 /* From stock Projectile code... 182 bounceElasticity = 0.999f; 183 bounceFriction = 0.3f; 184 */ 185 186 particleEmitter = NULL; 187 particleEmitterId = 0; 188 189 particleWaterEmitter = NULL; 190 particleWaterEmitterId = 0; 191 192 splash = NULL; 193 splashId = 0; 194 195 /* From stock Projectile code... 196 decal = NULL; 197 decalId = 0; 198 */ 199 200 lightDesc = NULL; 201 lightDescId = 0; 202 203 starting_vel_vec.zero(); 204 205 isGuided = false; 206 precision = 0; 207 trackDelay = 0; 208 ballisticCoefficient = 1.0f; 209 210 followTerrain = false; 211 followTerrainHeight = 0.1f; 212 followTerrainAdjustRate = 20.0f; 213 followTerrainAdjustDelay = 0; 214 215 lifetime = MaxLifetimeTicks; 216 collision_mask = arcaneFX::sMissileCollisionMask; 217 218 acceleration = 0; 219 accelDelay = 0; 220 accelLifetime = 0; 221 222 launch_node = ST_NULLSTRING; 223 launch_offset.zero(); 224 launch_offset_server.zero(); 225 launch_offset_client.zero(); 226 launch_node_offset.zero(); 227 launch_pitch = 0; 228 launch_pan = 0; 229 launch_cons_s_spec = ST_NULLSTRING; 230 launch_cons_c_spec = ST_NULLSTRING; 231 232 echo_launch_offset = false; 233 234 wiggle_axis_string = ST_NULLSTRING; 235 wiggle_num_axis = 0; 236 wiggle_axis = 0; 237 238 hover_altitude = 0; 239 hover_attack_distance = 0; 240 hover_attack_gradient = 0; 241 hover_time = 0; 242 243 reverse_targeting = false; 244 245 caster_safety_time = U32_MAX; 246} 247 248afxMagicMissileData::afxMagicMissileData(const afxMagicMissileData& other, bool temp_clone) : GameBaseData(other, temp_clone) 249{ 250 projectileShapeName = other.projectileShapeName; 251 projectileShape = other.projectileShape; // -- TSShape loads using projectileShapeName 252 sound = other.sound; 253 splash = other.splash; 254 splashId = other.splashId; // -- for pack/unpack of splash ptr 255 lightDesc = other.lightDesc; 256 lightDescId = other.lightDescId; // -- for pack/unpack of lightDesc ptr 257 scale = other.scale; 258 isBallistic = other.isBallistic; 259 muzzleVelocity = other.muzzleVelocity; 260 gravityMod = other.gravityMod; 261 particleEmitter = other.particleEmitter; 262 particleEmitterId = other.particleEmitterId; // -- for pack/unpack of particleEmitter ptr 263 particleWaterEmitter = other.particleWaterEmitter; 264 particleWaterEmitterId = other.particleWaterEmitterId; // -- for pack/unpack of particleWaterEmitter ptr 265 266 collision_mask = other.collision_mask; 267 starting_vel_vec = other.starting_vel_vec; 268 isGuided = other.isGuided; 269 precision = other.precision; 270 trackDelay = other.trackDelay; 271 ballisticCoefficient = other.ballisticCoefficient; 272 followTerrain = other.followTerrain; 273 followTerrainHeight = other.followTerrainHeight; 274 followTerrainAdjustRate = other.followTerrainAdjustRate; 275 followTerrainAdjustDelay = other.followTerrainAdjustDelay; 276 lifetime = other.lifetime; 277 fadeDelay = other.fadeDelay; 278 acceleration = other.acceleration; 279 accelDelay = other.accelDelay; 280 accelLifetime = other.accelLifetime; 281 launch_node = other.launch_node; 282 launch_offset = other.launch_offset; 283 launch_offset_server = other.launch_offset_server; 284 launch_offset_client = other.launch_offset_client; 285 launch_node_offset = other.launch_node_offset; 286 launch_pitch = other.launch_pitch; 287 launch_pan = other.launch_pan; 288 launch_cons_s_spec = other.launch_cons_s_spec; 289 launch_cons_c_spec = other.launch_cons_c_spec; 290 launch_cons_s_def = other.launch_cons_s_def; 291 launch_cons_c_def = other.launch_cons_c_def; 292 echo_launch_offset = other.echo_launch_offset; 293 wiggle_magnitudes = other.wiggle_magnitudes; 294 wiggle_speeds = other.wiggle_speeds; 295 wiggle_axis_string = other.wiggle_axis_string; 296 wiggle_num_axis = other.wiggle_num_axis; 297 wiggle_axis = other.wiggle_axis; 298 hover_altitude = other.hover_altitude; 299 hover_attack_distance = other.hover_attack_distance; 300 hover_attack_gradient = other.hover_attack_gradient; 301 hover_time = other.hover_time; 302 reverse_targeting = other.reverse_targeting; 303 caster_safety_time = other.caster_safety_time; 304} 305 306afxMagicMissileData::~afxMagicMissileData() 307{ 308 if (wiggle_axis) 309 delete [] wiggle_axis; 310} 311 312afxMagicMissileData* afxMagicMissileData::cloneAndPerformSubstitutions(const SimObject* owner, S32 index) 313{ 314 if (!owner || getSubstitutionCount() == 0) 315 return this; 316 317 afxMagicMissileData* sub_missile_db = new afxMagicMissileData(*this, true); 318 performSubstitutions(sub_missile_db, owner, index); 319 320 return sub_missile_db; 321} 322 323//-------------------------------------------------------------------------- 324 325#define myOffset(field) Offset(field, afxMagicMissileData) 326 327FRangeValidator muzzleVelocityValidator(0, 10000); 328FRangeValidator missilePrecisionValidator(0.f, 100.f); 329FRangeValidator missileTrackDelayValidator(0, 100000); 330FRangeValidator missileBallisticCoefficientValidator(0, 1); 331 332void afxMagicMissileData::initPersistFields() 333{ 334 static IRangeValidatorScaled ticksFromMS(TickMs, 0, MaxLifetimeTicks); 335 336 addField("particleEmitter", TYPEID<ParticleEmitterData>(), Offset(particleEmitter, afxMagicMissileData)); 337 addField("particleWaterEmitter", TYPEID<ParticleEmitterData>(), Offset(particleWaterEmitter, afxMagicMissileData)); 338 339 addField("projectileShapeName", TypeFilename, Offset(projectileShapeName, afxMagicMissileData)); 340 addField("scale", TypePoint3F, Offset(scale, afxMagicMissileData)); 341 342 addField("sound", TypeSFXTrackName, Offset(sound, afxMagicMissileData)); 343 344 /* From stock Projectile code... 345 addField("explosion", TYPEID< ExplosionData >(), Offset(explosion, ProjectileData)); 346 addField("waterExplosion", TYPEID< ExplosionData >(), Offset(waterExplosion, ProjectileData)); 347 */ 348 349 addField("splash", TYPEID<SplashData>(), Offset(splash, afxMagicMissileData)); 350 /* From stock Projectile code... 351 addField("decal", TYPEID< DecalData >(), Offset(decal, ProjectileData)); 352 */ 353 354 addField("lightDesc", TYPEID< LightDescription >(), Offset(lightDesc, afxMagicMissileData)); 355 356 addField("isBallistic", TypeBool, Offset(isBallistic, afxMagicMissileData)); 357 /* From stock Projectile code... 358 addField("velInheritFactor", TypeF32, Offset(velInheritFactor, ProjectileData)); 359 */ 360 addNamedFieldV(muzzleVelocity, TypeF32, afxMagicMissileData, &muzzleVelocityValidator); 361 /* From stock Projectile code... 362 addField("impactForce", TypeF32, Offset(impactForce, ProjectileData)); 363 */ 364 addNamedFieldV(lifetime, TypeS32, afxMagicMissileData, &ticksFromMS); 365 /* From stock Projectile code... 366 addProtectedField("armingDelay", TypeS32, Offset(armingDelay, ProjectileData), &setArmingDelay, &getScaledValue, 367 "The time in milliseconds before the projectile is armed and will cause damage or explode on impact." ); 368 369 addProtectedField("fadeDelay", TypeS32, Offset(fadeDelay, ProjectileData), &setFadeDelay, &getScaledValue, 370 "The time in milliseconds when the projectile begins to fade out. Must be less than the lifetime to have an effect." ); 371 372 addField("bounceElasticity", TypeF32, Offset(bounceElasticity, ProjectileData)); 373 addField("bounceFriction", TypeF32, Offset(bounceFriction, ProjectileData)); 374 */ 375 addField("gravityMod", TypeF32, Offset(gravityMod, afxMagicMissileData)); 376 377 // FIELDS ADDED BY MAGIC-MISSILE 378 379 addField("missileShapeName", TypeFilename, myOffset(projectileShapeName)); 380 addField("missileShapeScale", TypePoint3F, myOffset(scale)); 381 382 addField("startingVelocityVector",TypePoint3F, myOffset(starting_vel_vec)); 383 384 addNamedField(isGuided, TypeBool, afxMagicMissileData); 385 addNamedFieldV(precision, TypeF32, afxMagicMissileData, &missilePrecisionValidator); 386 addNamedFieldV(trackDelay, TypeS32, afxMagicMissileData, &missileTrackDelayValidator); 387 addNamedFieldV(ballisticCoefficient, TypeF32, afxMagicMissileData, &missileBallisticCoefficientValidator); 388 389 addField("collisionMask", TypeS32, myOffset(collision_mask)); 390 391 addField("followTerrain", TypeBool, myOffset(followTerrain)); 392 addField("followTerrainHeight", TypeF32, myOffset(followTerrainHeight)); 393 addField("followTerrainAdjustRate", TypeF32, myOffset(followTerrainAdjustRate)); 394 addFieldV("followTerrainAdjustDelay", TypeS32, myOffset(followTerrainAdjustDelay), &ticksFromMS); 395 396 addNamedField(acceleration, TypeF32, afxMagicMissileData); 397 addNamedFieldV(accelDelay, TypeS32, afxMagicMissileData, &ticksFromMS); 398 addNamedFieldV(accelLifetime, TypeS32, afxMagicMissileData, &ticksFromMS); 399 400 addField("launchNode", TypeString, myOffset(launch_node)); 401 addField("launchOffset", TypePoint3F, myOffset(launch_offset)); 402 addField("launchOffsetServer",TypePoint3F, myOffset(launch_offset_server)); 403 addField("launchOffsetClient",TypePoint3F, myOffset(launch_offset_client)); 404 addField("launchNodeOffset", TypePoint3F, myOffset(launch_node_offset)); 405 addField("launchAimPitch", TypeF32, myOffset(launch_pitch)); 406 addField("launchAimPan", TypeF32, myOffset(launch_pan)); 407 addField("launchConstraintServer", TypeString, myOffset(launch_cons_s_spec)); 408 addField("launchConstraintClient", TypeString, myOffset(launch_cons_c_spec)); 409 // 410 addField("echoLaunchOffset", TypeBool, myOffset(echo_launch_offset)); 411 412 addField("wiggleMagnitudes", TypeF32Vector, myOffset(wiggle_magnitudes)); 413 addField("wiggleSpeeds", TypeF32Vector, myOffset(wiggle_speeds)); 414 addField("wiggleAxis", TypeString, myOffset(wiggle_axis_string)); 415 416 addField("hoverAltitude", TypeF32, myOffset(hover_altitude)); 417 addField("hoverAttackDistance", TypeF32, myOffset(hover_attack_distance)); 418 addField("hoverAttackGradient", TypeF32, myOffset(hover_attack_gradient)); 419 addFieldV("hoverTime", TypeS32, myOffset(hover_time), &ticksFromMS); 420 421 addField("reverseTargeting", TypeBool, myOffset(reverse_targeting)); 422 423 addFieldV("casterSafetyTime", TypeS32, myOffset(caster_safety_time), &ticksFromMS); 424 425 Parent::initPersistFields(); 426 427 // disallow some field substitutions 428 onlyKeepClearSubstitutions("particleEmitter"); // subs resolving to "~~", or "~0" are OK 429 onlyKeepClearSubstitutions("particleWaterEmitter"); 430 onlyKeepClearSubstitutions("sound"); 431 onlyKeepClearSubstitutions("splash"); 432} 433 434 435//-------------------------------------------------------------------------- 436bool afxMagicMissileData::onAdd() 437{ 438 if(!Parent::onAdd()) 439 return false; 440 441 // ADDED BY MAGIC-MISSILE 442 443 // Wiggle axes //////////////////////////////////////////////////////////// 444 if (wiggle_axis_string != ST_NULLSTRING && wiggle_num_axis == 0) 445 { 446 // Tokenize input string and convert to Point3F array 447 // 448 Vector<char*> dataBlocks(__FILE__, __LINE__); 449 450 // make a copy of points_string 451 dsize_t tokCopyLen = dStrlen(wiggle_axis_string) + 1; 452 char* tokCopy = new char[tokCopyLen]; 453 dStrcpy(tokCopy, wiggle_axis_string, tokCopyLen); 454 455 // extract tokens one by one, adding them to dataBlocks 456 char* currTok = dStrtok(tokCopy, " \t"); 457 while (currTok != NULL) 458 { 459 dataBlocks.push_back(currTok); 460 currTok = dStrtok(NULL, " \t"); 461 } 462 463 // bail if there were no tokens in the string 464 if (dataBlocks.size() == 0) 465 { 466 Con::warnf(ConsoleLogEntry::General, "afxMagicMissileData(%s) invalid wiggle axis string. No tokens found", getName()); 467 delete [] tokCopy; 468 return false; 469 } 470 471 // Find wiggle_num_axis (round up to multiple of 3) // WARNING here if not multiple of 3? 472 for (U32 i = 0; i < dataBlocks.size()%3; i++) 473 { 474 dataBlocks.push_back("0.0"); 475 } 476 477 wiggle_num_axis = dataBlocks.size()/3; 478 wiggle_axis = new Point3F[wiggle_num_axis]; 479 480 U32 p_i = 0; 481 for (U32 i = 0; i < dataBlocks.size(); i+=3, p_i++) 482 { 483 F32 x,y,z; 484 x = dAtof(dataBlocks[i]); // What about overflow? 485 y = dAtof(dataBlocks[i+1]); 486 z = dAtof(dataBlocks[i+2]); 487 wiggle_axis[p_i].set(x,y,z); 488 489 wiggle_axis[p_i].normalizeSafe(); // sufficient???? 490 } 491 492 delete [] tokCopy; 493 } 494 495 launch_cons_s_def.parseSpec(launch_cons_s_spec, true, false); 496 launch_cons_c_def.parseSpec(launch_cons_c_spec, false, true); 497 498 return true; 499} 500 501 502bool afxMagicMissileData::preload(bool server, String &errorStr) 503{ 504 if (Parent::preload(server, errorStr) == false) 505 return false; 506 507 if( !server ) 508 { 509 if (!particleEmitter && particleEmitterId != 0) 510 if (Sim::findObject(particleEmitterId, particleEmitter) == false) 511 Con::errorf(ConsoleLogEntry::General, "afxMagicMissileData::preload: Invalid packet, bad datablockId(particleEmitter): %d", particleEmitterId); 512 513 if (!particleWaterEmitter && particleWaterEmitterId != 0) 514 if (Sim::findObject(particleWaterEmitterId, particleWaterEmitter) == false) 515 Con::errorf(ConsoleLogEntry::General, "afxMagicMissileData::preload: Invalid packet, bad datablockId(particleWaterEmitter): %d", particleWaterEmitterId); 516 517 /* From stock Projectile code... 518 if (!explosion && explosionId != 0) 519 if (Sim::findObject(explosionId, explosion) == false) 520 Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(explosion): %d", explosionId); 521 522 if (!waterExplosion && waterExplosionId != 0) 523 if (Sim::findObject(waterExplosionId, waterExplosion) == false) 524 Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(waterExplosion): %d", waterExplosionId); 525 */ 526 527 if (!splash && splashId != 0) 528 if (Sim::findObject(splashId, splash) == false) 529 Con::errorf(ConsoleLogEntry::General, "afxMagicMissileData::preload: Invalid packet, bad datablockId(splash): %d", splashId); 530 531 /* From stock Projectile code... 532 if (!decal && decalId != 0) 533 if (Sim::findObject(decalId, decal) == false) 534 Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(decal): %d", decalId); 535 */ 536 537 String sfxErrorStr; 538 if( !sfxResolve( &sound, sfxErrorStr) ) 539 Con::errorf(ConsoleLogEntry::General, "afxMagicMissileData::preload: Invalid packet: %s", sfxErrorStr.c_str()); 540 541 if (!lightDesc && lightDescId != 0) 542 if (Sim::findObject(lightDescId, lightDesc) == false) 543 Con::errorf(ConsoleLogEntry::General, "afxMagicMissileData::preload: Invalid packet, bad datablockid(lightDesc): %d", lightDescId); 544 } 545 546 if (projectileShapeName != ST_NULLSTRING) 547 { 548 projectileShape = ResourceManager::get().load(projectileShapeName); 549 if (bool(projectileShape) == false) 550 { 551 errorStr = String::ToString("afxMagicMissileData::load: Couldn't load shape \"%s\"", projectileShapeName); 552 return false; 553 } 554 /* From stock Projectile code... 555 activateSeq = projectileShape->findSequence("activate"); 556 maintainSeq = projectileShape->findSequence("maintain"); 557 */ 558 } 559 560 if (bool(projectileShape)) // create an instance to preload shape data 561 { 562 TSShapeInstance* pDummy = new TSShapeInstance(projectileShape, !server); 563 delete pDummy; 564 } 565 566 return true; 567} 568 569//-------------------------------------------------------------------------- 570// Modified from floorPlanRes.cc 571// Read a vector of items 572template <class T> 573bool readVector(Vector<T> & vec, Stream & stream, const char * msg) 574{ 575 U32 num, i; 576 bool Ok = true; 577 stream.read( & num ); 578 vec.setSize( num ); 579 for( i = 0; i < num && Ok; i++ ){ 580 Ok = stream.read(& vec[i]); 581 AssertISV( Ok, avar("math vec read error (%s) on elem %d", msg, i) ); 582 } 583 return Ok; 584} 585// Write a vector of items 586template <class T> 587bool writeVector(const Vector<T> & vec, Stream & stream, const char * msg) 588{ 589 bool Ok = true; 590 stream.write( vec.size() ); 591 for( U32 i = 0; i < vec.size() && Ok; i++ ) { 592 Ok = stream.write(vec[i]); 593 AssertISV( Ok, avar("vec write error (%s) on elem %d", msg, i) ); 594 } 595 return Ok; 596} 597//-------------------------------------------------------------------------- 598 599void afxMagicMissileData::packData(BitStream* stream) 600{ 601 Parent::packData(stream); 602 603 stream->writeString(projectileShapeName); 604 /* From stock Projectile code... 605 stream->writeFlag(faceViewer); 606 */ 607 if(stream->writeFlag(scale.x != 1 || scale.y != 1 || scale.z != 1)) 608 { 609 stream->write(scale.x); 610 stream->write(scale.y); 611 stream->write(scale.z); 612 } 613 614 stream->write(collision_mask); 615 616 if (stream->writeFlag(particleEmitter != NULL)) 617 stream->writeRangedU32(particleEmitter->getId(), DataBlockObjectIdFirst, 618 DataBlockObjectIdLast); 619 620 if (stream->writeFlag(particleWaterEmitter != NULL)) 621 stream->writeRangedU32(particleWaterEmitter->getId(), DataBlockObjectIdFirst, 622 DataBlockObjectIdLast); 623 624 /* From stock Projectile code... 625 if (stream->writeFlag(explosion != NULL)) 626 stream->writeRangedU32(explosion->getId(), DataBlockObjectIdFirst, 627 DataBlockObjectIdLast); 628 629 if (stream->writeFlag(waterExplosion != NULL)) 630 stream->writeRangedU32(waterExplosion->getId(), DataBlockObjectIdFirst, 631 DataBlockObjectIdLast); 632 */ 633 634 if (stream->writeFlag(splash != NULL)) 635 stream->writeRangedU32(splash->getId(), DataBlockObjectIdFirst, 636 DataBlockObjectIdLast); 637 638 /* From stock Projectile code... 639 if (stream->writeFlag(decal != NULL)) 640 stream->writeRangedU32(decal->getId(), DataBlockObjectIdFirst, 641 DataBlockObjectIdLast); 642 */ 643 644 sfxWrite( stream, sound ); 645 646 if ( stream->writeFlag(lightDesc != NULL)) 647 stream->writeRangedU32(lightDesc->getId(), DataBlockObjectIdFirst, 648 DataBlockObjectIdLast); 649 650 /* From stock Projectile code... 651 stream->write(impactForce); 652 */ 653 654 stream->write(lifetime); 655 /* From stock Projectile code... 656 stream->write(armingDelay); 657 stream->write(fadeDelay); 658 */ 659 660 if(stream->writeFlag(isBallistic)) 661 { 662 stream->write(gravityMod); 663 /* From stock Projectile code... 664 stream->write(bounceElasticity); 665 stream->write(bounceFriction); 666 */ 667 stream->write(ballisticCoefficient); 668 } 669 670 if(stream->writeFlag(isGuided)) 671 { 672 stream->write(precision); 673 stream->write(trackDelay); 674 } 675 676 stream->write(muzzleVelocity); 677 mathWrite(*stream, starting_vel_vec); 678 stream->write(acceleration); 679 stream->write(accelDelay); 680 stream->write(accelLifetime); 681 682 stream->writeString(launch_node); 683 mathWrite(*stream, launch_offset); 684 mathWrite(*stream, launch_offset_server); 685 mathWrite(*stream, launch_offset_client); 686 mathWrite(*stream, launch_node_offset); 687 stream->write(launch_pitch); 688 stream->write(launch_pan); 689 stream->writeString(launch_cons_c_spec); 690 stream->writeFlag(echo_launch_offset); 691 692 writeVector(wiggle_magnitudes, *stream, "afxMagicMissile: wiggle_magnitudes"); 693 writeVector(wiggle_speeds, *stream, "afxMagicMissile: wiggle_speeds"); 694 695 stream->write(wiggle_num_axis); 696 for (U32 i = 0; i < wiggle_num_axis; i++) 697 mathWrite(*stream, wiggle_axis[i]); 698 699 stream->write(hover_altitude); 700 stream->write(hover_attack_distance); 701 stream->write(hover_attack_gradient); 702 stream->writeRangedU32(hover_time, 0, MaxLifetimeTicks); 703 704 stream->writeFlag(reverse_targeting); 705 706 stream->write(caster_safety_time); 707} 708 709void afxMagicMissileData::unpackData(BitStream* stream) 710{ 711 Parent::unpackData(stream); 712 713 projectileShapeName = stream->readSTString(); 714 /* From stock Projectile code... 715 faceViewer = stream->readFlag(); 716 */ 717 if(stream->readFlag()) 718 { 719 stream->read(&scale.x); 720 stream->read(&scale.y); 721 stream->read(&scale.z); 722 } 723 else 724 scale.set(1,1,1); 725 726 stream->read(&collision_mask); 727 728 if (stream->readFlag()) 729 particleEmitterId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); 730 731 if (stream->readFlag()) 732 particleWaterEmitterId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); 733 734 /* From stock Projectile code... 735 if (stream->readFlag()) 736 explosionId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); 737 738 if (stream->readFlag()) 739 waterExplosionId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); 740 */ 741 742 if (stream->readFlag()) 743 splashId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); 744 745 /* From stock Projectile code... 746 if (stream->readFlag()) 747 decalId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); 748 */ 749 750 sfxRead( stream, &sound ); 751 752 if (stream->readFlag()) 753 lightDescId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); 754 755 /* From stock Projectile code... 756 stream->read(&impactForce); 757 */ 758 759 stream->read(&lifetime); 760 /* From stock Projectile code... 761 stream->read(&armingDelay); 762 stream->read(&fadeDelay); 763 */ 764 765 isBallistic = stream->readFlag(); 766 if(isBallistic) 767 { 768 stream->read(&gravityMod); 769 /* From stock Projectile code... 770 stream->read(&bounceElasticity); 771 stream->read(&bounceFriction); 772 */ 773 stream->read(&ballisticCoefficient); 774 } 775 776 isGuided = stream->readFlag(); 777 if(isGuided) 778 { 779 stream->read(&precision); 780 stream->read(&trackDelay); 781 } 782 783 stream->read(&muzzleVelocity); 784 mathRead(*stream, &starting_vel_vec); 785 stream->read(&acceleration); 786 stream->read(&accelDelay); 787 stream->read(&accelLifetime); 788 789 launch_node = stream->readSTString(); 790 mathRead(*stream, &launch_offset); 791 mathRead(*stream, &launch_offset_server); 792 mathRead(*stream, &launch_offset_client); 793 mathRead(*stream, &launch_node_offset); 794 stream->read(&launch_pitch); 795 stream->read(&launch_pan); 796 launch_cons_c_spec = stream->readSTString(); 797 echo_launch_offset = stream->readFlag(); 798 799 readVector(wiggle_magnitudes, *stream, "afxMagicMissile: wiggle_magnitudes"); 800 readVector(wiggle_speeds, *stream, "afxMagicMissile: wiggle_speeds"); 801 802 if (wiggle_axis) 803 delete [] wiggle_axis; 804 wiggle_axis = 0; 805 wiggle_num_axis = 0; 806 807 stream->read(&wiggle_num_axis); 808 if (wiggle_num_axis > 0) 809 { 810 wiggle_axis = new Point3F[wiggle_num_axis]; 811 for (U32 i = 0; i < wiggle_num_axis; i++) 812 mathRead(*stream, &wiggle_axis[i]); 813 } 814 815 stream->read(&hover_altitude); 816 stream->read(&hover_attack_distance); 817 stream->read(&hover_attack_gradient); 818 hover_time = stream->readRangedU32(0, MaxLifetimeTicks); 819 820 reverse_targeting = stream->readFlag(); 821 822 stream->read(&caster_safety_time); 823} 824 825/* From stock Projectile code... 826bool ProjectileData::setLifetime( void *obj, const char *index, const char *data ) 827{ 828 S32 value = dAtoi(data); 829 value = scaleValue(value); 830 831 ProjectileData *object = static_cast<ProjectileData*>(obj); 832 object->lifetime = value; 833 834 return false; 835} 836 837bool ProjectileData::setArmingDelay( void *obj, const char *index, const char *data ) 838{ 839 S32 value = dAtoi(data); 840 value = scaleValue(value); 841 842 ProjectileData *object = static_cast<ProjectileData*>(obj); 843 object->armingDelay = value; 844 845 return false; 846} 847 848bool ProjectileData::setFadeDelay( void *obj, const char *index, const char *data ) 849{ 850 S32 value = dAtoi(data); 851 value = scaleValue(value); 852 853 ProjectileData *object = static_cast<ProjectileData*>(obj); 854 object->fadeDelay = value; 855 856 return false; 857} 858 859const char *ProjectileData::getScaledValue( void *obj, const char *data) 860{ 861 862 S32 value = dAtoi(data); 863 value = scaleValue(value, false); 864 865 String stringData = String::ToString(value); 866 char *strBuffer = Con::getReturnBuffer(stringData.size()); 867 dMemcpy( strBuffer, stringData, stringData.size() ); 868 869 return strBuffer; 870} 871 872S32 ProjectileData::scaleValue( S32 value, bool down ) 873{ 874 S32 minV = 0; 875 S32 maxV = Projectile::MaxLivingTicks; 876 877 // scale down to ticks before we validate 878 if( down ) 879 value /= TickMs; 880 881 if(value < minV || value > maxV) 882 { 883 Con::errorf("ProjectileData::scaleValue(S32 value = %d, bool down = %b) - Scaled value must be between %d and %d", value, down, minV, maxV); 884 if(value < minV) 885 value = minV; 886 else if(value > maxV) 887 value = maxV; 888 } 889 890 // scale up from ticks after we validate 891 if( !down ) 892 value *= TickMs; 893 894 return value; 895} 896*/ 897 898void afxMagicMissileData::gather_cons_defs(Vector<afxConstraintDef>& defs) 899{ 900 if (launch_cons_s_def.isDefined()) 901 defs.push_back(launch_cons_s_def); 902 if (launch_cons_c_def.isDefined()) 903 defs.push_back(launch_cons_c_def); 904}; 905 906//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 907//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 908// afxMagicMissile 909 910afxMagicMissile::afxMagicMissile() 911{ 912 init(true, true); 913} 914 915afxMagicMissile::afxMagicMissile(bool on_server, bool on_client) 916{ 917 init(on_server, on_client); 918} 919 920//-------------------------------------------------------------------------- 921//-------------------------------------- 922// 923void afxMagicMissile::init(bool on_server, bool on_client) 924{ 925 mPhysicsWorld = NULL; 926 927 mTypeMask |= ProjectileObjectType | LightObjectType; 928 929 mLight = LightManager::createLightInfo(); 930 mLight->setType( LightInfo::Point ); 931 932 mLightState.clear(); 933 mLightState.setLightInfo( mLight ); 934 935 mCurrPosition.zero(); 936 mCurrVelocity.set(0, 0, 1); 937 938 mCurrTick = 0; 939 940 mParticleEmitter = NULL; 941 mParticleWaterEmitter = NULL; 942 mSound = 0; 943 944 mProjectileShape = NULL; 945 946 mDataBlock = NULL; 947 948 choreographer = NULL; 949 950 if (on_server != on_client) 951 { 952 client_only = on_client; 953 server_only = on_server; 954 mNetFlags.clear(Ghostable | ScopeAlways); 955 if (client_only) 956 mNetFlags.set(IsGhost); 957 } 958 else 959 { 960 // note -- setting neither server or client makes no sense so we 961 // treat as if both are set. 962 mNetFlags.set(Ghostable | ScopeAlways); 963 client_only = server_only = false; 964 } 965 966 mCurrDeltaBase.zero(); 967 mCurrBackDelta.zero(); 968 collision_mask = 0; 969 prec_inc = 0.0f; 970 971 did_launch = false; 972 did_impact = false; 973 974 missile_target = NULL; 975 collide_exempt = NULL; 976 977 use_accel = false; 978 979 hover_attack_go = false; 980 hover_attack_tick = 0; 981 982 starting_velocity = 0.0; 983 starting_vel_vec.zero(); 984 985 ss_object = 0; 986 ss_index = 0; 987} 988 989afxMagicMissile::~afxMagicMissile() 990{ 991 SAFE_DELETE(mLight); 992 993 delete mProjectileShape; 994 mProjectileShape = NULL; 995} 996 997//-------------------------------------------------------------------------- 998void afxMagicMissile::initPersistFields() 999{ 1000 addGroup("Physics"); 1001 addField("initialPosition", TypePoint3F, Offset(mCurrPosition, afxMagicMissile) , 1002 "Initial starting position for this missile."); 1003 addField("initialVelocity", TypePoint3F, Offset(mCurrVelocity, afxMagicMissile) , 1004 "Initial starting velocity for this missile."); 1005 endGroup("Physics"); 1006 1007 /* From stock Projectile code... 1008 addGroup("Source"); 1009 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."); 1010 addField("sourceSlot", TypeS32, Offset(mSourceObjectSlot, Projectile) ,"Which weapon slot on the sourceObject that this projectile originates from."); 1011 endGroup("Source"); 1012 */ 1013 1014 Parent::initPersistFields(); 1015} 1016 1017/* From stock Projectile code... 1018bool Projectile::calculateImpact(float, 1019 Point3F& pointOfImpact, 1020 float& impactTime) 1021{ 1022 Con::warnf(ConsoleLogEntry::General, "Projectile::calculateImpact: Should never be called"); 1023 1024 impactTime = 0; 1025 pointOfImpact.set(0, 0, 0); 1026 return false; 1027} 1028 1029 1030//-------------------------------------------------------------------------- 1031F32 Projectile::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 updateSkips) 1032{ 1033 F32 ret = Parent::getUpdatePriority(camInfo, updateMask, updateSkips); 1034 // if the camera "owns" this object, it should have a slightly higher priority 1035 if(mSourceObject == camInfo->camera) 1036 return ret + 0.2; 1037 return ret; 1038} 1039*/ 1040 1041bool afxMagicMissile::onAdd() 1042{ 1043 if(!Parent::onAdd()) 1044 return false; 1045 1046 if (isServerObject()) 1047 { 1048 /* From stock Projectile code... 1049 ShapeBase* ptr; 1050 if (Sim::findObject(mSourceObjectId, ptr)) 1051 { 1052 mSourceObject = ptr; 1053 1054 // Since we later do processAfter( mSourceObject ) we must clearProcessAfter 1055 // if it is deleted. SceneObject already handles this in onDeleteNotify so 1056 // all we need to do is register for the notification. 1057 deleteNotify( ptr ); 1058 } 1059 else 1060 { 1061 if (mSourceObjectId != -1) 1062 Con::errorf(ConsoleLogEntry::General, "Projectile::onAdd: mSourceObjectId is invalid"); 1063 mSourceObject = NULL; 1064 } 1065 1066 // If we're on the server, we need to inherit some of our parent's velocity 1067 // 1068 mCurrTick = 0; 1069 */ 1070 } 1071 else 1072 { 1073 if (bool(mDataBlock->projectileShape)) 1074 { 1075 mProjectileShape = new TSShapeInstance(mDataBlock->projectileShape, true); 1076 /* From stock Projectile code... 1077 if (mDataBlock->activateSeq != -1) 1078 { 1079 mActivateThread = mProjectileShape->addThread(); 1080 mProjectileShape->setTimeScale(mActivateThread, 1); 1081 mProjectileShape->setSequence(mActivateThread, mDataBlock->activateSeq, 0); 1082 } 1083 */ 1084 } 1085 if (mDataBlock->particleEmitter != NULL) 1086 { 1087 ParticleEmitter* pEmitter = new ParticleEmitter; 1088 //pEmitter->setDataBlock(mDataBlock->particleEmitter->cloneAndPerformSubstitutions(ss_object, ss_index)); 1089 pEmitter->onNewDataBlock(mDataBlock->particleEmitter->cloneAndPerformSubstitutions(ss_object, ss_index), false); 1090 if (pEmitter->registerObject() == false) 1091 { 1092 Con::warnf(ConsoleLogEntry::General, "Could not register particle emitter for particle of class: %s", mDataBlock->getName()); 1093 delete pEmitter; 1094 pEmitter = NULL; 1095 } 1096 mParticleEmitter = pEmitter; 1097 } 1098 1099 if (mDataBlock->particleWaterEmitter != NULL) 1100 { 1101 ParticleEmitter* pEmitter = new ParticleEmitter; 1102 pEmitter->onNewDataBlock(mDataBlock->particleWaterEmitter->cloneAndPerformSubstitutions(ss_object, ss_index), false); 1103 if (pEmitter->registerObject() == false) 1104 { 1105 Con::warnf(ConsoleLogEntry::General, "Could not register particle emitter for particle of class: %s", mDataBlock->getName()); 1106 delete pEmitter; 1107 pEmitter = NULL; 1108 } 1109 mParticleWaterEmitter = pEmitter; 1110 } 1111 } 1112 /* From stock Projectile code... 1113 if (mSourceObject.isValid()) 1114 processAfter(mSourceObject); 1115 */ 1116 1117 // detect for acceleration 1118 use_accel = (mDataBlock->acceleration != 0 && mDataBlock->accelLifetime > 0); 1119 1120 // Setup our bounding box 1121 if (bool(mDataBlock->projectileShape) == true) 1122 mObjBox = mDataBlock->projectileShape->mBounds; 1123 else 1124 mObjBox = Box3F(Point3F(0, 0, 0), Point3F(0, 0, 0)); 1125 resetWorldBox(); 1126 addToScene(); 1127 1128 if ( PHYSICSMGR ) 1129 mPhysicsWorld = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); 1130 1131 return true; 1132} 1133 1134 1135void afxMagicMissile::onRemove() 1136{ 1137 if(mParticleEmitter) 1138 { 1139 mParticleEmitter->deleteWhenEmpty(); 1140 mParticleEmitter = NULL; 1141 } 1142 1143 if(mParticleWaterEmitter) 1144 { 1145 mParticleWaterEmitter->deleteWhenEmpty(); 1146 mParticleWaterEmitter = NULL; 1147 } 1148 1149 SFX_DELETE( mSound ); 1150 1151 removeFromScene(); 1152 Parent::onRemove(); 1153} 1154 1155 1156bool afxMagicMissile::onNewDataBlock(GameBaseData* dptr, bool reload) 1157{ 1158 mDataBlock = dynamic_cast<afxMagicMissileData*>(dptr); 1159 if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) 1160 return false; 1161 1162 starting_velocity = mDataBlock->muzzleVelocity; 1163 starting_vel_vec = mDataBlock->starting_vel_vec; 1164 collision_mask = mDataBlock->collision_mask; 1165 1166 if ( isGhost() ) 1167 { 1168 // Create the sound ahead of time. This reduces runtime 1169 // costs and makes the system easier to understand. 1170 1171 SFX_DELETE( mSound ); 1172 1173 if ( mDataBlock->sound ) 1174 mSound = SFX->createSource( mDataBlock->sound ); 1175 } 1176 1177 return true; 1178} 1179 1180 1181//-------------------------------------------------------------------------- 1182 1183void afxMagicMissile::submitLights( LightManager *lm, bool staticLighting ) 1184{ 1185 if ( staticLighting || isHidden() || !mDataBlock->lightDesc ) 1186 return; 1187 1188 mDataBlock->lightDesc->submitLight( &mLightState, getRenderTransform(), lm, this ); 1189} 1190 1191bool afxMagicMissile::pointInWater(const Point3F &point) 1192{ 1193 // This is pretty much a hack so we can use the existing ContainerQueryInfo 1194 // and findObject router. 1195 1196 // We only care if we intersect with water at all 1197 // so build a box at the point that has only 1 z extent. 1198 // And test if water coverage is anything other than zero. 1199 1200 Box3F boundsBox( point, point ); 1201 boundsBox.maxExtents.z += 1.0f; 1202 1203 ContainerQueryInfo info; 1204 info.box = boundsBox; 1205 info.mass = 0.0f; 1206 1207 // Find and retreive physics info from intersecting WaterObject(s) 1208 if(mContainer != NULL) 1209 { 1210 mContainer->findObjects( boundsBox, WaterObjectType, findRouter, &info ); 1211 } 1212 else 1213 { 1214 // Handle special case where the projectile has exploded prior to having 1215 // called onAdd() on the client. This occurs when the projectile on the 1216 // server is created and then explodes in the same network update tick. 1217 // On the client end in NetConnection::ghostReadPacket() the ghost is 1218 // created and then Projectile::unpackUpdate() is called prior to the 1219 // projectile being registered. Within unpackUpdate() the explosion 1220 // is triggered, but without being registered onAdd() isn't called and 1221 // the container is not set. As all we're doing is checking if the 1222 // given explosion point is within water, we should be able to use the 1223 // global container here. We could likely always get away with this, 1224 // but using the actual defined container when possible is the right 1225 // thing to do. DAW 1226 AssertFatal(isClientObject(), "Server projectile has not been properly added"); 1227 gClientContainer.findObjects( boundsBox, WaterObjectType, findRouter, &info ); 1228 } 1229 1230 return ( info.waterCoverage > 0.0f ); 1231} 1232 1233//---------------------------------------------------------------------------- 1234 1235void afxMagicMissile::emitParticles(const Point3F& from, const Point3F& to, const Point3F& vel, const U32 ms) 1236{ 1237 /* From stock Projectile code... 1238 if ( isHidden() ) 1239 return; 1240 */ 1241 1242 Point3F axis = -vel; 1243 1244 if( axis.isZero() ) 1245 axis.set( 0.0, 0.0, 1.0 ); 1246 else 1247 axis.normalize(); 1248 1249 bool fromWater = pointInWater(from); 1250 bool toWater = pointInWater(to); 1251 1252 if (!fromWater && !toWater && bool(mParticleEmitter)) // not in water 1253 mParticleEmitter->emitParticles(from, to, axis, vel, ms); 1254 else if (fromWater && toWater && bool(mParticleWaterEmitter)) // in water 1255 mParticleWaterEmitter->emitParticles(from, to, axis, vel, ms); 1256 else if (!fromWater && toWater) // entering water 1257 { 1258 // cast the ray to get the surface point of the water 1259 RayInfo rInfo; 1260 if (gClientContainer.castRay(from, to, WaterObjectType, &rInfo)) 1261 { 1262 create_splash(rInfo.point); 1263 1264 // create an emitter for the particles out of water and the particles in water 1265 if (mParticleEmitter) 1266 mParticleEmitter->emitParticles(from, rInfo.point, axis, vel, ms); 1267 1268 if (mParticleWaterEmitter) 1269 mParticleWaterEmitter->emitParticles(rInfo.point, to, axis, vel, ms); 1270 } 1271 } 1272 else if (fromWater && !toWater) // leaving water 1273 { 1274 // cast the ray in the opposite direction since that point is out of the water, otherwise 1275 // we hit water immediately and wont get the appropriate surface point 1276 RayInfo rInfo; 1277 if (gClientContainer.castRay(to, from, WaterObjectType, &rInfo)) 1278 { 1279 create_splash(rInfo.point); 1280 1281 // create an emitter for the particles out of water and the particles in water 1282 if (mParticleEmitter) 1283 mParticleEmitter->emitParticles(rInfo.point, to, axis, vel, ms); 1284 1285 if (mParticleWaterEmitter) 1286 mParticleWaterEmitter->emitParticles(from, rInfo.point, axis, vel, ms); 1287 } 1288 } 1289} 1290 1291void afxMagicMissile::processTick(const Move* move) 1292{ 1293 Parent::processTick( move ); 1294 1295 // only active from launch to impact 1296 if (!is_active()) 1297 return; 1298 1299 mCurrTick++; 1300 1301 // missile fizzles out by exceeding lifetime 1302 if ((isServerObject() || client_only) && mCurrTick >= mDataBlock->lifetime) 1303 { 1304 did_impact = true; 1305 setMaskBits(ImpactMask); 1306 if (choreographer) 1307 { 1308 Point3F n = mCurrVelocity; n.normalizeSafe(); 1309 choreographer->impactNotify(mCurrPosition, n, 0); 1310 } 1311 Sim::postEvent(this, new ObjectDeleteEvent, Sim::getCurrentTime() + 500); 1312 return; 1313 } 1314 1315 static F32 dT = F32(TickMs)*0.001f; 1316 1317 Point3F old_pos = mCurrPosition; 1318 1319 // adjust missile velocity from gravity and drag influences 1320 if (mDataBlock->isBallistic) 1321 { 1322 F32 dV = (1 - mDataBlock->ballisticCoefficient)*dT; 1323 Point3F d(mCurrVelocity.x*dV, mCurrVelocity.y*dV, 9.81f*mDataBlock->gravityMod*dT); 1324 mCurrVelocity -= d; 1325 } 1326 1327 // adjust missile velocity from acceleration 1328 if (use_accel) 1329 { 1330 if (mCurrTick > mDataBlock->accelDelay && 1331 mCurrTick <= mDataBlock->accelDelay + mDataBlock->accelLifetime) 1332 { 1333 Point3F d = mCurrVelocity; d.normalizeSafe(); 1334 mCurrVelocity += d*mDataBlock->acceleration*dT; 1335 } 1336 } 1337 1338 // adjust mCurrVelocity from guidance system influences 1339 if (mDataBlock->isGuided && missile_target && mCurrTick > mDataBlock->trackDelay) 1340 { 1341 // get the position tracked by the guidance system 1342 Point3F target_pos = missile_target->getBoxCenter(); 1343 1344 Point3F target_vec = target_pos - mCurrPosition; 1345 1346 F32 target_dist_sq = target_vec.lenSquared(); 1347 if (target_dist_sq < 4.0f) 1348 prec_inc += 1.0f; 1349 1350 // hover 1351 if (mDataBlock->hover_altitude > 0.0f) 1352 { 1353 Point3F target_vec_xy(target_vec.x, target_vec.y, 0); 1354 F32 xy_dist = target_vec_xy.len(); 1355 1356 if (xy_dist > mDataBlock->hover_attack_distance) 1357 { 1358 hover_attack_go = false; 1359 1360 if (xy_dist > mDataBlock->hover_attack_distance + mDataBlock->hover_attack_gradient) 1361 { 1362 target_pos.z += mDataBlock->hover_altitude; 1363 } 1364 else 1365 { 1366 target_pos.z += afxEase::eq( (xy_dist-mDataBlock->hover_attack_distance)/<a href="/coding/class/classafxmagicmissile/#classafxmagicmissile_1ae9f94d9860633b87b372e3ff11eebcca">mDataBlock</a>->hover_attack_gradient, 1367 0.0f, mDataBlock->hover_altitude, 1368 0.25f, 0.75f); 1369 } 1370 target_vec = target_pos - mCurrPosition; 1371 } 1372 1373 else 1374 { 1375 if (!hover_attack_go) 1376 { 1377 hover_attack_go = true; 1378 hover_attack_tick = 0; 1379 } 1380 hover_attack_tick++; 1381 1382 if (hover_attack_tick < mDataBlock->hover_time) 1383 { 1384 target_pos.z += mDataBlock->hover_altitude; 1385 target_vec = target_pos - mCurrPosition; 1386 } 1387 } 1388 } 1389 1390 // apply precision 1391 1392 // extract speed 1393 F32 speed = mCurrVelocity.len(); 1394 1395 // normalize vectors 1396 target_vec.normalizeSafe(); 1397 mCurrVelocity.normalize(); 1398 1399 F32 prec = mDataBlock->precision; 1400 1401 // fade in precision gradually to avoid sudden turn 1402 if (mCurrTick < mDataBlock->trackDelay + 16) 1403 prec *= (mCurrTick - mDataBlock->trackDelay)/16.0f; 1404 1405 prec += prec_inc; 1406 if (prec > 100) 1407 prec = 100; 1408 1409 // apply precision weighting 1410 target_vec *= prec; 1411 mCurrVelocity *= (100 - prec); 1412 1413 mCurrVelocity += target_vec; 1414 mCurrVelocity.normalize(); 1415 mCurrVelocity *= speed; 1416 } 1417 1418 // wiggle 1419 for (U32 i = 0; i < mDataBlock->wiggle_num_axis; i++) 1420 { 1421 if (i >= mDataBlock->wiggle_magnitudes.size() || i >= mDataBlock->wiggle_speeds.size()) 1422 break; 1423 1424 F32 wiggle_mag = mDataBlock->wiggle_magnitudes[i]; 1425 F32 wiggle_speed = mDataBlock->wiggle_speeds[i]; 1426 Point3F wiggle_axis = mDataBlock->wiggle_axis[i]; 1427 //wiggle_axis.normalizeSafe(); // sufficient???? 1428 1429 F32 theta = wiggle_mag * mSin(wiggle_speed*(mCurrTick*TickSec)); 1430 //Con::printf( "theta: %f", theta ); 1431 AngAxisF thetaRot(wiggle_axis, theta); 1432 MatrixF temp(true); 1433 thetaRot.setMatrix(&temp); 1434 temp.mulP(mCurrVelocity); 1435 } 1436 1437 Point3F new_pos = old_pos + mCurrVelocity*dT; 1438 1439 // conform to terrain 1440 if (mDataBlock->followTerrain && mCurrTick >= mDataBlock->followTerrainAdjustDelay) 1441 { 1442 U32 mask = TerrainObjectType | TerrainLikeObjectType; // | InteriorObjectType; 1443 1444 F32 ht = mDataBlock->followTerrainHeight; 1445 F32 ht_rate = mDataBlock->followTerrainAdjustRate; 1446 F32 ht_min = 0.05f; 1447 if (ht < ht_min) 1448 ht = ht_min; 1449 1450 SceneContainer* container = (isServerObject()) ? &gServerContainer : &gClientContainer; 1451 Point3F above_pos = new_pos; above_pos.z += 10000; 1452 Point3F below_pos = new_pos; below_pos.z -= 10000; 1453 RayInfo rInfo; 1454 if (container && container->castRay(above_pos, below_pos, mask, &rInfo)) 1455 { 1456 F32 terrain_z = rInfo.point.z; 1457 F32 seek_z = terrain_z + ht; 1458 if (new_pos.z < seek_z) 1459 { 1460 new_pos.z += ht_rate*dT; 1461 if (new_pos.z > seek_z) 1462 new_pos.z = seek_z; 1463 } 1464 else if (new_pos.z > seek_z) 1465 { 1466 new_pos.z -= ht_rate*dT; 1467 if (new_pos.z < seek_z) 1468 new_pos.z = seek_z; 1469 } 1470 1471 if (new_pos.z < terrain_z + ht_min) 1472 new_pos.z = terrain_z + ht_min; 1473 } 1474 } 1475 1476 // only check for impacts on server 1477 if (isServerObject()) 1478 { 1479 // avoid collision with the spellcaster 1480 if (collide_exempt && mCurrTick <= mDataBlock->caster_safety_time) 1481 collide_exempt->disableCollision(); 1482 1483 // check for collision along ray from old to new position 1484 RayInfo rInfo; 1485 bool did_hit = false; 1486 1487 if (mPhysicsWorld) 1488 { 1489 // did_hit = mPhysicsWorld->castRay(old_pos, new_pos, &rInfo, Point3F(new_pos - old_pos) * mDataBlock->impactForce ); 1490 // Impulse currently hardcoded for testing purposes 1491 did_hit = mPhysicsWorld->castRay(old_pos, new_pos, &rInfo, Point3F(new_pos - old_pos) * 1000.0f ); 1492 } 1493 else 1494 { 1495 did_hit = getContainer()->castRay(old_pos, new_pos, collision_mask, &rInfo); 1496 } 1497 1498 // restore collisions on spellcaster 1499 if (collide_exempt && mCurrTick <= mDataBlock->caster_safety_time) 1500 collide_exempt->enableCollision(); 1501 1502 // process impact 1503 if (did_hit) 1504 { 1505 MatrixF xform(true); 1506 xform.setColumn(3, rInfo.point); 1507 setTransform(xform); 1508 mCurrPosition = rInfo.point; 1509 mCurrVelocity = Point3F(0, 0, 0); 1510 did_impact = true; 1511 setMaskBits(ImpactMask); 1512 if (choreographer) 1513 { 1514 choreographer->impactNotify(rInfo.point, rInfo.normal, rInfo.object); 1515 Sim::postEvent(this, new ObjectDeleteEvent, Sim::getCurrentTime() + 500); 1516 } 1517 } 1518 } 1519 else // if (isClientObject()) 1520 { 1521 emitParticles(mCurrPosition, new_pos, mCurrVelocity, TickMs); 1522 updateSound(); 1523 } 1524 1525 // interp values used in interpolateTick() 1526 mCurrDeltaBase = new_pos; 1527 mCurrBackDelta = mCurrPosition - new_pos; 1528 1529 mCurrPosition = new_pos; 1530 1531 MatrixF xform(true); 1532 xform.setColumn(3, mCurrPosition); 1533 setTransform(xform); 1534} 1535 1536void afxMagicMissile::interpolateTick(F32 delta) 1537{ 1538 Parent::interpolateTick(delta); 1539 1540 // only active from launch to impact 1541 if (!is_active()) 1542 return; 1543 1544 Point3F interpPos = mCurrDeltaBase + mCurrBackDelta * delta; 1545 Point3F dir = mCurrVelocity; 1546 if(dir.isZero()) 1547 dir.set(0,0,1); 1548 else 1549 dir.normalize(); 1550 1551 MatrixF xform(true); 1552 xform = MathUtils::createOrientFromDir(dir); 1553 xform.setPosition(interpPos); 1554 setRenderTransform(xform); 1555 1556 updateSound(); 1557} 1558 1559//-------------------------------------------------------------------------- 1560U32 afxMagicMissile::packUpdate(NetConnection* con, U32 mask, BitStream* stream) 1561{ 1562 U32 retMask = Parent::packUpdate( con, mask, stream ); 1563 1564 const bool isInitalUpdate = mask & GameBase::InitialUpdateMask; 1565 1566 // InitialUpdateMask 1567 if ( stream->writeFlag( isInitalUpdate ) ) 1568 { 1569 Point3F pos; 1570 getTransform().getColumn( 3, &pos ); 1571 stream->writeCompressedPoint( pos ); 1572 F32 len = mCurrVelocity.len(); 1573 1574 if ( stream->writeFlag( len > 0.02 ) ) 1575 { 1576 Point3F outVel = mCurrVelocity; 1577 outVel *= 1 / len; 1578 stream->writeNormalVector( outVel, 10 ); 1579 len *= 32.0; // 5 bits for fraction 1580 1581 if ( len > 8191 ) 1582 len = 8191; 1583 1584 stream->writeInt( (S32)len, 13 ); 1585 } 1586 1587 stream->writeRangedU32( mCurrTick, 0, afxMagicMissileData::MaxLifetimeTicks ); 1588 1589 if (choreographer) 1590 { 1591 S32 ghostIndex = con->getGhostIndex( choreographer ); 1592 if ( stream->writeFlag( ghostIndex != -1 ) ) 1593 { 1594 stream->writeRangedU32( U32(ghostIndex), 1595 0, 1596 NetConnection::MaxGhostCount ); 1597 } 1598 else 1599 // have not recieved the ghost for the source object yet, try again later 1600 retMask |= GameBase::InitialUpdateMask; 1601 } 1602 else 1603 stream->writeFlag( false ); 1604 } 1605 1606 // impact update 1607 if (stream->writeFlag(mask & ImpactMask)) 1608 { 1609 mathWrite(*stream, mCurrPosition); 1610 mathWrite(*stream, mCurrVelocity); 1611 stream->writeFlag(did_impact); 1612 } 1613 1614 // guided update 1615 if (stream->writeFlag(mask & GuideMask)) 1616 { 1617 mathWrite(*stream, mCurrPosition); 1618 mathWrite(*stream, mCurrVelocity); 1619 } 1620 1621 return retMask; 1622} 1623 1624void afxMagicMissile::unpackUpdate(NetConnection* con, BitStream* stream) 1625{ 1626 Parent::unpackUpdate(con, stream); 1627 1628 if ( stream->readFlag() ) // InitialUpdateMask 1629 { 1630 Point3F pos; 1631 stream->readCompressedPoint( &pos ); 1632 if ( stream->readFlag() ) 1633 { 1634 stream->readNormalVector( &mCurrVelocity, 10 ); 1635 mCurrVelocity *= stream->readInt( 13 ) / 32.0f; 1636 } 1637 else 1638 mCurrVelocity.zero(); 1639 1640 mCurrDeltaBase = pos; 1641 mCurrBackDelta = mCurrPosition - pos; 1642 mCurrPosition = pos; 1643 setPosition( mCurrPosition ); 1644 1645 mCurrTick = stream->readRangedU32(0, afxMagicMissileData::MaxLifetimeTicks); 1646 if ( stream->readFlag() ) 1647 { 1648 U32 id = stream->readRangedU32(0, NetConnection::MaxGhostCount); 1649 choreographer = dynamic_cast<afxChoreographer*>(con->resolveGhost(id)); 1650 if (choreographer) 1651 { 1652 deleteNotify(choreographer); 1653 } 1654 } 1655 else 1656 { 1657 if (choreographer) 1658 clearNotify(choreographer); 1659 choreographer = 0; 1660 } 1661 } 1662 1663 // impact update 1664 if (stream->readFlag()) 1665 { 1666 mathRead(*stream, &mCurrPosition); 1667 mathRead(*stream, &mCurrVelocity); 1668 did_impact = stream->readFlag(); 1669 1670 } 1671 1672 if (stream->readFlag()) // guided update 1673 { 1674 mathRead( *stream, &mCurrPosition ); 1675 mathRead( *stream, &mCurrVelocity ); 1676 } 1677} 1678 1679//-------------------------------------------------------------------------- 1680void afxMagicMissile::prepRenderImage(SceneRenderState* state) 1681{ 1682 if (!is_active()) 1683 return; 1684 1685 /* 1686 if (isHidden() || mFadeValue <= (1.0/255.0)) 1687 return; 1688 */ 1689 1690 if ( mDataBlock->lightDesc ) 1691 { 1692 mDataBlock->lightDesc->prepRender( state, &mLightState, getRenderTransform() ); 1693 } 1694 1695 /* 1696 if ( mFlareData ) 1697 { 1698 mFlareState.fullBrightness = mDataBlock->lightDesc->mBrightness; 1699 mFlareState.scale = mFlareScale; 1700 mFlareState.lightInfo = mLight; 1701 mFlareState.lightMat = getTransform(); 1702 1703 mFlareData->prepRender( state, &mFlareState ); 1704 } 1705 */ 1706 1707 prepBatchRender( state ); 1708} 1709 1710void afxMagicMissile::prepBatchRender(SceneRenderState* state) 1711{ 1712 if ( !mProjectileShape ) 1713 return; 1714 1715 GFXTransformSaver saver; 1716 1717 // Set up our TS render state. 1718 TSRenderState rdata; 1719 rdata.setSceneState( state ); 1720 1721 // We might have some forward lit materials 1722 // so pass down a query to gather lights. 1723 LightQuery query; 1724 query.init( getWorldSphere() ); 1725 rdata.setLightQuery( &query ); 1726 1727 MatrixF mat = getRenderTransform(); 1728 mat.scale( mObjScale ); 1729 mat.scale( mDataBlock->scale ); 1730 GFX->setWorldMatrix( mat ); 1731 1732 mProjectileShape->setDetailFromPosAndScale( state, mat.getPosition(), mObjScale ); 1733 mProjectileShape->animate(); 1734 1735 mProjectileShape->render( rdata ); 1736} 1737 1738void afxMagicMissile::onDeleteNotify(SimObject* obj) 1739{ 1740 ShapeBase* shape_test = dynamic_cast<ShapeBase*>(obj); 1741 if (shape_test == collide_exempt) 1742 { 1743 collide_exempt = NULL; 1744 Parent::onDeleteNotify(obj); 1745 return; 1746 } 1747 1748 SceneObject* target_test = dynamic_cast<SceneObject*>(obj); 1749 if (target_test == missile_target) 1750 { 1751 missile_target = NULL; 1752 Parent::onDeleteNotify(obj); 1753 return; 1754 } 1755 1756 afxChoreographer* ch = dynamic_cast<afxChoreographer*>(obj); 1757 if (ch == choreographer) 1758 { 1759 choreographer = NULL; 1760 Parent::onDeleteNotify(obj); 1761 return; 1762 } 1763 1764 Parent::onDeleteNotify(obj); 1765} 1766 1767//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 1768// private: 1769 1770void afxMagicMissile::create_splash(const Point3F& pos) 1771{ 1772 if (!mDataBlock || !mDataBlock->splash) 1773 return; 1774 1775 MatrixF xfm = getTransform(); 1776 xfm.setPosition(pos); 1777 1778 Splash* splash = new Splash(); 1779 splash->onNewDataBlock(mDataBlock->splash, false); 1780 splash->setTransform(xfm); 1781 splash->setInitialState(xfm.getPosition(), Point3F(0.0, 0.0, 1.0)); 1782 if (!splash->registerObject()) 1783 { 1784 delete splash; 1785 splash = NULL; 1786 } 1787} 1788 1789void afxMagicMissile::get_launch_constraint_data(Point3F& pos, Point3F& vel) 1790{ 1791 // need a choreographer 1792 if (!choreographer) 1793 { 1794 Con::errorf("afxMagicMissile::get_launch_constraint_data(): missing choreographer."); 1795 return; 1796 } 1797 1798 // need a constraint manager 1799 afxConstraintMgr* cons_mgr = choreographer->getConstraintMgr(); 1800 if (!cons_mgr) 1801 { 1802 Con::errorf("afxMagicMissile::get_launch_constraint_data(): missing constriant manager."); 1803 return; 1804 } 1805 1806 // need a valid constraint 1807 afxConstraintID launch_cons_id; 1808 if (isServerObject()) 1809 launch_cons_id = cons_mgr->getConstraintId(mDataBlock->launch_cons_s_def); 1810 else 1811 launch_cons_id = cons_mgr->getConstraintId(mDataBlock->launch_cons_c_def); 1812 1813 afxConstraint* launch_cons = cons_mgr->getConstraint(launch_cons_id); 1814 if (!launch_cons) 1815 { 1816 Con::errorf("afxMagicMissile::get_launch_constraint_data(): constraint undefined."); 1817 return; 1818 } 1819 1820 MatrixF launch_xfm; 1821 launch_cons->getTransform(launch_xfm); 1822 1823 Point3F launch_pos; 1824 launch_cons->getPosition(launch_pos); 1825 1826 pos = launch_pos; 1827 1828 // echo the actual launch position to the console 1829 if (mDataBlock->echo_launch_offset) 1830 { 1831 SceneObject* default_launcher = get_default_launcher(); 1832 if (default_launcher) 1833 { 1834 MatrixF launcher_xfm_inv = default_launcher->getWorldTransform(); 1835 VectorF offset = pos - default_launcher->getRenderPosition(); 1836 launcher_xfm_inv.mulV(offset); 1837 if (isServerObject()) 1838 Con::printf("launchOffsetServer = \"%g %g %g\";", offset.x, offset.y, offset.z); 1839 else 1840 Con::printf("launchOffsetClient = \"%g %g %g\";", offset.x, offset.y, offset.z); 1841 } 1842 } 1843 1844 // setup aiming matrix to straight forward and level 1845 MatrixF aim_mtx; 1846 AngAxisF aim_aa(Point3F(0,1,0),0); 1847 aim_aa.setMatrix(&aim_mtx); 1848 1849 // calculate final aiming vector 1850 MatrixF aim2_mtx; 1851 aim2_mtx.mul(launch_xfm, aim_mtx); 1852 1853 VectorF aim_vec; 1854 aim2_mtx.getColumn(1,&aim_vec); 1855 aim_vec.normalizeSafe(); 1856 1857 // give velocity vector a magnitude 1858 vel = aim_vec*mDataBlock->muzzleVelocity; 1859} 1860 1861// resolve the launch constraint object. normally it's the caster, but for 1862// reverse_targeting the target object us used. 1863SceneObject* afxMagicMissile::get_default_launcher() const 1864{ 1865 SceneObject* launch_cons_obj = 0; 1866 if (mDataBlock->reverse_targeting) 1867 { 1868 if (dynamic_cast<afxMagicSpell*>(choreographer)) 1869 launch_cons_obj = ((afxMagicSpell*)choreographer)->mTarget; 1870 if (!launch_cons_obj) 1871 { 1872 Con::errorf("afxMagicMissile::get_launch_data(): missing target constraint object for reverse targeted missile."); 1873 return 0; 1874 } 1875 } 1876 else 1877 { 1878 if (dynamic_cast<afxMagicSpell*>(choreographer)) 1879 launch_cons_obj = ((afxMagicSpell*)choreographer)->mCaster; 1880 if (!launch_cons_obj) 1881 { 1882 Con::errorf("afxMagicMissile::get_launch_data(): missing launch constraint object missile."); 1883 return 0; 1884 } 1885 } 1886 1887 return launch_cons_obj; 1888} 1889 1890void afxMagicMissile::get_launch_data(Point3F& pos, Point3F& vel) 1891{ 1892 bool use_constraint = (isServerObject()) ? mDataBlock->launch_cons_s_def.isDefined() : mDataBlock->launch_cons_c_def.isDefined(); 1893 if (use_constraint) 1894 { 1895 get_launch_constraint_data(pos, vel); 1896 return; 1897 } 1898 1899 // a choreographer pointer is required 1900 if (!choreographer) 1901 { 1902 Con::errorf("afxMagicMissile::get_launch_data(): missing choreographer."); 1903 return; 1904 } 1905 1906 SceneObject* launch_cons_obj = get_default_launcher(); 1907 if (!launch_cons_obj) 1908 return; 1909 1910 MatrixF launch_xfm = launch_cons_obj->getRenderTransform(); 1911 1912 // calculate launch position 1913 Point3F offset_override = (isClientObject()) ? mDataBlock->launch_offset_client : 1914 mDataBlock->launch_offset_server; 1915 // override 1916 if (!offset_override.isZero()) 1917 { 1918 launch_xfm.mulV(offset_override); 1919 pos = launch_cons_obj->getRenderPosition() + offset_override; 1920 } 1921 // no override 1922 else 1923 { 1924 // get transformed launch offset 1925 VectorF launch_offset = mDataBlock->launch_offset; 1926 launch_xfm.mulV(launch_offset); 1927 1928 StringTableEntry launch_node = mDataBlock->launch_node; 1929 1930 // calculate position of missile at launch 1931 if (launch_node != ST_NULLSTRING) 1932 { 1933 ShapeBase* launch_cons_shape = dynamic_cast<ShapeBase*>(launch_cons_obj); 1934 TSShapeInstance* shape_inst = (launch_cons_shape) ? launch_cons_shape->getShapeInstance() : 0; 1935 if (!shape_inst || !shape_inst->getShape()) 1936 launch_node = ST_NULLSTRING; 1937 else 1938 { 1939 S32 node_ID = shape_inst->getShape()->findNode(launch_node); 1940 MatrixF node_xfm = launch_cons_obj->getRenderTransform(); 1941 node_xfm.scale(launch_cons_obj->getScale()); 1942 if (node_ID >= 0) 1943 node_xfm.mul(shape_inst->mNodeTransforms[node_ID]); 1944 1945 VectorF node_offset = mDataBlock->launch_node_offset; 1946 node_xfm.mulV(node_offset); 1947 1948 pos = node_xfm.getPosition() + launch_offset + node_offset; 1949 } 1950 } 1951 // calculate launch position without launch node 1952 else 1953 pos = launch_cons_obj->getRenderPosition() + launch_offset; 1954 } 1955 1956 // echo the actual launch position to the console 1957 if (mDataBlock->echo_launch_offset) 1958 { 1959 VectorF offset = pos - launch_cons_obj->getRenderPosition(); 1960 MatrixF caster_xfm_inv = launch_xfm; 1961 caster_xfm_inv.affineInverse(); 1962 caster_xfm_inv.mulV(offset); 1963 if (isServerObject()) 1964 Con::printf("launchOffsetServer = \"%g %g %g\";", offset.x, offset.y, offset.z); 1965 else 1966 Con::printf("launchOffsetClient = \"%g %g %g\";", offset.x, offset.y, offset.z); 1967 } 1968 1969 // calculate launch velocity vector 1970 if (starting_vel_vec.isZero()) 1971 { 1972 // setup aiming matrix to straight forward and level 1973 MatrixF aim_mtx; 1974 AngAxisF aim_aa(Point3F(0,1,0),0); 1975 aim_aa.setMatrix(&aim_mtx); 1976 1977 // setup pitch matrix 1978 MatrixF pitch_mtx; 1979 AngAxisF pitch_aa(Point3F(1,0,0),mDegToRad(mDataBlock->launch_pitch)); 1980 pitch_aa.setMatrix(&pitch_mtx); 1981 1982 // setup pan matrix 1983 MatrixF pan_mtx; 1984 AngAxisF pan_aa(Point3F(0,0,1),mDegToRad(mDataBlock->launch_pan)); 1985 pan_aa.setMatrix(&pan_mtx); 1986 1987 // calculate adjusted aiming matrix 1988 aim_mtx.mul(pitch_mtx); 1989 aim_mtx.mul(pan_mtx); 1990 1991 // calculate final aiming vector 1992 MatrixF aim2_mtx; 1993 aim2_mtx.mul(launch_xfm, aim_mtx); 1994 VectorF aim_vec; 1995 aim2_mtx.getColumn(1,&aim_vec); 1996 aim_vec.normalizeSafe(); 1997 1998 // give velocity vector a magnitude 1999 vel = aim_vec*mDataBlock->muzzleVelocity; 2000 } 2001 else 2002 { 2003 vel = starting_vel_vec*starting_velocity; 2004 } 2005} 2006 2007void afxMagicMissile::updateSound() 2008{ 2009 if (!mDataBlock->sound) 2010 return; 2011 2012 if ( mSound ) 2013 { 2014 if ( !mSound->isPlaying() ) 2015 mSound->play(); 2016 2017 mSound->setVelocity( getVelocity() ); 2018 mSound->setTransform( getRenderTransform() ); 2019 } 2020} 2021 2022//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 2023// public: 2024 2025void afxMagicMissile::launch() 2026{ 2027 get_launch_data(mCurrPosition, mCurrVelocity); 2028 2029 mCurrDeltaBase = mCurrPosition; 2030 mCurrBackDelta.zero(); 2031 2032 did_launch = true; 2033 2034 setPosition(mCurrPosition); 2035 2036 afxMagicSpell* spell = dynamic_cast<afxMagicSpell*>(choreographer); 2037 if (spell) 2038 { 2039 if (mDataBlock->reverse_targeting) 2040 { 2041 missile_target = spell->mCaster; 2042 collide_exempt = spell->mTarget; 2043 } 2044 else 2045 { 2046 missile_target = spell->mTarget; 2047 collide_exempt = spell->mCaster; 2048 } 2049 2050 if (spell->mCaster) 2051 processAfter(spell->mCaster); 2052 if (missile_target) 2053 deleteNotify(missile_target); 2054 if (collide_exempt) 2055 deleteNotify(collide_exempt); 2056 } 2057 else 2058 { 2059 missile_target = 0; 2060 collide_exempt = 0; 2061 } 2062} 2063 2064void afxMagicMissile::setChoreographer(afxChoreographer* chor) 2065{ 2066 if (choreographer) 2067 clearNotify(choreographer); 2068 choreographer = chor; 2069 if (choreographer) 2070 deleteNotify(choreographer); 2071} 2072 2073void afxMagicMissile::setStartingVelocityVector(const Point3F& vel_vec) 2074{ 2075 starting_vel_vec = vel_vec; 2076} 2077 2078void afxMagicMissile::setStartingVelocity(const F32 vel) 2079{ 2080 starting_velocity = vel; 2081} 2082 2083void afxMagicMissile::getStartingVelocityValues(F32& vel, Point3F& vel_vec) 2084{ 2085 vel = starting_velocity; 2086 vel_vec = starting_vel_vec; 2087} 2088 2089//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 2090 2091/* From stock Projectile code... 2092DefineEngineMethod(Projectile, presimulate, void, (F32 seconds), (1.0f), "Updates velocity and position, and performs collision testing.\n" 2093 "@param seconds Amount of time, in seconds, since the simulation began, to start the simulation at.\n" 2094 "@tsexample\n" 2095 "// Tell the projectile object to process a simulation event, and provide the amount of time\n" 2096 " in seconds that has passed since the simulation began.\n" 2097 "%seconds = 2000;\n" 2098 "%projectile.presimulate(%seconds);\n" 2099 "@endtsexample\n") 2100{ 2101 object->simulate( seconds ); 2102} 2103*/ 2104 2105DefineEngineMethod(afxMagicMissile, setStartingVelocityVector, void, (Point3F velocityVec),, 2106 "Set the starting velocity-vector for a magic-missile.\n\n" 2107 "@ingroup AFX") 2108{ 2109 object->setStartingVelocityVector(velocityVec); 2110} 2111 2112DefineEngineMethod(afxMagicMissile, setStartingVelocity, void, (float velocity),, 2113 "Set the starting velocity for a magic-missile.\n\n" 2114 "@ingroup AFX") 2115{ 2116 object->setStartingVelocity(velocity); 2117} 2118 2119//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 2120