shapeImage.cpp
Engine/source/T3D/shapeImage.cpp
Public Variables
Public Functions
ConsoleDocClass(ShapeBaseImageData , "@brief Represents geometry <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be mounted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classshapebase/">ShapeBase</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )
IMPLEMENT_CALLBACK(ShapeBaseImageData , onMount , void , (SceneObject *obj, S32 slot, F32 dt) , (obj, slot, dt) , "@brief Called when the Image is first mounted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@param obj object that this Image has been mounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to\n</a>" "@param slot Image mount slot on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@param dt time remaining in this Image <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">update\n</a>" )
IMPLEMENT_CALLBACK(ShapeBaseImageData , onUnmount , void , (SceneObject *obj, S32 slot, F32 dt) , (obj, slot, dt) , "@brief Called when the Image is unmounted from the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@param obj object that this Image has been unmounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">from\n</a>" "@param slot Image mount slot on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@param dt time remaining in this Image <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">update\n</a>" )
ImplementEnumType(ShapeBaseImageLightType , "@brief The type of light <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> attach <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ShapeBaseImage.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n\n</a>" )
ImplementEnumType(ShapeBaseImageLoadedState , "@brief The loaded state of this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ShapeBaseImage.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n\n</a>" )
ImplementEnumType(ShapeBaseImageRecoilState , "@brief What kind of recoil this ShapeBaseImage should emit when <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">fired.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n\n</a>" )
ImplementEnumType(ShapeBaseImageSpinState , "@brief How the spin animation should be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">played.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n\n</a>" )
Detailed Description
Public Variables
EndImplementEnumType
ShapeBaseImageData::StateData gDefaultStateData
ShapeBaseImageData * InvalidImagePtr
Public Functions
ConsoleDocClass(ShapeBaseImageData , "@brief Represents geometry <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be mounted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classshapebase/">ShapeBase</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )
IMPLEMENT_CALLBACK(ShapeBaseImageData , onMount , void , (SceneObject *obj, S32 slot, F32 dt) , (obj, slot, dt) , "@brief Called when the Image is first mounted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@param obj object that this Image has been mounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to\n</a>" "@param slot Image mount slot on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@param dt time remaining in this Image <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">update\n</a>" )
IMPLEMENT_CALLBACK(ShapeBaseImageData , onUnmount , void , (SceneObject *obj, S32 slot, F32 dt) , (obj, slot, dt) , "@brief Called when the Image is unmounted from the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@param obj object that this Image has been unmounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">from\n</a>" "@param slot Image mount slot on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@param dt time remaining in this Image <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">update\n</a>" )
IMPLEMENT_CO_DATABLOCK_V1(ShapeBaseImageData )
ImplementEnumType(ShapeBaseImageLightType , "@brief The type of light <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> attach <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ShapeBaseImage.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n\n</a>" )
ImplementEnumType(ShapeBaseImageLoadedState , "@brief The loaded state of this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ShapeBaseImage.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n\n</a>" )
ImplementEnumType(ShapeBaseImageRecoilState , "@brief What kind of recoil this ShapeBaseImage should emit when <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">fired.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n\n</a>" )
ImplementEnumType(ShapeBaseImageSpinState , "@brief How the spin animation should be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">played.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n\n</a>" )
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#include "platform/platform.h" 25 26#include "T3D/shapeBase.h" 27#include "core/resourceManager.h" 28#include "core/stream/bitStream.h" 29#include "ts/tsShapeInstance.h" 30#include "console/consoleInternal.h" 31#include "console/consoleTypes.h" 32#include "console/engineAPI.h" 33#include "lighting/lightInfo.h" 34#include "lighting/lightManager.h" 35#include "T3D/fx/particleEmitter.h" 36#include "T3D/projectile.h" 37#include "T3D/gameBase/gameConnection.h" 38#include "math/mathIO.h" 39#include "T3D/debris.h" 40#include "math/mathUtils.h" 41#include "sim/netObject.h" 42#include "sfx/sfxTrack.h" 43#include "sfx/sfxSource.h" 44#include "sfx/sfxSystem.h" 45#include "sfx/sfxTypes.h" 46#include "scene/sceneManager.h" 47#include "core/stream/fileStream.h" 48#include "T3D/fx/cameraFXMgr.h" 49 50//---------------------------------------------------------------------------- 51 52ShapeBaseImageData* InvalidImagePtr = (ShapeBaseImageData*) 1; 53 54ImplementEnumType( ShapeBaseImageLoadedState, 55 "@brief The loaded state of this ShapeBaseImage.\n\n" 56 "@ingroup gameObjects\n\n") 57 { ShapeBaseImageData::StateData::IgnoreLoaded, "Ignore", "Ignore the loaded state.\n" }, 58 { ShapeBaseImageData::StateData::Loaded, "Loaded", "ShapeBaseImage is loaded.\n" }, 59 { ShapeBaseImageData::StateData::NotLoaded, "Empty", "ShapeBaseImage is not loaded.\n" }, 60EndImplementEnumType; 61 62ImplementEnumType( ShapeBaseImageSpinState, 63 "@brief How the spin animation should be played.\n\n" 64 "@ingroup gameObjects\n\n") 65 { ShapeBaseImageData::StateData::IgnoreSpin,"Ignore", "No changes to the spin sequence.\n" }, 66 { ShapeBaseImageData::StateData::NoSpin, "Stop", "Stops the spin sequence at its current position\n" }, 67 { ShapeBaseImageData::StateData::SpinUp, "SpinUp", "Increase spin sequence timeScale from 0 (on state entry) to 1 (after stateTimeoutValue seconds).\n" }, 68 { ShapeBaseImageData::StateData::SpinDown, "SpinDown", "Decrease spin sequence timeScale from 1 (on state entry) to 0 (after stateTimeoutValue seconds).\n" }, 69 { ShapeBaseImageData::StateData::FullSpin, "FullSpeed", "Resume the spin sequence playback at its current position with timeScale = 1.\n"}, 70EndImplementEnumType; 71 72ImplementEnumType( ShapeBaseImageRecoilState, 73 "@brief What kind of recoil this ShapeBaseImage should emit when fired.\n\n" 74 "@ingroup gameObjects\n\n") 75 { ShapeBaseImageData::StateData::NoRecoil, "NoRecoil", "No recoil occurs.\n" }, 76 { ShapeBaseImageData::StateData::LightRecoil, "LightRecoil", "A light recoil occurs.\n" }, 77 { ShapeBaseImageData::StateData::MediumRecoil, "MediumRecoil", "A medium recoil occurs.\n" }, 78 { ShapeBaseImageData::StateData::HeavyRecoil, "HeavyRecoil", "A heavy recoil occurs.\n" }, 79EndImplementEnumType; 80 81ImplementEnumType( ShapeBaseImageLightType, 82 "@brief The type of light to attach to this ShapeBaseImage.\n\n" 83 "@ingroup gameObjects\n\n") 84 { ShapeBaseImageData::NoLight, "NoLight", "No light is attached.\n" }, 85 { ShapeBaseImageData::ConstantLight, "ConstantLight", "A constant emitting light is attached.\n" }, 86 { ShapeBaseImageData::SpotLight, "SpotLight", "A spotlight is attached.\n" }, 87 { ShapeBaseImageData::PulsingLight, "PulsingLight", "A pusling light is attached.\n" }, 88 { ShapeBaseImageData::WeaponFireLight, "WeaponFireLight", "Light emits when the weapon is fired, then dissipates.\n" } 89EndImplementEnumType; 90 91//---------------------------------------------------------------------------- 92//---------------------------------------------------------------------------- 93 94IMPLEMENT_CO_DATABLOCK_V1(ShapeBaseImageData); 95 96ConsoleDocClass( ShapeBaseImageData, 97 "@brief Represents geometry to be mounted to a ShapeBase object.\n\n" 98 "@ingroup gameObjects\n" 99); 100 101IMPLEMENT_CALLBACK( ShapeBaseImageData, onMount, void, ( SceneObject* obj, S32 slot, F32 dt ), ( obj, slot, dt ), 102 "@brief Called when the Image is first mounted to the object.\n\n" 103 104 "@param obj object that this Image has been mounted to\n" 105 "@param slot Image mount slot on the object\n" 106 "@param dt time remaining in this Image update\n" ); 107 108IMPLEMENT_CALLBACK( ShapeBaseImageData, onUnmount, void, ( SceneObject* obj, S32 slot, F32 dt ), ( obj, slot, dt ), 109 "@brief Called when the Image is unmounted from the object.\n\n" 110 111 "@param obj object that this Image has been unmounted from\n" 112 "@param slot Image mount slot on the object\n" 113 "@param dt time remaining in this Image update\n" ); 114 115ShapeBaseImageData::StateData::StateData() 116{ 117 name = 0; 118 transition.loaded[0] = transition.loaded[1] = -1; 119 transition.ammo[0] = transition.ammo[1] = -1; 120 transition.target[0] = transition.target[1] = -1; 121 transition.trigger[0] = transition.trigger[1] = -1; 122 transition.altTrigger[0] = transition.altTrigger[1] = -1; 123 transition.wet[0] = transition.wet[1] = -1; 124 transition.motion[0] = transition.motion[1] = -1; 125 transition.timeout = -1; 126 waitForTimeout = true; 127 timeoutValue = 0; 128 fire = false; 129 altFire = false; 130 reload = false; 131 energyDrain = 0; 132 allowImageChange = true; 133 loaded = IgnoreLoaded; 134 spin = IgnoreSpin; 135 recoil = NoRecoil; 136 sound = 0; 137 emitter = NULL; 138 shapeSequence = NULL; 139 shapeSequenceScale = true; 140 script = 0; 141 ignoreLoadedForReady = false; 142 143 ejectShell = false; 144 scaleAnimation = false; 145 scaleAnimationFP = false; 146 sequenceTransitionIn = false; 147 sequenceTransitionOut = false; 148 sequenceNeverTransition = false; 149 sequenceTransitionTime = 0; 150 direction = false; 151 emitterTime = 0.0f; 152 153 for( U32 i=0; i<MaxShapes; ++i) 154 { 155 sequence[i] = -1; 156 sequenceVis[i] = -1; 157 flashSequence[i] = false; 158 emitterNode[i] = -1; 159 } 160} 161 162static ShapeBaseImageData::StateData gDefaultStateData; 163 164//---------------------------------------------------------------------------- 165 166ShapeBaseImageData::ShapeBaseImageData() 167{ 168 emap = false; 169 170 mountPoint = 0; 171 mountOffset.identity(); 172 eyeOffset.identity(); 173 correctMuzzleVector = true; 174 correctMuzzleVectorTP = true; 175 firstPerson = true; 176 useFirstPersonShape = false; 177 useEyeOffset = false; 178 useEyeNode = false; 179 mass = 0; 180 181 usesEnergy = false; 182 minEnergy = 2; 183 accuFire = false; 184 185 projectile = NULL; 186 187 cloakable = true; 188 189 lightType = ShapeBaseImageData::NoLight; 190 lightColor.set(1.f,1.f,1.f,1.f); 191 lightDuration = 1000; 192 lightRadius = 10.f; 193 lightBrightness = 1.0f; 194 195 shapeName = "core/rendering/shapes/noshape.dts"; 196 shapeNameFP = ""; 197 imageAnimPrefix = ""; 198 imageAnimPrefixFP = ""; 199 fireState = -1; 200 altFireState = -1; 201 reloadState = -1; 202 computeCRC = false; 203 204 animateAllShapes = true; 205 animateOnServer = false; 206 207 scriptAnimTransitionTime = 0.25f; 208 209 // 210 for (S32 i = 0; i < MaxStates; i++) { 211 stateName[i] = 0; 212 213 stateTransitionLoaded[i] = 0; 214 stateTransitionNotLoaded[i] = 0; 215 stateTransitionAmmo[i] = 0; 216 stateTransitionNoAmmo[i] = 0; 217 stateTransitionTarget[i] = 0; 218 stateTransitionNoTarget[i] = 0; 219 stateTransitionWet[i] = 0; 220 stateTransitionNotWet[i] = 0; 221 stateTransitionMotion[i] = 0; 222 stateTransitionNoMotion[i] = 0; 223 stateTransitionTriggerUp[i] = 0; 224 stateTransitionTriggerDown[i] = 0; 225 stateTransitionAltTriggerUp[i] = 0; 226 stateTransitionAltTriggerDown[i] = 0; 227 stateTransitionTimeout[i] = 0; 228 229 stateTransitionGeneric0In[i] = 0; 230 stateTransitionGeneric0Out[i] = 0; 231 stateTransitionGeneric1In[i] = 0; 232 stateTransitionGeneric1Out[i] = 0; 233 stateTransitionGeneric2In[i] = 0; 234 stateTransitionGeneric2Out[i] = 0; 235 stateTransitionGeneric3In[i] = 0; 236 stateTransitionGeneric3Out[i] = 0; 237 238 stateWaitForTimeout[i] = true; 239 stateTimeoutValue[i] = 0; 240 stateFire[i] = false; 241 stateAlternateFire[i] = false; 242 stateReload[i] = false; 243 stateEjectShell[i] = false; 244 stateEnergyDrain[i] = 0; 245 stateAllowImageChange[i] = true; 246 stateScaleAnimation[i] = true; 247 stateScaleAnimationFP[i] = true; 248 stateSequenceTransitionIn[i] = false; 249 stateSequenceTransitionOut[i] = false; 250 stateSequenceNeverTransition[i] = false; 251 stateSequenceTransitionTime[i] = 0.25f; 252 stateDirection[i] = true; 253 stateLoaded[i] = StateData::IgnoreLoaded; 254 stateSpin[i] = StateData::IgnoreSpin; 255 stateRecoil[i] = StateData::NoRecoil; 256 stateSequence[i] = 0; 257 stateSequenceRandomFlash[i] = false; 258 259 stateShapeSequence[i] = 0; 260 stateScaleShapeSequence[i] = false; 261 262 stateSound[i] = 0; 263 stateScript[i] = 0; 264 stateEmitter[i] = 0; 265 stateEmitterTime[i] = 0; 266 stateEmitterNode[i] = 0; 267 stateIgnoreLoadedForReady[i] = false; 268 } 269 statesLoaded = false; 270 271 maxConcurrentSounds = 0; 272 273 useRemainderDT = false; 274 275 casing = NULL; 276 casingID = 0; 277 shellExitDir.set( 1.0, 0.0, 1.0 ); 278 shellExitDir.normalize(); 279 shellExitVariance = 20.0; 280 shellVelocity = 1.0; 281 282 fireStateName = NULL; 283 284 for(U32 i=0; i<MaxShapes; ++i) 285 { 286 mCRC[i] = U32_MAX; 287 mountTransform[i].identity(); 288 retractNode[i] = -1; 289 muzzleNode[i] = -1; 290 ejectNode[i] = -1; 291 emitterNode[i] = -1; 292 eyeMountNode[i] = -1; 293 eyeNode[i] = -1; 294 spinSequence[i] = -1; 295 ambientSequence[i] = -1; 296 isAnimated[i] = false; 297 hasFlash[i] = false; 298 shapeIsValid[i] = false; 299 } 300 301 shakeCamera = false; 302 camShakeFreq = Point3F::Zero; 303 camShakeAmp = Point3F::Zero; 304 camShakeDuration = 1.5f; 305 camShakeRadius = 3.0f; 306 camShakeFalloff = 10.0f; 307} 308 309ShapeBaseImageData::~ShapeBaseImageData() 310{ 311} 312 313bool ShapeBaseImageData::onAdd() 314{ 315 if (!Parent::onAdd()) 316 return false; 317 318 // Copy state data from the scripting arrays into the 319 // state structure array. If we have state data already, 320 // we are on the client and need to leave it alone. 321 for (U32 i = 0; i < MaxStates; i++) { 322 StateData& s = state[i]; 323 if (statesLoaded == false) { 324 s.name = stateName[i]; 325 s.transition.loaded[0] = lookupState(stateTransitionNotLoaded[i]); 326 s.transition.loaded[1] = lookupState(stateTransitionLoaded[i]); 327 s.transition.ammo[0] = lookupState(stateTransitionNoAmmo[i]); 328 s.transition.ammo[1] = lookupState(stateTransitionAmmo[i]); 329 s.transition.target[0] = lookupState(stateTransitionNoTarget[i]); 330 s.transition.target[1] = lookupState(stateTransitionTarget[i]); 331 s.transition.wet[0] = lookupState(stateTransitionNotWet[i]); 332 s.transition.wet[1] = lookupState(stateTransitionWet[i]); 333 s.transition.motion[0] = lookupState(stateTransitionNoMotion[i]); 334 s.transition.motion[1] = lookupState(stateTransitionMotion[i]); 335 s.transition.trigger[0] = lookupState(stateTransitionTriggerUp[i]); 336 s.transition.trigger[1] = lookupState(stateTransitionTriggerDown[i]); 337 s.transition.altTrigger[0] = lookupState(stateTransitionAltTriggerUp[i]); 338 s.transition.altTrigger[1] = lookupState(stateTransitionAltTriggerDown[i]); 339 s.transition.timeout = lookupState(stateTransitionTimeout[i]); 340 341 s.transition.genericTrigger[0][0] = lookupState(stateTransitionGeneric0Out[i]); 342 s.transition.genericTrigger[0][1] = lookupState(stateTransitionGeneric0In[i]); 343 s.transition.genericTrigger[1][0] = lookupState(stateTransitionGeneric1Out[i]); 344 s.transition.genericTrigger[1][1] = lookupState(stateTransitionGeneric1In[i]); 345 s.transition.genericTrigger[2][0] = lookupState(stateTransitionGeneric2Out[i]); 346 s.transition.genericTrigger[2][1] = lookupState(stateTransitionGeneric2In[i]); 347 s.transition.genericTrigger[3][0] = lookupState(stateTransitionGeneric3Out[i]); 348 s.transition.genericTrigger[3][1] = lookupState(stateTransitionGeneric3In[i]); 349 350 s.waitForTimeout = stateWaitForTimeout[i]; 351 s.timeoutValue = stateTimeoutValue[i]; 352 s.fire = stateFire[i]; 353 s.altFire = stateAlternateFire[i]; 354 s.reload = stateReload[i]; 355 s.ejectShell = stateEjectShell[i]; 356 s.energyDrain = stateEnergyDrain[i]; 357 s.allowImageChange = stateAllowImageChange[i]; 358 s.scaleAnimation = stateScaleAnimation[i]; 359 s.scaleAnimationFP = stateScaleAnimationFP[i]; 360 s.sequenceTransitionIn = stateSequenceTransitionIn[i]; 361 s.sequenceTransitionOut = stateSequenceTransitionOut[i]; 362 s.sequenceNeverTransition = stateSequenceNeverTransition[i]; 363 s.sequenceTransitionTime = stateSequenceTransitionTime[i]; 364 s.direction = stateDirection[i]; 365 s.loaded = stateLoaded[i]; 366 s.spin = stateSpin[i]; 367 s.recoil = stateRecoil[i]; 368 369 s.shapeSequence = stateShapeSequence[i]; 370 s.shapeSequenceScale = stateScaleShapeSequence[i]; 371 372 s.sound = stateSound[i]; 373 s.script = stateScript[i]; 374 s.emitter = stateEmitter[i]; 375 s.emitterTime = stateEmitterTime[i]; 376 377 // Resolved at load time 378 for( U32 j=0; j<MaxShapes; ++j) 379 { 380 s.sequence[j] = -1; // Sequence is resolved in load 381 s.sequenceVis[j] = -1; // Vis Sequence is resolved in load 382 s.emitterNode[j] = -1; // Sequnce is resolved in load 383 } 384 } 385 386 // The first state marked as "fire" is the state entered on the 387 // client when it recieves a fire event. 388 if (s.fire && fireState == -1) 389 fireState = i; 390 391 // The first state marked as "alternateFire" is the state entered on the 392 // client when it recieves an alternate fire event. 393 if (s.altFire && altFireState == -1) 394 altFireState = i; 395 396 // The first state marked as "reload" is the state entered on the 397 // client when it recieves a reload event. 398 if (s.reload && reloadState == -1) 399 reloadState = i; 400 } 401 402 // Always preload images, this is needed to avoid problems with 403 // resolving sequences before transmission to a client. 404 return true; 405} 406 407bool ShapeBaseImageData::preload(bool server, String &errorStr) 408{ 409 if (!Parent::preload(server, errorStr)) 410 return false; 411 412 // Resolve objects transmitted from server 413 if (!server) { 414 if (projectile) 415 if (Sim::findObject(SimObjectId((uintptr_t)projectile), projectile) == false) 416 Con::errorf(ConsoleLogEntry::General, "Error, unable to load projectile for shapebaseimagedata"); 417 418 for (U32 i = 0; i < MaxStates; i++) { 419 if (state[i].emitter) 420 if (!Sim::findObject(SimObjectId((uintptr_t)state[i].emitter), state[i].emitter)) 421 Con::errorf(ConsoleLogEntry::General, "Error, unable to load emitter for image datablock"); 422 423 String str; 424 if( !sfxResolve( &state[ i ].sound, str ) ) 425 Con::errorf( ConsoleLogEntry::General, str.c_str() ); 426 } 427 } 428 429 // Use the first person eye offset if it's set. 430 useEyeOffset = !eyeOffset.isIdentity(); 431 432 // Go through each of the shapes 433 for (U32 i=0; i<MaxShapes; ++i) 434 { 435 // Shape 0: Standard image shape 436 // Shape 1: Optional first person image shape 437 438 StringTableEntry name; 439 if (i == FirstPersonImageShape) 440 { 441 if ((useEyeOffset || useEyeNode) && shapeNameFP && shapeNameFP[0]) 442 { 443 // Make use of the first person shape 444 useFirstPersonShape = true; 445 name = shapeNameFP; 446 } 447 else 448 { 449 // Skip the first person shape 450 continue; 451 } 452 } 453 else 454 { 455 name = shapeName; 456 } 457 458 if (name && name[0]) { 459 // Resolve shapename 460 shape[i] = ResourceManager::get().load(name); 461 if (!bool(shape[i])) { 462 errorStr = String::ToString("Unable to load shape: %s", name); 463 return false; 464 } 465 if(computeCRC) 466 { 467 Con::printf("Validation required for shape: %s", name); 468 469 Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode(shape[i].getPath()); 470 471 if (!fileRef) 472 { 473 errorStr = String::ToString("ShapeBaseImageData: Couldn't load shape \"%s\"",name); 474 return false; 475 } 476 477 if(server) 478 { 479 mCRC[i] = fileRef->getChecksum(); 480 } 481 else if(mCRC[i] != fileRef->getChecksum()) 482 { 483 errorStr = String::ToString("Shape \"%s\" does not match version on server.",name); 484 return false; 485 } 486 } 487 488 // Resolve nodes & build mount transform 489 eyeMountNode[i] = shape[i]->findNode("eyeMount"); 490 eyeNode[i] = shape[i]->findNode("eye"); 491 if (eyeNode[i] == -1) 492 eyeNode[i] = eyeMountNode[i]; 493 ejectNode[i] = shape[i]->findNode("ejectPoint"); 494 muzzleNode[i] = shape[i]->findNode("muzzlePoint"); 495 retractNode[i] = shape[i]->findNode("retractionPoint"); 496 mountTransform[i] = mountOffset; 497 S32 node = shape[i]->findNode("mountPoint"); 498 if (node != -1) { 499 MatrixF total(1); 500 do { 501 MatrixF nmat; 502 QuatF q; 503 TSTransform::setMatrix(shape[i]->defaultRotations[node].getQuatF(&q),shape[i]->defaultTranslations[node],&nmat); 504 total.mul(nmat); 505 node = shape[i]->nodes[node].parentIndex; 506 } 507 while(node != -1); 508 total.inverse(); 509 mountTransform[i].mul(total); 510 } 511 512 // Resolve state sequence names & emitter nodes 513 isAnimated[i] = false; 514 hasFlash[i] = false; 515 for (U32 j = 0; j < MaxStates; j++) { 516 StateData& s = state[j]; 517 if (stateSequence[j] && stateSequence[j][0]) 518 s.sequence[i] = shape[i]->findSequence(stateSequence[j]); 519 if (s.sequence[i] != -1) 520 { 521 // This state has an animation sequence 522 isAnimated[i] = true; 523 } 524 525 if (stateSequence[j] && stateSequence[j][0] && stateSequenceRandomFlash[j]) { 526 char bufferVis[128]; 527 dStrncpy(bufferVis, stateSequence[j], 100); 528 dStrcat(bufferVis, "_vis", 128); 529 s.sequenceVis[i] = shape[i]->findSequence(bufferVis); 530 } 531 if (s.sequenceVis[i] != -1) 532 { 533 // This state has a flash animation sequence 534 s.flashSequence[i] = true; 535 hasFlash[i] = true; 536 } 537 538 s.ignoreLoadedForReady = stateIgnoreLoadedForReady[j]; 539 540 if (stateEmitterNode[j] && stateEmitterNode[j][0]) 541 s.emitterNode[i] = shape[i]->findNode(stateEmitterNode[j]); 542 if (s.emitterNode[i] == -1) 543 s.emitterNode[i] = muzzleNode[i]; 544 } 545 546 ambientSequence[i] = shape[i]->findSequence("ambient"); 547 spinSequence[i] = shape[i]->findSequence("spin"); 548 549 shapeIsValid[i] = true; 550 } 551 else { 552 errorStr = "Bad Datablock from server"; 553 return false; 554 } 555 } 556 557 if( !casing && casingID != 0 ) 558 { 559 if( !Sim::findObject( SimObjectId( casingID ), casing ) ) 560 { 561 Con::errorf( ConsoleLogEntry::General, "ShapeBaseImageData::preload: Invalid packet, bad datablockId(casing): 0x%x", casingID ); 562 } 563 } 564 565 566 // Preload the shapes 567 for( U32 i=0; i<MaxShapes; ++i) 568 { 569 if( shapeIsValid[i] ) 570 { 571 TSShapeInstance* pDummy = new TSShapeInstance(shape[i], !server); 572 delete pDummy; 573 } 574 } 575 return true; 576} 577 578S32 ShapeBaseImageData::lookupState(const char* name) 579{ 580 if (!name || !name[0]) 581 return -1; 582 for (U32 i = 0; i < MaxStates; i++) 583 if (stateName[i] && !dStricmp(name,stateName[i])) 584 return i; 585 Con::errorf(ConsoleLogEntry::General,"ShapeBaseImageData:: Could not resolve state \"%s\" for image \"%s\"",name,getName()); 586 return 0; 587} 588 589void ShapeBaseImageData::initPersistFields() 590{ 591 addField( "emap", TypeBool, Offset(emap, ShapeBaseImageData), 592 "@brief Whether to enable environment mapping on this Image.\n\n" ); 593 594 addField( "shapeFile", TypeShapeFilename, Offset(shapeName, ShapeBaseImageData), 595 "@brief The DTS or DAE model to use for this Image.\n\n" ); 596 597 addField( "shapeFileFP", TypeShapeFilename, Offset(shapeNameFP, ShapeBaseImageData), 598 "@brief The DTS or DAE model to use for this Image when in first person.\n\n" 599 "This is an optional parameter that also requires either eyeOffset or useEyeNode " 600 "to be set. If none of these conditions is met then shapeFile will be used " 601 "for all cases.\n\n" 602 "Typically you set a first person image for a weapon that " 603 "includes the player's arms attached to it for animating while firing, " 604 "reloading, etc. This is typical of many FPS games." 605 "@see eyeOffset\n" 606 "@see useEyeNode\n"); 607 608 addField( "imageAnimPrefix", TypeCaseString, Offset(imageAnimPrefix, ShapeBaseImageData), 609 "@brief Passed along to the mounting shape to modify animation sequences played in third person. [optional]\n\n" ); 610 addField( "imageAnimPrefixFP", TypeCaseString, Offset(imageAnimPrefixFP, ShapeBaseImageData), 611 "@brief Passed along to the mounting shape to modify animation sequences played in first person. [optional]\n\n" ); 612 613 addField( "animateAllShapes", TypeBool, Offset(animateAllShapes, ShapeBaseImageData), 614 "@brief Indicates that all shapes should be animated in sync.\n\n" 615 "When multiple shapes are defined for this image datablock, each of them are automatically " 616 "animated in step with each other. This allows for easy switching between between shapes " 617 "when some other condition changes, such as going from first person to third person, and " 618 "keeping their look consistent. If you know that you'll never switch between shapes on the " 619 "fly, such as players only being allowed in a first person view, then you could set this to " 620 "false to save some calculations.\n\n" 621 "There are other circumstances internal to the engine that determine that only the current shape " 622 "should be animated rather than all defined shapes. In those cases, this property is ignored.\n\n" 623 "@note This property is only important if you have more than one shape defined, such as shapeFileFP.\n\n" 624 "@see shapeFileFP\n"); 625 626 addField( "animateOnServer", TypeBool, Offset(animateOnServer, ShapeBaseImageData), 627 "@brief Indicates that the image should be animated on the server.\n\n" 628 "In most cases you'll want this set if you're using useEyeNode. You may also want to " 629 "set this if the muzzlePoint is animated while it shoots. You can set this " 630 "to false even if these previous cases are true if the image's shape is set " 631 "up in the correct position and orientation in the 'root' pose and none of " 632 "the nodes are animated at key times, such as the muzzlePoint essentially " 633 "remaining at the same position at the start of the fire state (it could " 634 "animate just fine after the projectile is away as the muzzle vector is only " 635 "calculated at the start of the state).\n\n" 636 "You'll also want to set this to true if you're animating the camera using the " 637 "image's 'eye' node -- unless the movement is very subtle and doesn't need to " 638 "be reflected on the server.\n\n" 639 "@note Setting this to true causes up to four animation threads to be advanced on the server " 640 "for each instance in use, although for most images only one or two are actually defined.\n\n" 641 "@see useEyeNode\n"); 642 643 addField( "scriptAnimTransitionTime", TypeF32, Offset(scriptAnimTransitionTime, ShapeBaseImageData), 644 "@brief The amount of time to transition between the previous sequence and new sequence when the script prefix has changed.\n\n" 645 "When setImageScriptAnimPrefix() is used on a ShapeBase that has this image mounted, the image " 646 "will attempt to switch to the new animation sequence based on the given script prefix. This is " 647 "the amount of time it takes to transition from the previously playing animation sequence to" 648 "the new script prefix-based animation sequence.\n" 649 "@see ShapeBase::setImageScriptAnimPrefix()"); 650 651 addField( "projectile", TYPEID< ProjectileData >(), Offset(projectile, ShapeBaseImageData), 652 "@brief The projectile fired by this Image\n\n" ); 653 654 addField( "cloakable", TypeBool, Offset(cloakable, ShapeBaseImageData), 655 "@brief Whether this Image can be cloaked.\n\n" 656 "Currently unused." ); 657 658 addField( "mountPoint", TypeS32, Offset(mountPoint, ShapeBaseImageData), 659 "@brief Mount node # to mount this Image to.\n\n" 660 "This should correspond to a mount# node on the ShapeBase derived object we are mounting to." ); 661 662 addField( "offset", TypeMatrixPosition, Offset(mountOffset, ShapeBaseImageData), 663 "@brief \"X Y Z\" translation offset from this Image's <i>mountPoint</i> node to " 664 "attach to.\n\n" 665 "Defaults to \"0 0 0\". ie. attach this Image's " 666 "<i>mountPoint</i> node to the ShapeBase model's mount# node without any offset.\n" 667 "@see rotation"); 668 669 addField( "rotation", TypeMatrixRotation, Offset(mountOffset, ShapeBaseImageData), 670 "@brief \"X Y Z ANGLE\" rotation offset from this Image's <i>mountPoint</i> node " 671 "to attach to.\n\n" 672 "Defaults to \"0 0 0\". ie. attach this Image's " 673 "<i>mountPoint</i> node to the ShapeBase model's mount# node without any additional rotation.\n" 674 "@see offset"); 675 676 addField( "eyeOffset", TypeMatrixPosition, Offset(eyeOffset, ShapeBaseImageData), 677 "@brief \"X Y Z\" translation offset from the ShapeBase model's eye node.\n\n" 678 "When in first person view, this is the offset from the eye node to place the gun. This " 679 "gives the gun a fixed point in space, typical of a lot of FPS games.\n" 680 "@see eyeRotation"); 681 682 addField( "eyeRotation", TypeMatrixRotation, Offset(eyeOffset, ShapeBaseImageData), 683 "@brief \"X Y Z ANGLE\" rotation offset from the ShapeBase model's eye node.\n\n" 684 "When in first person view, this is the rotation from the eye node to place the gun.\n" 685 "@see eyeOffset"); 686 687 addField( "useEyeNode", TypeBool, Offset(useEyeNode, ShapeBaseImageData), 688 "@brief Mount image using image's eyeMount node and place the camera at the image's eye node (or " 689 "at the eyeMount node if the eye node is missing).\n\n" 690 "When in first person view, if an 'eyeMount' node is present in the image's shape, this indicates " 691 "that the image should mount eyeMount node to Player eye node for image placement. The " 692 "Player's camera should also mount to the image's eye node to inherit any animation (or the eyeMount " 693 "node if the image doesn't have an eye node).\n\n" 694 "@note Used instead of eyeOffset.\n\n" 695 "@note Read about the animateOnServer field as you may want to set it to true if you're using useEyeNode.\n\n" 696 "@see eyeOffset\n\n" 697 "@see animateOnServer\n\n"); 698 699 addField( "correctMuzzleVector", TypeBool, Offset(correctMuzzleVector, ShapeBaseImageData), 700 "@brief Flag to adjust the aiming vector to the eye's LOS point when in 1st person view.\n\n" 701 "@see ShapeBase::getMuzzleVector()" ); 702 703 addField( "correctMuzzleVectorTP", TypeBool, Offset(correctMuzzleVectorTP, ShapeBaseImageData), 704 "@brief Flag to adjust the aiming vector to the camera's LOS point when in 3rd person view.\n\n" 705 "@see ShapeBase::getMuzzleVector()" ); 706 707 addField( "firstPerson", TypeBool, Offset(firstPerson, ShapeBaseImageData), 708 "@brief Set to true to render the image in first person." ); 709 710 addField( "mass", TypeF32, Offset(mass, ShapeBaseImageData), 711 "@brief Mass of this Image.\n\n" 712 "This is added to the total mass of the ShapeBase object." ); 713 714 addField( "usesEnergy", TypeBool, Offset(usesEnergy,ShapeBaseImageData), 715 "@brief Flag indicating whether this Image uses energy instead of ammo. The energy level comes from the ShapeBase object we're mounted to.\n\n" 716 "@see ShapeBase::setEnergyLevel()"); 717 718 addField( "minEnergy", TypeF32, Offset(minEnergy, ShapeBaseImageData), 719 "@brief Minimum Image energy for it to be operable.\n\n" 720 "@see usesEnergy"); 721 722 addField( "accuFire", TypeBool, Offset(accuFire, ShapeBaseImageData), 723 "@brief Flag to control whether the Image's aim is automatically converged with " 724 "the crosshair.\n\n" 725 "Currently unused." ); 726 727 addField( "lightType", TYPEID< ShapeBaseImageData::LightType >(), Offset(lightType, ShapeBaseImageData), 728 "@brief The type of light this Image emits.\n\n" 729 "@see ShapeBaseImageLightType"); 730 731 addField( "lightColor", TypeColorF, Offset(lightColor, ShapeBaseImageData), 732 "@brief The color of light this Image emits.\n\n" 733 "@see lightType"); 734 735 addField( "lightDuration", TypeS32, Offset(lightDuration, ShapeBaseImageData), 736 "@brief Duration in SimTime of Pulsing and WeaponFire type lights.\n\n" 737 "@see lightType"); 738 739 addField( "lightRadius", TypeF32, Offset(lightRadius, ShapeBaseImageData), 740 "@brief Radius of the light this Image emits.\n\n" 741 "@see lightType"); 742 743 addField( "lightBrightness", TypeF32, Offset(lightBrightness, ShapeBaseImageData), 744 "@brief Brightness of the light this Image emits.\n\n" 745 "Only valid for WeaponFireLight." 746 "@see lightType"); 747 748 addField( "shakeCamera", TypeBool, Offset(shakeCamera, ShapeBaseImageData), 749 "@brief Flag indicating whether the camera should shake when this Image fires.\n\n" ); 750 751 addField( "camShakeFreq", TypePoint3F, Offset(camShakeFreq, ShapeBaseImageData), 752 "@brief Frequency of the camera shaking effect.\n\n" 753 "@see shakeCamera" ); 754 755 addField( "camShakeAmp", TypePoint3F, Offset(camShakeAmp, ShapeBaseImageData), 756 "@brief Amplitude of the camera shaking effect.\n\n" 757 "@see shakeCamera" ); 758 759 addField( "camShakeDuration", TypeF32, Offset(camShakeDuration, ShapeBaseImageData), 760 "Duration (in seconds) to shake the camera." ); 761 762 addField( "camShakeRadius", TypeF32, Offset(camShakeRadius, ShapeBaseImageData), 763 "Radial distance that a camera's position must be within relative to the " 764 "center of the explosion to be shaken." ); 765 766 addField( "camShakeFalloff", TypeF32, Offset(camShakeFalloff, ShapeBaseImageData), 767 "Falloff value for the camera shake." ); 768 769 addField( "casing", TYPEID< DebrisData >(), Offset(casing, ShapeBaseImageData), 770 "@brief DebrisData datablock to use for ejected casings.\n\n" 771 "@see stateEjectShell" ); 772 773 addField( "shellExitDir", TypePoint3F, Offset(shellExitDir, ShapeBaseImageData), 774 "@brief Vector direction to eject shell casings.\n\n" 775 "@see casing"); 776 777 addField( "shellExitVariance", TypeF32, Offset(shellExitVariance, ShapeBaseImageData), 778 "@brief Variance (in degrees) from the shellExitDir vector to eject casings.\n\n" 779 "@see shellExitDir"); 780 781 addField( "shellVelocity", TypeF32, Offset(shellVelocity, ShapeBaseImageData), 782 "@brief Speed at which to eject casings.\n\n" 783 "@see casing"); 784 785 // State arrays 786 addArray( "States", MaxStates ); 787 788 addField( "stateName", TypeCaseString, Offset(stateName, ShapeBaseImageData), MaxStates, 789 "Name of this state." ); 790 791 addField( "stateTransitionOnLoaded", TypeString, Offset(stateTransitionLoaded, ShapeBaseImageData), MaxStates, 792 "Name of the state to transition to when the loaded state of the Image " 793 "changes to 'Loaded'." ); 794 addField( "stateTransitionOnNotLoaded", TypeString, Offset(stateTransitionNotLoaded, ShapeBaseImageData), MaxStates, 795 "Name of the state to transition to when the loaded state of the Image " 796 "changes to 'Empty'." ); 797 addField( "stateTransitionOnAmmo", TypeString, Offset(stateTransitionAmmo, ShapeBaseImageData), MaxStates, 798 "Name of the state to transition to when the ammo state of the Image " 799 "changes to true." ); 800 addField( "stateTransitionOnNoAmmo", TypeString, Offset(stateTransitionNoAmmo, ShapeBaseImageData), MaxStates, 801 "Name of the state to transition to when the ammo state of the Image " 802 "changes to false." ); 803 addField( "stateTransitionOnTarget", TypeString, Offset(stateTransitionTarget, ShapeBaseImageData), MaxStates, 804 "Name of the state to transition to when the Image gains a target." ); 805 addField( "stateTransitionOnNoTarget", TypeString, Offset(stateTransitionNoTarget, ShapeBaseImageData), MaxStates, 806 "Name of the state to transition to when the Image loses a target." ); 807 addField( "stateTransitionOnWet", TypeString, Offset(stateTransitionWet, ShapeBaseImageData), MaxStates, 808 "Name of the state to transition to when the Image enters the water." ); 809 addField( "stateTransitionOnNotWet", TypeString, Offset(stateTransitionNotWet, ShapeBaseImageData), MaxStates, 810 "Name of the state to transition to when the Image exits the water." ); 811 addField( "stateTransitionOnMotion", TypeString, Offset(stateTransitionMotion, ShapeBaseImageData), MaxStates, 812 "Name of the state to transition to when the Player moves." ); 813 addField( "stateTransitionOnNoMotion", TypeString, Offset(stateTransitionNoMotion, ShapeBaseImageData), MaxStates, 814 "Name of the state to transition to when the Player stops moving." ); 815 addField( "stateTransitionOnTriggerUp", TypeString, Offset(stateTransitionTriggerUp, ShapeBaseImageData), MaxStates, 816 "Name of the state to transition to when the trigger state of the Image " 817 "changes to true (fire button down)." ); 818 addField( "stateTransitionOnTriggerDown", TypeString, Offset(stateTransitionTriggerDown, ShapeBaseImageData), MaxStates, 819 "Name of the state to transition to when the trigger state of the Image " 820 "changes to false (fire button released)." ); 821 addField( "stateTransitionOnAltTriggerUp", TypeString, Offset(stateTransitionAltTriggerUp, ShapeBaseImageData), MaxStates, 822 "Name of the state to transition to when the alt trigger state of the " 823 "Image changes to true (alt fire button down)." ); 824 addField( "stateTransitionOnAltTriggerDown", TypeString, Offset(stateTransitionAltTriggerDown, ShapeBaseImageData), MaxStates, 825 "Name of the state to transition to when the alt trigger state of the " 826 "Image changes to false (alt fire button up)." ); 827 addField( "stateTransitionOnTimeout", TypeString, Offset(stateTransitionTimeout, ShapeBaseImageData), MaxStates, 828 "Name of the state to transition to when we have been in this state " 829 "for stateTimeoutValue seconds." ); 830 831 addField( "stateTransitionGeneric0In", TypeString, Offset(stateTransitionGeneric0In, ShapeBaseImageData), MaxStates, 832 "Name of the state to transition to when the generic trigger 0 state " 833 "changes to true." ); 834 addField( "stateTransitionGeneric0Out", TypeString, Offset(stateTransitionGeneric0Out, ShapeBaseImageData), MaxStates, 835 "Name of the state to transition to when the generic trigger 0 state " 836 "changes to false." ); 837 addField( "stateTransitionGeneric1In", TypeString, Offset(stateTransitionGeneric1In, ShapeBaseImageData), MaxStates, 838 "Name of the state to transition to when the generic trigger 1 state " 839 "changes to true." ); 840 addField( "stateTransitionGeneric1Out", TypeString, Offset(stateTransitionGeneric1Out, ShapeBaseImageData), MaxStates, 841 "Name of the state to transition to when the generic trigger 1 state " 842 "changes to false." ); 843 addField( "stateTransitionGeneric2In", TypeString, Offset(stateTransitionGeneric2In, ShapeBaseImageData), MaxStates, 844 "Name of the state to transition to when the generic trigger 2 state " 845 "changes to true." ); 846 addField( "stateTransitionGeneric2Out", TypeString, Offset(stateTransitionGeneric2Out, ShapeBaseImageData), MaxStates, 847 "Name of the state to transition to when the generic trigger 2 state " 848 "changes to false." ); 849 addField( "stateTransitionGeneric3In", TypeString, Offset(stateTransitionGeneric3In, ShapeBaseImageData), MaxStates, 850 "Name of the state to transition to when the generic trigger 3 state " 851 "changes to true." ); 852 addField( "stateTransitionGeneric3Out", TypeString, Offset(stateTransitionGeneric3Out, ShapeBaseImageData), MaxStates, 853 "Name of the state to transition to when the generic trigger 3 state " 854 "changes to false." ); 855 856 addField( "stateTimeoutValue", TypeF32, Offset(stateTimeoutValue, ShapeBaseImageData), MaxStates, 857 "Time in seconds to wait before transitioning to stateTransitionOnTimeout." ); 858 addField( "stateWaitForTimeout", TypeBool, Offset(stateWaitForTimeout, ShapeBaseImageData), MaxStates, 859 "If false, this state ignores stateTimeoutValue and transitions " 860 "immediately if other transition conditions are met." ); 861 addField( "stateFire", TypeBool, Offset(stateFire, ShapeBaseImageData), MaxStates, 862 "The first state with this set to true is the state entered by the " 863 "client when it receives the 'fire' event." ); 864 addField( "stateAlternateFire", TypeBool, Offset(stateAlternateFire, ShapeBaseImageData), MaxStates, 865 "The first state with this set to true is the state entered by the " 866 "client when it receives the 'altFire' event." ); 867 addField( "stateReload", TypeBool, Offset(stateReload, ShapeBaseImageData), MaxStates, 868 "The first state with this set to true is the state entered by the " 869 "client when it receives the 'reload' event." ); 870 addField( "stateEjectShell", TypeBool, Offset(stateEjectShell, ShapeBaseImageData), MaxStates, 871 "If true, a shell casing will be ejected in this state." ); 872 addField( "stateEnergyDrain", TypeF32, Offset(stateEnergyDrain, ShapeBaseImageData), MaxStates, 873 "@brief Amount of energy to subtract from the Image in this state.\n\n" 874 "Energy is drained at stateEnergyDrain units/tick as long as we are in " 875 "this state.\n" 876 "@see usesEnergy"); 877 addField( "stateAllowImageChange", TypeBool, Offset(stateAllowImageChange, ShapeBaseImageData), MaxStates, 878 "@brief If false, other Images will temporarily be blocked from mounting " 879 "while the state machine is executing the tasks in this state.\n\n" 880 "For instance, if we have a rocket launcher, the player shouldn't " 881 "be able to switch out <i>while</i> firing. So, you'd set " 882 "stateAllowImageChange to false in firing states, and true the rest " 883 "of the time." ); 884 addField( "stateDirection", TypeBool, Offset(stateDirection, ShapeBaseImageData), MaxStates, 885 "@brief Direction of the animation to play in this state.\n\n" 886 "True is forward, false is backward." ); 887 addField( "stateLoadedFlag", TYPEID< ShapeBaseImageData::StateData::LoadedState >(), Offset(stateLoaded, ShapeBaseImageData), MaxStates, 888 "@brief Set the loaded state of the Image.\n\n" 889 "<ul><li>IgnoreLoaded: Don't change Image loaded state.</li>" 890 "<li>Loaded: Set Image loaded state to true.</li>" 891 "<li>NotLoaded: Set Image loaded state to false.</li></ul>\n" 892 "@see ShapeBaseImageLoadedState"); 893 addField( "stateSpinThread", TYPEID< ShapeBaseImageData::StateData::SpinState >(), Offset(stateSpin, ShapeBaseImageData), MaxStates, 894 "@brief Controls how fast the 'spin' animation sequence will be played in " 895 "this state.\n\n" 896 "<ul><li>Ignore: No change to the spin sequence.</li>" 897 "<li>Stop: Stops the spin sequence at its current position.</li>" 898 "<li>SpinUp: Increase spin sequence timeScale from 0 (on state entry) " 899 "to 1 (after stateTimeoutValue seconds).</li>" 900 "<li>SpinDown: Decrease spin sequence timeScale from 1 (on state entry) " 901 "to 0 (after stateTimeoutValue seconds).</li>" 902 "<li>FullSpeed: Resume the spin sequence playback at its current " 903 "position with timeScale=1.</li></ul>\n" 904 "@see ShapeBaseImageSpinState"); 905 addField( "stateRecoil", TYPEID< ShapeBaseImageData::StateData::RecoilState >(), Offset(stateRecoil, ShapeBaseImageData), MaxStates, 906 "@brief Type of recoil sequence to play on the ShapeBase object on entry to " 907 "this state.\n\n" 908 "<ul><li>NoRecoil: Do not play a recoil sequence.</li>" 909 "<li>LightRecoil: Play the light_recoil sequence.</li>" 910 "<li>MediumRecoil: Play the medium_recoil sequence.</li>" 911 "<li>HeavyRecoil: Play the heavy_recoil sequence.</li></ul>\n" 912 "@see ShapeBaseImageRecoilState"); 913 addField( "stateSequence", TypeString, Offset(stateSequence, ShapeBaseImageData), MaxStates, 914 "Name of the sequence to play on entry to this state." ); 915 addField( "stateSequenceRandomFlash", TypeBool, Offset(stateSequenceRandomFlash, ShapeBaseImageData), MaxStates, 916 "@brief If true, the muzzle flash sequence will be played while in this state.\n\n" 917 "The name of the muzzle flash sequence is the same as stateSequence, " 918 "with \"_vis\" at the end." ); 919 addField( "stateScaleAnimation", TypeBool, Offset(stateScaleAnimation, ShapeBaseImageData), MaxStates, 920 "If true, the timeScale of the stateSequence animation will be adjusted " 921 "such that the sequence plays for stateTimeoutValue seconds. " ); 922 addField( "stateScaleAnimationFP", TypeBool, Offset(stateScaleAnimationFP, ShapeBaseImageData), MaxStates, 923 "If true, the timeScale of the first person stateSequence animation will be adjusted " 924 "such that the sequence plays for stateTimeoutValue seconds. " ); 925 addField( "stateSequenceTransitionIn", TypeBool, Offset(stateSequenceTransitionIn, ShapeBaseImageData), MaxStates, 926 "Do we transition to the state's sequence when we enter the state?" ); 927 addField( "stateSequenceTransitionOut", TypeBool, Offset(stateSequenceTransitionOut, ShapeBaseImageData), MaxStates, 928 "Do we transition to the new state's sequence when we leave the state?" ); 929 addField( "stateSequenceNeverTransition", TypeBool, Offset(stateSequenceNeverTransition, ShapeBaseImageData), MaxStates, 930 "Never allow a transition to this sequence. Often used for a fire sequence." ); 931 addField( "stateSequenceTransitionTime", TypeF32, Offset(stateSequenceTransitionTime, ShapeBaseImageData), MaxStates, 932 "The time to transition in or out of a sequence." ); 933 934 addField( "stateShapeSequence", TypeString, Offset(stateShapeSequence, ShapeBaseImageData), MaxStates, 935 "Name of the sequence that is played on the mounting shape." ); 936 addField( "stateScaleShapeSequence", TypeBool, Offset(stateScaleShapeSequence, ShapeBaseImageData), MaxStates, 937 "Indicates if the sequence to be played on the mounting shape should be scaled to the length of the state." ); 938 939 addField( "stateSound", TypeSFXTrackName, Offset(stateSound, ShapeBaseImageData), MaxStates, 940 "Sound to play on entry to this state." ); 941 addField( "stateScript", TypeCaseString, Offset(stateScript, ShapeBaseImageData), MaxStates, 942 "@brief Method to execute on entering this state.\n\n" 943 "Scoped to this image class name, then ShapeBaseImageData. The script " 944 "callback function takes the same arguments as the onMount callback.\n" 945 "@see onMount() for the same arguments as this callback."); 946 947 addField( "stateEmitter", TYPEID< ParticleEmitterData >(), Offset(stateEmitter, ShapeBaseImageData), MaxStates, 948 "@brief Emitter to generate particles in this state (from muzzle point or " 949 "specified node).\n\n" 950 "@see stateEmitterNode" ); 951 addField( "stateEmitterTime", TypeF32, Offset(stateEmitterTime, ShapeBaseImageData), MaxStates, 952 "How long (in seconds) to emit particles on entry to this state." ); 953 addField( "stateEmitterNode", TypeString, Offset(stateEmitterNode, ShapeBaseImageData), MaxStates, 954 "@brief Name of the node to emit particles from.\n\n" 955 "@see stateEmitter" ); 956 addField( "stateIgnoreLoadedForReady", TypeBool, Offset(stateIgnoreLoadedForReady, ShapeBaseImageData), MaxStates, 957 "@brief If set to true, and both ready and loaded transitions are true, the " 958 "ready transition will be taken instead of the loaded transition.\n\n" 959 "A state is 'ready' if pressing the fire trigger in that state would " 960 "transition to the fire state." ); 961 962 endArray( "States" ); 963 964 addField( "computeCRC", TypeBool, Offset(computeCRC, ShapeBaseImageData), 965 "If true, verify that the CRC of the client's Image matches the server's " 966 "CRC for the Image when loaded by the client." ); 967 968 addField( "maxConcurrentSounds", TypeS32, Offset(maxConcurrentSounds, ShapeBaseImageData), 969 "@brief Maximum number of sounds this Image can play at a time.\n\n" 970 "Any value <= 0 indicates that it can play an infinite number of sounds." ); 971 972 addField( "useRemainderDT", TypeBool, Offset(useRemainderDT, ShapeBaseImageData), 973 "@brief If true, allow multiple timeout transitions to occur within a single " 974 "tick (useful if states have a very small timeout).\n\n" ); 975 976 Parent::initPersistFields(); 977} 978 979void ShapeBaseImageData::packData(BitStream* stream) 980{ 981 Parent::packData(stream); 982 983 if(stream->writeFlag(computeCRC)) 984 { 985 for( U32 j=0; j<MaxShapes; ++j ) 986 { 987 stream->write(mCRC[j]); 988 } 989 } 990 991 stream->writeString(shapeName); // shape 0 for normal use 992 stream->writeString(shapeNameFP); // shape 1 for first person use (optional) 993 994 stream->writeString(imageAnimPrefix); 995 stream->writeString(imageAnimPrefixFP); 996 997 stream->write(mountPoint); 998 if (!stream->writeFlag(mountOffset.isIdentity())) 999 stream->writeAffineTransform(mountOffset); 1000 if (!stream->writeFlag(eyeOffset.isIdentity())) 1001 stream->writeAffineTransform(eyeOffset); 1002 1003 stream->writeFlag(animateOnServer); 1004 1005 stream->write(scriptAnimTransitionTime); 1006 1007 stream->writeFlag(useEyeNode); 1008 1009 stream->writeFlag(correctMuzzleVector); 1010 stream->writeFlag(correctMuzzleVectorTP); 1011 stream->writeFlag(firstPerson); 1012 stream->write(mass); 1013 stream->writeFlag(usesEnergy); 1014 stream->write(minEnergy); 1015 1016 for( U32 j=0; j<MaxShapes; ++j) 1017 { 1018 stream->writeFlag(hasFlash[j]); 1019 } 1020 1021 // Client doesn't need accuFire 1022 1023 // Write the projectile datablock 1024 if (stream->writeFlag(projectile)) 1025 stream->writeRangedU32(mPacked ? SimObjectId((uintptr_t)projectile): 1026 projectile->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); 1027 1028 stream->writeFlag(cloakable); 1029 stream->writeRangedU32(lightType, 0, NumLightTypes-1); 1030 if(lightType != NoLight) 1031 { 1032 stream->write(lightRadius); 1033 stream->write(lightDuration); 1034 stream->writeFloat(lightColor.red, 7); 1035 stream->writeFloat(lightColor.green, 7); 1036 stream->writeFloat(lightColor.blue, 7); 1037 stream->writeFloat(lightColor.alpha, 7); 1038 stream->write(lightBrightness); 1039 } 1040 1041 if ( stream->writeFlag( shakeCamera ) ) 1042 { 1043 mathWrite( *stream, camShakeFreq ); 1044 mathWrite( *stream, camShakeAmp ); 1045 stream->write( camShakeDuration ); 1046 stream->write( camShakeRadius ); 1047 stream->write( camShakeFalloff ); 1048 } 1049 1050 mathWrite( *stream, shellExitDir ); 1051 stream->write(shellExitVariance); 1052 stream->write(shellVelocity); 1053 1054 if( stream->writeFlag( casing ) ) 1055 { 1056 stream->writeRangedU32(mPacked ? SimObjectId((uintptr_t)casing): 1057 casing->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); 1058 } 1059 1060 for (U32 i = 0; i < MaxStates; i++) 1061 if (stream->writeFlag(state[i].name && state[i].name[0])) { 1062 StateData& s = state[i]; 1063 // States info not needed on the client: 1064 // s.allowImageChange 1065 // s.scriptNames 1066 // Transitions are inc. one to account for -1 values 1067 stream->writeString(state[i].name); 1068 1069 stream->writeInt(s.transition.loaded[0]+1,NumStateBits); 1070 stream->writeInt(s.transition.loaded[1]+1,NumStateBits); 1071 stream->writeInt(s.transition.ammo[0]+1,NumStateBits); 1072 stream->writeInt(s.transition.ammo[1]+1,NumStateBits); 1073 stream->writeInt(s.transition.target[0]+1,NumStateBits); 1074 stream->writeInt(s.transition.target[1]+1,NumStateBits); 1075 stream->writeInt(s.transition.wet[0]+1,NumStateBits); 1076 stream->writeInt(s.transition.wet[1]+1,NumStateBits); 1077 stream->writeInt(s.transition.trigger[0]+1,NumStateBits); 1078 stream->writeInt(s.transition.trigger[1]+1,NumStateBits); 1079 stream->writeInt(s.transition.altTrigger[0]+1,NumStateBits); 1080 stream->writeInt(s.transition.altTrigger[1]+1,NumStateBits); 1081 stream->writeInt(s.transition.timeout+1,NumStateBits); 1082 1083 // Most states don't make use of the motion transition. 1084 if (stream->writeFlag(s.transition.motion[0] != -1 || s.transition.motion[1] != -1)) 1085 { 1086 // This state does 1087 stream->writeInt(s.transition.motion[0]+1,NumStateBits); 1088 stream->writeInt(s.transition.motion[1]+1,NumStateBits); 1089 } 1090 1091 // Most states don't make use of the generic trigger transitions. Don't transmit 1092 // if that is the case here. 1093 for (U32 j=0; j<MaxGenericTriggers; ++j) 1094 { 1095 if (stream->writeFlag(s.transition.genericTrigger[j][0] != -1 || s.transition.genericTrigger[j][1] != -1)) 1096 { 1097 stream->writeInt(s.transition.genericTrigger[j][0]+1,NumStateBits); 1098 stream->writeInt(s.transition.genericTrigger[j][1]+1,NumStateBits); 1099 } 1100 } 1101 1102 if(stream->writeFlag(s.timeoutValue != gDefaultStateData.timeoutValue)) 1103 stream->write(s.timeoutValue); 1104 1105 stream->writeFlag(s.waitForTimeout); 1106 stream->writeFlag(s.fire); 1107 stream->writeFlag(s.altFire); 1108 stream->writeFlag(s.reload); 1109 stream->writeFlag(s.ejectShell); 1110 stream->writeFlag(s.scaleAnimation); 1111 stream->writeFlag(s.scaleAnimationFP); 1112 stream->writeFlag(s.direction); 1113 1114 stream->writeFlag(s.sequenceTransitionIn); 1115 stream->writeFlag(s.sequenceTransitionOut); 1116 stream->writeFlag(s.sequenceNeverTransition); 1117 if(stream->writeFlag(s.sequenceTransitionTime != gDefaultStateData.sequenceTransitionTime)) 1118 stream->write(s.sequenceTransitionTime); 1119 1120 stream->writeString(s.shapeSequence); 1121 stream->writeFlag(s.shapeSequenceScale); 1122 1123 if(stream->writeFlag(s.energyDrain != gDefaultStateData.energyDrain)) 1124 stream->write(s.energyDrain); 1125 1126 stream->writeInt(s.loaded,StateData::NumLoadedBits); 1127 stream->writeInt(s.spin,StateData::NumSpinBits); 1128 stream->writeInt(s.recoil,StateData::NumRecoilBits); 1129 1130 for( U32 j=0; j<MaxShapes; ++j ) 1131 { 1132 if(stream->writeFlag(s.sequence[j] != gDefaultStateData.sequence[j])) 1133 stream->writeSignedInt(s.sequence[j], 16); 1134 1135 if(stream->writeFlag(s.sequenceVis[j] != gDefaultStateData.sequenceVis[j])) 1136 stream->writeSignedInt(s.sequenceVis[j],16); 1137 1138 stream->writeFlag(s.flashSequence[j]); 1139 } 1140 1141 stream->writeFlag(s.ignoreLoadedForReady); 1142 1143 if (stream->writeFlag(s.emitter)) 1144 { 1145 stream->writeRangedU32(mPacked ? SimObjectId((uintptr_t)s.emitter): 1146 s.emitter->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); 1147 stream->write(s.emitterTime); 1148 1149 for( U32 j=0; j<MaxShapes; ++j ) 1150 { 1151 stream->write(s.emitterNode[j]); 1152 } 1153 } 1154 1155 sfxWrite( stream, s.sound ); 1156 } 1157 stream->write(maxConcurrentSounds); 1158 stream->writeFlag(useRemainderDT); 1159} 1160 1161void ShapeBaseImageData::unpackData(BitStream* stream) 1162{ 1163 Parent::unpackData(stream); 1164 computeCRC = stream->readFlag(); 1165 if(computeCRC) 1166 { 1167 for( U32 j=0; j<MaxShapes; ++j ) 1168 { 1169 stream->read(&mCRC[j]); 1170 } 1171 } 1172 1173 shapeName = stream->readSTString(); // shape 0 for normal use 1174 shapeNameFP = stream->readSTString(); // shape 1 for first person use (optional) 1175 1176 imageAnimPrefix = stream->readSTString(); 1177 imageAnimPrefixFP = stream->readSTString(); 1178 1179 stream->read(&mountPoint); 1180 if (stream->readFlag()) 1181 mountOffset.identity(); 1182 else 1183 stream->readAffineTransform(&mountOffset); 1184 if (stream->readFlag()) 1185 eyeOffset.identity(); 1186 else 1187 stream->readAffineTransform(&eyeOffset); 1188 1189 animateOnServer = stream->readFlag(); 1190 1191 stream->read(&scriptAnimTransitionTime); 1192 1193 useEyeNode = stream->readFlag(); 1194 1195 correctMuzzleVector = stream->readFlag(); 1196 correctMuzzleVectorTP = stream->readFlag(); 1197 firstPerson = stream->readFlag(); 1198 stream->read(&mass); 1199 usesEnergy = stream->readFlag(); 1200 stream->read(&minEnergy); 1201 1202 for( U32 j=0; j<MaxShapes; ++j ) 1203 { 1204 hasFlash[j] = stream->readFlag(); 1205 } 1206 1207 projectile = (stream->readFlag() ? 1208 (ProjectileData*)(uintptr_t)stream->readRangedU32(DataBlockObjectIdFirst, 1209 DataBlockObjectIdLast) : 0); 1210 1211 cloakable = stream->readFlag(); 1212 lightType = stream->readRangedU32(0, NumLightTypes-1); 1213 if(lightType != NoLight) 1214 { 1215 stream->read(&lightRadius); 1216 stream->read(&lightDuration); 1217 lightColor.red = stream->readFloat(7); 1218 lightColor.green = stream->readFloat(7); 1219 lightColor.blue = stream->readFloat(7); 1220 lightColor.alpha = stream->readFloat(7); 1221 stream->read( &lightBrightness ); 1222 } 1223 1224 shakeCamera = stream->readFlag(); 1225 if ( shakeCamera ) 1226 { 1227 mathRead( *stream, &camShakeFreq ); 1228 mathRead( *stream, &camShakeAmp ); 1229 stream->read( &camShakeDuration ); 1230 stream->read( &camShakeRadius ); 1231 stream->read( &camShakeFalloff ); 1232 } 1233 1234 mathRead( *stream, &shellExitDir ); 1235 stream->read(&shellExitVariance); 1236 stream->read(&shellVelocity); 1237 1238 if(stream->readFlag()) 1239 { 1240 casingID = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); 1241 } 1242 1243 for (U32 i = 0; i < MaxStates; i++) { 1244 if (stream->readFlag()) { 1245 StateData& s = state[i]; 1246 // States info not needed on the client: 1247 // s.allowImageChange 1248 // s.scriptNames 1249 // Transitions are dec. one to restore -1 values 1250 s.name = stream->readSTString(); 1251 1252 s.transition.loaded[0] = stream->readInt(NumStateBits) - 1; 1253 s.transition.loaded[1] = stream->readInt(NumStateBits) - 1; 1254 s.transition.ammo[0] = stream->readInt(NumStateBits) - 1; 1255 s.transition.ammo[1] = stream->readInt(NumStateBits) - 1; 1256 s.transition.target[0] = stream->readInt(NumStateBits) - 1; 1257 s.transition.target[1] = stream->readInt(NumStateBits) - 1; 1258 s.transition.wet[0] = stream->readInt(NumStateBits) - 1; 1259 s.transition.wet[1] = stream->readInt(NumStateBits) - 1; 1260 s.transition.trigger[0] = stream->readInt(NumStateBits) - 1; 1261 s.transition.trigger[1] = stream->readInt(NumStateBits) - 1; 1262 s.transition.altTrigger[0] = stream->readInt(NumStateBits) - 1; 1263 s.transition.altTrigger[1] = stream->readInt(NumStateBits) - 1; 1264 s.transition.timeout = stream->readInt(NumStateBits) - 1; 1265 1266 // Motion trigger 1267 if (stream->readFlag()) 1268 { 1269 s.transition.motion[0] = stream->readInt(NumStateBits) - 1; 1270 s.transition.motion[1] = stream->readInt(NumStateBits) - 1; 1271 } 1272 else 1273 { 1274 s.transition.motion[0] = -1; 1275 s.transition.motion[1] = -1; 1276 } 1277 1278 // Generic triggers 1279 for (U32 j=0; j<MaxGenericTriggers; ++j) 1280 { 1281 if (stream->readFlag()) 1282 { 1283 s.transition.genericTrigger[j][0] = stream->readInt(NumStateBits) - 1; 1284 s.transition.genericTrigger[j][1] = stream->readInt(NumStateBits) - 1; 1285 } 1286 else 1287 { 1288 s.transition.genericTrigger[j][0] = -1; 1289 s.transition.genericTrigger[j][1] = -1; 1290 } 1291 } 1292 1293 if(stream->readFlag()) 1294 stream->read(&s.timeoutValue); 1295 else 1296 s.timeoutValue = gDefaultStateData.timeoutValue; 1297 1298 s.waitForTimeout = stream->readFlag(); 1299 s.fire = stream->readFlag(); 1300 s.altFire = stream->readFlag(); 1301 s.reload = stream->readFlag(); 1302 s.ejectShell = stream->readFlag(); 1303 s.scaleAnimation = stream->readFlag(); 1304 s.scaleAnimationFP = stream->readFlag(); 1305 s.direction = stream->readFlag(); 1306 1307 s.sequenceTransitionIn = stream->readFlag(); 1308 s.sequenceTransitionOut = stream->readFlag(); 1309 s.sequenceNeverTransition = stream->readFlag(); 1310 if (stream->readFlag()) 1311 stream->read(&s.sequenceTransitionTime); 1312 else 1313 s.sequenceTransitionTime = gDefaultStateData.sequenceTransitionTime; 1314 1315 s.shapeSequence = stream->readSTString(); 1316 s.shapeSequenceScale = stream->readFlag(); 1317 1318 if(stream->readFlag()) 1319 stream->read(&s.energyDrain); 1320 else 1321 s.energyDrain = gDefaultStateData.energyDrain; 1322 1323 s.loaded = (StateData::LoadedState)stream->readInt(StateData::NumLoadedBits); 1324 s.spin = (StateData::SpinState)stream->readInt(StateData::NumSpinBits); 1325 s.recoil = (StateData::RecoilState)stream->readInt(StateData::NumRecoilBits); 1326 1327 for( U32 j=0; j<MaxShapes; ++j ) 1328 { 1329 if(stream->readFlag()) 1330 s.sequence[j] = stream->readSignedInt(16); 1331 else 1332 s.sequence[j] = gDefaultStateData.sequence[j]; 1333 1334 if(stream->readFlag()) 1335 s.sequenceVis[j] = stream->readSignedInt(16); 1336 else 1337 s.sequenceVis[j] = gDefaultStateData.sequenceVis[j]; 1338 1339 s.flashSequence[j] = stream->readFlag(); 1340 } 1341 1342 s.ignoreLoadedForReady = stream->readFlag(); 1343 1344 if (stream->readFlag()) 1345 { 1346 s.emitter = (ParticleEmitterData*)(uintptr_t)stream->readRangedU32(DataBlockObjectIdFirst, 1347 DataBlockObjectIdLast); 1348 stream->read(&s.emitterTime); 1349 1350 for( U32 j=0; j<MaxShapes; ++j ) 1351 { 1352 stream->read(&(s.emitterNode[j])); 1353 } 1354 } 1355 else 1356 s.emitter = 0; 1357 1358 sfxRead( stream, &s.sound ); 1359 } 1360 } 1361 1362 stream->read(&maxConcurrentSounds); 1363 useRemainderDT = stream->readFlag(); 1364 1365 statesLoaded = true; 1366} 1367 1368void ShapeBaseImageData::inspectPostApply() 1369{ 1370 Parent::inspectPostApply(); 1371 1372 // This does not do a very good job of applying changes to states 1373 // which may have occured in the editor, but at least we can do this... 1374 useEyeOffset = !eyeOffset.isIdentity(); 1375} 1376 1377//---------------------------------------------------------------------------- 1378//---------------------------------------------------------------------------- 1379 1380//---------------------------------------------------------------------------- 1381 1382ShapeBase::MountedImage::MountedImage() 1383{ 1384 for (U32 i=0; i<ShapeBaseImageData::MaxShapes; ++i) 1385 { 1386 shapeInstance[i] = 0; 1387 ambientThread[i] = NULL; 1388 visThread[i] = NULL; 1389 animThread[i] = NULL; 1390 flashThread[i] = NULL; 1391 spinThread[i] = NULL; 1392 } 1393 1394 doAnimateAllShapes = false; 1395 forceAnimateAllShapes = false; 1396 lastShapeIndex = 0; 1397 1398 state = 0; 1399 dataBlock = 0; 1400 nextImage = InvalidImagePtr; 1401 delayTime = 0; 1402 rDT = 0.0f; 1403 ammo = false; 1404 target = false; 1405 triggerDown = false; 1406 altTriggerDown = false; 1407 loaded = false; 1408 fireCount = 0; 1409 altFireCount = 0; 1410 reloadCount = 0; 1411 wet = false; 1412 motion = false; 1413 lightStart = 0; 1414 lightInfo = NULL; 1415 dMemset(emitter, 0, sizeof(emitter)); 1416 1417 for (U32 i=0; i<ShapeBaseImageData::MaxGenericTriggers; ++i) 1418 { 1419 genericTrigger[i] = false; 1420 } 1421 1422 nextLoaded = false; 1423} 1424 1425ShapeBase::MountedImage::~MountedImage() 1426{ 1427 for (U32 i=0; i<ShapeBaseImageData::MaxShapes; ++i) 1428 { 1429 delete shapeInstance[i]; 1430 shapeInstance[i] = 0; 1431 } 1432 1433 // stop sound 1434 for(Vector<SFXSource*>::iterator i = mSoundSources.begin(); i != mSoundSources.end(); i++) 1435 { 1436 SFX_DELETE((*i)); 1437 } 1438 mSoundSources.clear(); 1439 1440 for (S32 i = 0; i < MaxImageEmitters; i++) 1441 if (bool(emitter[i].emitter)) 1442 emitter[i].emitter->deleteWhenEmpty(); 1443 1444 if ( lightInfo != NULL ) 1445 delete lightInfo; 1446} 1447 1448void ShapeBase::MountedImage::addSoundSource(SFXSource* source) 1449{ 1450 if(source != NULL) 1451 { 1452 if(dataBlock->maxConcurrentSounds > 0 && mSoundSources.size() > dataBlock->maxConcurrentSounds) 1453 { 1454 SFX_DELETE(mSoundSources.first()); 1455 mSoundSources.pop_front(); 1456 } 1457 source->play(); 1458 mSoundSources.push_back(source); 1459 } 1460} 1461 1462void ShapeBase::MountedImage::updateSoundSources( const MatrixF &renderTransform ) 1463{ 1464 // Update all the sounds removing any ones that have stopped. 1465 for ( U32 i=0; i < mSoundSources.size(); ) 1466 { 1467 SFXSource *source = mSoundSources[i]; 1468 1469 if ( source->isStopped() ) 1470 { 1471 SFX_DELETE( source ); 1472 mSoundSources.erase_fast( i ); 1473 continue; 1474 } 1475 1476 source->setTransform(renderTransform); 1477 i++; 1478 } 1479} 1480 1481void ShapeBase::MountedImage::updateDoAnimateAllShapes(const ShapeBase* owner) 1482{ 1483 doAnimateAllShapes = false; 1484 if (!dataBlock) 1485 return; 1486 1487 // According to ShapeBase::isFirstPerson() the server is always in first person mode. 1488 // Therefore we don't need to animate any other shapes but the one that will be 1489 // used for first person. 1490 1491 // Sometimes this is forced externally, so honour it. 1492 if (forceAnimateAllShapes) 1493 { 1494 doAnimateAllShapes = true; 1495 return; 1496 } 1497 1498 if (owner->isClientObject()) 1499 { 1500 // If this client object doesn't have a controlling client, then according to 1501 // ShapeBase::isFirstPerson() it cannot ever be in first person mode. So no need 1502 // to animate any shapes beyond the current one. 1503 if (!owner->getControllingClient()) 1504 { 1505 return; 1506 } 1507 1508 doAnimateAllShapes = dataBlock->animateAllShapes; 1509 } 1510} 1511 1512//---------------------------------------------------------------------------- 1513//---------------------------------------------------------------------------- 1514 1515//---------------------------------------------------------------------------- 1516// Any item with an item image is selectable 1517 1518bool ShapeBase::mountImage(ShapeBaseImageData* imageData,U32 imageSlot,bool loaded,NetStringHandle &skinNameHandle) 1519{ 1520 AssertFatal(imageSlot<MaxMountedImages,"Out of range image slot"); 1521 1522 MountedImage& image = mMountedImageList[imageSlot]; 1523 if (image.dataBlock) { 1524 if ((image.dataBlock == imageData) && (image.skinNameHandle == skinNameHandle)) { 1525 // Image already loaded 1526 image.nextImage = InvalidImagePtr; 1527 return true; 1528 } 1529 } 1530 // 1531 setImage(imageSlot,imageData,skinNameHandle,loaded); 1532 1533 return true; 1534} 1535 1536bool ShapeBase::unmountImage(U32 imageSlot) 1537{ 1538 AssertFatal(imageSlot<MaxMountedImages,"Out of range image slot"); 1539 1540 bool returnValue = false; 1541 MountedImage& image = mMountedImageList[imageSlot]; 1542 if (image.dataBlock) 1543 { 1544 NetStringHandle temp; 1545 setImage(imageSlot,0, temp); 1546 returnValue = true; 1547 } 1548 1549 return returnValue; 1550} 1551 1552 1553//---------------------------------------------------------------------------- 1554 1555ShapeBaseImageData* ShapeBase::getMountedImage(U32 imageSlot) 1556{ 1557 AssertFatal(imageSlot<MaxMountedImages,"Out of range image slot"); 1558 1559 return mMountedImageList[imageSlot].dataBlock; 1560} 1561 1562 1563ShapeBase::MountedImage* ShapeBase::getImageStruct(U32 imageSlot) 1564{ 1565 return &mMountedImageList[imageSlot]; 1566} 1567 1568 1569ShapeBaseImageData* ShapeBase::getPendingImage(U32 imageSlot) 1570{ 1571 ShapeBaseImageData* data = mMountedImageList[imageSlot].nextImage; 1572 return (data == InvalidImagePtr)? 0: data; 1573} 1574 1575bool ShapeBase::isImageFiring(U32 imageSlot) 1576{ 1577 MountedImage& image = mMountedImageList[imageSlot]; 1578 return image.dataBlock && image.state->fire; 1579} 1580 1581bool ShapeBase::isImageAltFiring(U32 imageSlot) 1582{ 1583 MountedImage& image = mMountedImageList[imageSlot]; 1584 return image.dataBlock && image.state->altFire; 1585} 1586 1587bool ShapeBase::isImageReloading(U32 imageSlot) 1588{ 1589 MountedImage& image = mMountedImageList[imageSlot]; 1590 return image.dataBlock && image.state->reload; 1591} 1592 1593bool ShapeBase::isImageReady(U32 imageSlot,U32 ns,U32 depth) 1594{ 1595 // Will pressing the trigger lead to a fire state? 1596 MountedImage& image = mMountedImageList[imageSlot]; 1597 if (depth++ > 5 || !image.dataBlock) 1598 return false; 1599 ShapeBaseImageData::StateData& stateData = (ns == -1) ? 1600 *image.state : image.dataBlock->state[ns]; 1601 if (stateData.fire) 1602 return true; 1603 1604 // Try the transitions... 1605 if (stateData.ignoreLoadedForReady == true) { 1606 if ((ns = stateData.transition.loaded[true]) != -1) 1607 if (isImageReady(imageSlot,ns,depth)) 1608 return true; 1609 } else { 1610 if ((ns = stateData.transition.loaded[image.loaded]) != -1) 1611 if (isImageReady(imageSlot,ns,depth)) 1612 return true; 1613 } 1614 for (U32 i=0; i<ShapeBaseImageData::MaxGenericTriggers; ++i) 1615 { 1616 if ((ns = stateData.transition.genericTrigger[i][image.genericTrigger[i]]) != -1) 1617 if (isImageReady(imageSlot,ns,depth)) 1618 return true; 1619 } 1620 if ((ns = stateData.transition.ammo[image.ammo]) != -1) 1621 if (isImageReady(imageSlot,ns,depth)) 1622 return true; 1623 if ((ns = stateData.transition.target[image.target]) != -1) 1624 if (isImageReady(imageSlot,ns,depth)) 1625 return true; 1626 if ((ns = stateData.transition.wet[image.wet]) != -1) 1627 if (isImageReady(imageSlot,ns,depth)) 1628 return true; 1629 if ((ns = stateData.transition.motion[image.motion]) != -1) 1630 if (isImageReady(imageSlot,ns,depth)) 1631 return true; 1632 if ((ns = stateData.transition.trigger[1]) != -1) 1633 if (isImageReady(imageSlot,ns,depth)) 1634 return true; 1635 if ((ns = stateData.transition.altTrigger[1]) != -1) 1636 if (isImageReady(imageSlot,ns,depth)) 1637 return true; 1638 if ((ns = stateData.transition.timeout) != -1) 1639 if (isImageReady(imageSlot,ns,depth)) 1640 return true; 1641 return false; 1642} 1643 1644bool ShapeBase::isImageMounted(ShapeBaseImageData* imageData) 1645{ 1646 for (U32 i = 0; i < MaxMountedImages; i++) 1647 if (imageData == mMountedImageList[i].dataBlock) 1648 return true; 1649 return false; 1650} 1651 1652S32 ShapeBase::getMountSlot(ShapeBaseImageData* imageData) 1653{ 1654 for (U32 i = 0; i < MaxMountedImages; i++) 1655 if (imageData == mMountedImageList[i].dataBlock) 1656 return i; 1657 return -1; 1658} 1659 1660NetStringHandle ShapeBase::getImageSkinTag(U32 imageSlot) 1661{ 1662 MountedImage& image = mMountedImageList[imageSlot]; 1663 return image.dataBlock? image.skinNameHandle : NetStringHandle(); 1664} 1665 1666const char* ShapeBase::getImageState(U32 imageSlot) 1667{ 1668 MountedImage& image = mMountedImageList[imageSlot]; 1669 return image.dataBlock? image.state->name: 0; 1670} 1671 1672void ShapeBase::setImageGenericTriggerState(U32 imageSlot, U32 trigger, bool state) 1673{ 1674 MountedImage& image = mMountedImageList[imageSlot]; 1675 if (image.dataBlock && image.genericTrigger[trigger] != state) { 1676 setMaskBits(ImageMaskN << imageSlot); 1677 image.genericTrigger[trigger] = state; 1678 } 1679} 1680 1681bool ShapeBase::getImageGenericTriggerState(U32 imageSlot, U32 trigger) 1682{ 1683 MountedImage& image = mMountedImageList[imageSlot]; 1684 if (!image.dataBlock) 1685 return false; 1686 return image.genericTrigger[trigger]; 1687} 1688 1689void ShapeBase::setImageAmmoState(U32 imageSlot,bool ammo) 1690{ 1691 MountedImage& image = mMountedImageList[imageSlot]; 1692 if (image.dataBlock && !image.dataBlock->usesEnergy && image.ammo != ammo) { 1693 setMaskBits(ImageMaskN << imageSlot); 1694 image.ammo = ammo; 1695 } 1696} 1697 1698bool ShapeBase::getImageAmmoState(U32 imageSlot) 1699{ 1700 MountedImage& image = mMountedImageList[imageSlot]; 1701 if (!image.dataBlock) 1702 return false; 1703 return image.ammo; 1704} 1705 1706void ShapeBase::setImageWetState(U32 imageSlot,bool wet) 1707{ 1708 MountedImage& image = mMountedImageList[imageSlot]; 1709 if (image.dataBlock && image.wet != wet) { 1710 setMaskBits(ImageMaskN << imageSlot); 1711 image.wet = wet; 1712 } 1713} 1714 1715bool ShapeBase::getImageWetState(U32 imageSlot) 1716{ 1717 MountedImage& image = mMountedImageList[imageSlot]; 1718 if (!image.dataBlock) 1719 return false; 1720 return image.wet; 1721} 1722 1723void ShapeBase::setImageMotionState(U32 imageSlot,bool motion) 1724{ 1725 MountedImage& image = mMountedImageList[imageSlot]; 1726 if (image.dataBlock && image.motion != motion) { 1727 setMaskBits(ImageMaskN << imageSlot); 1728 image.motion = motion; 1729 } 1730} 1731 1732bool ShapeBase::getImageMotionState(U32 imageSlot) 1733{ 1734 MountedImage& image = mMountedImageList[imageSlot]; 1735 if (!image.dataBlock) 1736 return false; 1737 return image.motion; 1738} 1739 1740void ShapeBase::setImageTargetState(U32 imageSlot,bool target) 1741{ 1742 MountedImage& image = mMountedImageList[imageSlot]; 1743 if (image.dataBlock && image.target != target) { 1744 setMaskBits(ImageMaskN << imageSlot); 1745 image.target = target; 1746 } 1747} 1748 1749bool ShapeBase::getImageTargetState(U32 imageSlot) 1750{ 1751 MountedImage& image = mMountedImageList[imageSlot]; 1752 if (!image.dataBlock) 1753 return false; 1754 return image.target; 1755} 1756 1757void ShapeBase::setImageLoadedState(U32 imageSlot,bool loaded) 1758{ 1759 MountedImage& image = mMountedImageList[imageSlot]; 1760 if (image.dataBlock && image.loaded != loaded) { 1761 setMaskBits(ImageMaskN << imageSlot); 1762 image.loaded = loaded; 1763 } 1764} 1765 1766bool ShapeBase::getImageLoadedState(U32 imageSlot) 1767{ 1768 MountedImage& image = mMountedImageList[imageSlot]; 1769 if (!image.dataBlock) 1770 return false; 1771 return image.loaded; 1772} 1773 1774void ShapeBase::getMuzzleVector(U32 imageSlot,VectorF* vec) 1775{ 1776 MatrixF mat; 1777 getMuzzleTransform(imageSlot,&mat); 1778 1779 GameConnection * gc = getControllingClient(); 1780 if (gc && !gc->isAIControlled()) 1781 { 1782 MountedImage& image = mMountedImageList[imageSlot]; 1783 1784 bool fp = gc->isFirstPerson(); 1785 if ((fp && image.dataBlock->correctMuzzleVector) || 1786 (!fp && image.dataBlock->correctMuzzleVectorTP)) 1787 if (getCorrectedAim(mat, vec)) 1788 return; 1789 } 1790 1791 mat.getColumn(1,vec); 1792} 1793 1794void ShapeBase::getMuzzlePoint(U32 imageSlot,Point3F* pos) 1795{ 1796 MatrixF mat; 1797 getMuzzleTransform(imageSlot,&mat); 1798 mat.getColumn(3,pos); 1799} 1800 1801 1802void ShapeBase::getRenderMuzzleVector(U32 imageSlot,VectorF* vec) 1803{ 1804 MatrixF mat; 1805 getRenderMuzzleTransform(imageSlot,&mat); 1806 1807 GameConnection * gc = getControllingClient(); 1808 if (gc && !gc->isAIControlled()) 1809 { 1810 MountedImage& image = mMountedImageList[imageSlot]; 1811 1812 bool fp = gc->isFirstPerson(); 1813 if ((fp && image.dataBlock->correctMuzzleVector) || 1814 (!fp && image.dataBlock->correctMuzzleVectorTP)) 1815 if (getCorrectedAim(mat, vec)) 1816 return; 1817 } 1818 1819 mat.getColumn(1,vec); 1820} 1821 1822void ShapeBase::getRenderMuzzlePoint(U32 imageSlot,Point3F* pos) 1823{ 1824 MatrixF mat; 1825 getRenderMuzzleTransform(imageSlot,&mat); 1826 mat.getColumn(3,pos); 1827} 1828 1829//---------------------------------------------------------------------------- 1830 1831void ShapeBase::scriptCallback(U32 imageSlot,const char* function) 1832{ 1833 MountedImage &image = mMountedImageList[imageSlot]; 1834 1835 char buff1[32]; 1836 dSprintf( buff1, 32, "%d", imageSlot ); 1837 1838 char buff2[32]; 1839 dSprintf( buff2, 32, "%f", image.dataBlock->useRemainderDT ? image.rDT : 0.0f ); 1840 1841 Con::executef( image.dataBlock, function, getIdString(), buff1, buff2 ); 1842} 1843 1844 1845//---------------------------------------------------------------------------- 1846 1847void ShapeBase::getMountTransform( S32 index, const MatrixF &xfm, MatrixF *outMat ) 1848{ 1849 // Returns mount point to world space transform 1850 if ( index >= 0 && index < SceneObject::NumMountPoints) { 1851 S32 ni = mDataBlock->mountPointNode[index]; 1852 if (ni != -1) { 1853 MatrixF mountTransform = mShapeInstance->mNodeTransforms[ni]; 1854 mountTransform.mul( xfm ); 1855 const Point3F& scale = getScale(); 1856 1857 // The position of the mount point needs to be scaled. 1858 Point3F position = mountTransform.getPosition(); 1859 position.convolve( scale ); 1860 mountTransform.setPosition( position ); 1861 1862 // Also we would like the object to be scaled to the model. 1863 outMat->mul(mObjToWorld, mountTransform); 1864 return; 1865 } 1866 } 1867 1868 // Then let SceneObject handle it. 1869 Parent::getMountTransform( index, xfm, outMat ); 1870} 1871 1872void ShapeBase::getImageTransform(U32 imageSlot,MatrixF* mat) 1873{ 1874 // Image transform in world space 1875 MountedImage& image = mMountedImageList[imageSlot]; 1876 if (image.dataBlock) { 1877 ShapeBaseImageData& data = *image.dataBlock; 1878 U32 shapeIndex = getImageShapeIndex(image); 1879 1880 MatrixF nmat; 1881 if (data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1) { 1882 // We need to animate, even on the server, to make sure the nodes are in the correct location. 1883 image.shapeInstance[shapeIndex]->animate(); 1884 1885 getEyeBaseTransform(&nmat, mDataBlock->mountedImagesBank); 1886 1887 MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]]; 1888 1889 mat->mul(nmat, mountTransform); 1890 } 1891 else if (data.useEyeOffset && isFirstPerson()) { 1892 getEyeTransform(&nmat); 1893 mat->mul(nmat,data.eyeOffset); 1894 } 1895 else { 1896 getMountTransform( image.dataBlock->mountPoint, MatrixF::Identity, &nmat ); 1897 mat->mul(nmat,data.mountTransform[shapeIndex]); 1898 } 1899 } 1900 else 1901 *mat = mObjToWorld; 1902} 1903 1904void ShapeBase::getImageTransform(U32 imageSlot,S32 node,MatrixF* mat) 1905{ 1906 // Image transform in world space 1907 MountedImage& image = mMountedImageList[imageSlot]; 1908 if (image.dataBlock) 1909 { 1910 if (node != -1) 1911 { 1912 ShapeBaseImageData& data = *image.dataBlock; 1913 U32 shapeIndex = getImageShapeIndex(image); 1914 1915 MatrixF nmat = image.shapeInstance[shapeIndex]->mNodeTransforms[node]; 1916 MatrixF mmat; 1917 1918 if (data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1) 1919 { 1920 // We need to animate, even on the server, to make sure the nodes are in the correct location. 1921 image.shapeInstance[shapeIndex]->animate(); 1922 1923 MatrixF emat; 1924 getEyeBaseTransform(&emat, mDataBlock->mountedImagesBank); 1925 1926 MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]]; 1927 mountTransform.affineInverse(); 1928 1929 mmat.mul(emat, mountTransform); 1930 } 1931 else if (data.useEyeOffset && isFirstPerson()) 1932 { 1933 MatrixF emat; 1934 getEyeTransform(&emat); 1935 mmat.mul(emat,data.eyeOffset); 1936 } 1937 else 1938 { 1939 MatrixF emat; 1940 getMountTransform( image.dataBlock->mountPoint, MatrixF::Identity, &emat ); 1941 mmat.mul(emat,data.mountTransform[shapeIndex]); 1942 } 1943 1944 mat->mul(mmat, nmat); 1945 } 1946 else 1947 getImageTransform(imageSlot,mat); 1948 } 1949 else 1950 *mat = mObjToWorld; 1951} 1952 1953void ShapeBase::getImageTransform(U32 imageSlot,StringTableEntry nodeName,MatrixF* mat) 1954{ 1955 getImageTransform( imageSlot, getNodeIndex( imageSlot, nodeName ), mat ); 1956} 1957 1958void ShapeBase::getMuzzleTransform(U32 imageSlot,MatrixF* mat) 1959{ 1960 // Muzzle transform in world space 1961 MountedImage& image = mMountedImageList[imageSlot]; 1962 if (image.dataBlock) 1963 getImageTransform(imageSlot,image.dataBlock->muzzleNode[getImageShapeIndex(image)],mat); 1964 else 1965 *mat = mObjToWorld; 1966} 1967 1968 1969//---------------------------------------------------------------------------- 1970 1971void ShapeBase::getRenderMountTransform( F32 delta, S32 mountPoint, const MatrixF &xfm, MatrixF *outMat ) 1972{ 1973 // Returns mount point to world space transform 1974 if ( mountPoint >= 0 && mountPoint < SceneObject::NumMountPoints) { 1975 S32 ni = mDataBlock->mountPointNode[mountPoint]; 1976 if (ni != -1) { 1977 MatrixF mountTransform = mShapeInstance->mNodeTransforms[ni]; 1978 mountTransform.mul( xfm ); 1979 const Point3F& scale = getScale(); 1980 1981 // The position of the mount point needs to be scaled. 1982 Point3F position = mountTransform.getPosition(); 1983 position.convolve( scale ); 1984 mountTransform.setPosition( position ); 1985 1986 // Also we would like the object to be scaled to the model. 1987 mountTransform.scale( scale ); 1988 outMat->mul(getRenderTransform(), mountTransform); 1989 return; 1990 } 1991 } 1992 1993 // Then let SceneObject handle it. 1994 Parent::getRenderMountTransform( delta, mountPoint, xfm, outMat ); 1995} 1996 1997 1998void ShapeBase::getRenderImageTransform( U32 imageSlot, MatrixF* mat, bool noEyeOffset ) 1999{ 2000 // Image transform in world space 2001 MountedImage& image = mMountedImageList[imageSlot]; 2002 if (image.dataBlock) 2003 { 2004 ShapeBaseImageData& data = *image.dataBlock; 2005 U32 shapeIndex = getImageShapeIndex(image); 2006 2007 MatrixF nmat; 2008 if ( data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1 ) { 2009 getRenderEyeBaseTransform(&nmat, mDataBlock->mountedImagesBank); 2010 2011 MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]]; 2012 2013 mat->mul(nmat, mountTransform); 2014 } 2015 else if ( !noEyeOffset && data.useEyeOffset && isFirstPerson() ) 2016 { 2017 getRenderEyeTransform(&nmat); 2018 mat->mul(nmat,data.eyeOffset); 2019 } 2020 else 2021 { 2022 getRenderMountTransform( 0.0f, data.mountPoint, MatrixF::Identity, &nmat ); 2023 mat->mul(nmat,data.mountTransform[shapeIndex]); 2024 } 2025 } 2026 else 2027 *mat = getRenderTransform(); 2028} 2029 2030void ShapeBase::getRenderImageTransform(U32 imageSlot,S32 node,MatrixF* mat) 2031{ 2032 // Image transform in world space 2033 MountedImage& image = mMountedImageList[imageSlot]; 2034 if (image.dataBlock) 2035 { 2036 if (node != -1) 2037 { 2038 ShapeBaseImageData& data = *image.dataBlock; 2039 U32 shapeIndex = getImageShapeIndex(image); 2040 2041 MatrixF nmat = image.shapeInstance[shapeIndex]->mNodeTransforms[node]; 2042 MatrixF mmat; 2043 2044 if ( data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1 ) 2045 { 2046 MatrixF emat; 2047 getRenderEyeBaseTransform(&emat, mDataBlock->mountedImagesBank); 2048 2049 MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]]; 2050 mountTransform.affineInverse(); 2051 2052 mmat.mul(emat, mountTransform); 2053 } 2054 else if ( data.useEyeOffset && isFirstPerson() ) 2055 { 2056 MatrixF emat; 2057 getRenderEyeTransform(&emat); 2058 mmat.mul(emat,data.eyeOffset); 2059 } 2060 else 2061 { 2062 MatrixF emat; 2063 getRenderMountTransform( 0.0f, data.mountPoint, MatrixF::Identity, &emat ); 2064 mmat.mul(emat,data.mountTransform[shapeIndex]); 2065 } 2066 2067 mat->mul(mmat, nmat); 2068 } 2069 else 2070 getRenderImageTransform(imageSlot,mat); 2071 } 2072 else 2073 *mat = getRenderTransform(); 2074} 2075 2076void ShapeBase::getRenderImageTransform(U32 imageSlot,StringTableEntry nodeName,MatrixF* mat) 2077{ 2078 getRenderImageTransform( imageSlot, getNodeIndex( imageSlot, nodeName ), mat ); 2079} 2080 2081void ShapeBase::getRenderMuzzleTransform(U32 imageSlot,MatrixF* mat) 2082{ 2083 // Muzzle transform in world space 2084 MountedImage& image = mMountedImageList[imageSlot]; 2085 if (image.dataBlock) 2086 getRenderImageTransform(imageSlot,image.dataBlock->muzzleNode[getImageShapeIndex(image)],mat); 2087 else 2088 *mat = getRenderTransform(); 2089} 2090 2091 2092void ShapeBase::getRetractionTransform(U32 imageSlot,MatrixF* mat) 2093{ 2094 // Muzzle transform in world space 2095 MountedImage& image = mMountedImageList[imageSlot]; 2096 if (image.dataBlock) { 2097 ShapeBaseImageData& data = *image.dataBlock; 2098 U32 imageShapeIndex = getImageShapeIndex(image); 2099 if (data.retractNode[imageShapeIndex] != -1) 2100 getImageTransform(imageSlot,data.retractNode[imageShapeIndex],mat); 2101 else 2102 getImageTransform(imageSlot,data.muzzleNode[imageShapeIndex],mat); 2103 } else { 2104 *mat = getTransform(); 2105 } 2106} 2107 2108 2109void ShapeBase::getRenderRetractionTransform(U32 imageSlot,MatrixF* mat) 2110{ 2111 // Muzzle transform in world space 2112 MountedImage& image = mMountedImageList[imageSlot]; 2113 if (image.dataBlock) { 2114 ShapeBaseImageData& data = *image.dataBlock; 2115 U32 imageShapeIndex = getImageShapeIndex(image); 2116 if (data.retractNode[imageShapeIndex] != -1) 2117 getRenderImageTransform(imageSlot,data.retractNode[imageShapeIndex],mat); 2118 else 2119 getRenderImageTransform(imageSlot,data.muzzleNode[imageShapeIndex],mat); 2120 } else { 2121 *mat = getRenderTransform(); 2122 } 2123} 2124 2125 2126//---------------------------------------------------------------------------- 2127 2128S32 ShapeBase::getNodeIndex(U32 imageSlot,StringTableEntry nodeName) 2129{ 2130 MountedImage& image = mMountedImageList[imageSlot]; 2131 if (image.dataBlock) 2132 return image.dataBlock->shape[getImageShapeIndex(image)]->findNode(nodeName); 2133 else 2134 return -1; 2135} 2136 2137// Modify muzzle if needed to aim at whatever is straight in front of the camera. Let the 2138// caller know if we actually modified the result. 2139bool ShapeBase::getCorrectedAim(const MatrixF& muzzleMat, VectorF* result) 2140{ 2141 F32 pullInD = sFullCorrectionDistance; 2142 const F32 maxAdjD = 500; 2143 2144 VectorF aheadVec(0, maxAdjD, 0); 2145 2146 MatrixF camMat; 2147 Point3F camPos; 2148 2149 F32 pos = 0; 2150 GameConnection * gc = getControllingClient(); 2151 if (gc && !gc->isFirstPerson()) 2152 pos = 1.0f; 2153 2154 getCameraTransform(&pos, &camMat); 2155 2156 camMat.getColumn(3, &camPos); 2157 camMat.mulV(aheadVec); 2158 Point3F aheadPoint = (camPos + aheadVec); 2159 2160 // Should we check if muzzle point is really close to camera? Does that happen? 2161 Point3F muzzlePos; 2162 muzzleMat.getColumn(3, &muzzlePos); 2163 2164 Point3F collidePoint; 2165 VectorF collideVector; 2166 disableCollision(); 2167 RayInfo rinfo; 2168 if (getContainer()->castRay(camPos, aheadPoint, STATIC_COLLISION_TYPEMASK</a>|<a href="/coding/file/objecttypes_8h/#objecttypes_8h_1a96ad067ae761cdb0978acf1983f7990da37d67fb4e997b0b39ee39023bdde80ab">DAMAGEABLE_TYPEMASK, &rinfo) && 2169 (mDot(rinfo.point - mObjToWorld.getPosition(), mObjToWorld.getForwardVector()) > 0)) // Check if point is behind us (could happen in 3rd person view) 2170 collideVector = ((collidePoint = rinfo.point) - camPos); 2171 else 2172 collideVector = ((collidePoint = aheadPoint) - camPos); 2173 enableCollision(); 2174 2175 // For close collision we want to NOT aim at ground since we're bending 2176 // the ray here as it is. But we don't want to pop, so adjust continuously. 2177 F32 lenSq = collideVector.lenSquared(); 2178 if (lenSq < (pullInD * pullInD) && lenSq > 0.04) 2179 { 2180 F32 len = mSqrt(lenSq); 2181 F32 mid = pullInD; // (pullInD + len) / 2.0; 2182 // This gives us point beyond to focus on- 2183 collideVector *= (mid / len); 2184 collidePoint = (camPos + collideVector); 2185 } 2186 2187 VectorF muzzleToCollide = (collidePoint - muzzlePos); 2188 lenSq = muzzleToCollide.lenSquared(); 2189 if (lenSq > 0.04) 2190 { 2191 muzzleToCollide *= (1 / mSqrt(lenSq)); 2192 * result = muzzleToCollide; 2193 return true; 2194 } 2195 return false; 2196} 2197 2198//---------------------------------------------------------------------------- 2199 2200void ShapeBase::updateMass() 2201{ 2202 if (mDataBlock) { 2203 F32 imass = 0; 2204 for (U32 i = 0; i < MaxMountedImages; i++) { 2205 MountedImage& image = mMountedImageList[i]; 2206 if (image.dataBlock) 2207 imass += image.dataBlock->mass; 2208 } 2209 // 2210 mMass = mDataBlock->mass + imass; 2211 mOneOverMass = 1 / mMass; 2212 } 2213} 2214 2215void ShapeBase::onImage(U32 imageSlot, bool unmount) 2216{ 2217} 2218 2219void ShapeBase::onImageRecoil(U32,ShapeBaseImageData::StateData::RecoilState) 2220{ 2221} 2222 2223void ShapeBase::onImageStateAnimation(U32 imageSlot, const char* seqName, bool direction, bool scaleToState, F32 stateTimeOutValue) 2224{ 2225} 2226 2227void ShapeBase::onImageAnimThreadChange(U32 imageSlot, S32 imageShapeIndex, ShapeBaseImageData::StateData* lastState, const char* anim, F32 pos, F32 timeScale, bool reset) 2228{ 2229} 2230 2231void ShapeBase::onImageAnimThreadUpdate(U32 imageSlot, S32 imageShapeIndex, F32 dt) 2232{ 2233} 2234 2235 2236//---------------------------------------------------------------------------- 2237 2238void ShapeBase::setImage( U32 imageSlot, 2239 ShapeBaseImageData* imageData, 2240 NetStringHandle& skinNameHandle, 2241 bool loaded, 2242 bool ammo, 2243 bool triggerDown, 2244 bool altTriggerDown, 2245 bool motion, 2246 bool genericTrigger0, 2247 bool genericTrigger1, 2248 bool genericTrigger2, 2249 bool genericTrigger3, 2250 bool target) 2251{ 2252 AssertFatal(imageSlot<MaxMountedImages,"Out of range image slot"); 2253 2254 MountedImage& image = mMountedImageList[imageSlot]; 2255 2256 // If we already have this datablock... 2257 if (image.dataBlock == imageData) { 2258 // Mark that there is not a datablock change pending. 2259 image.nextImage = InvalidImagePtr; 2260 // Change the skin handle if necessary. 2261 if (image.skinNameHandle != skinNameHandle) { 2262 if (!isGhost()) { 2263 // Serverside, note the skin handle and tell the client. 2264 image.skinNameHandle = skinNameHandle; 2265 setMaskBits(ImageMaskN << imageSlot); 2266 } 2267 else { 2268 // Clientside, do the reskin. 2269 image.skinNameHandle = skinNameHandle; 2270 for( U32 i=0; i<ShapeBaseImageData::MaxShapes; ++i) 2271 { 2272 if (image.shapeInstance[i]) 2273 { 2274 String newSkin = skinNameHandle.getString(); 2275 image.shapeInstance[i]->reSkin(newSkin, image.appliedSkinName); 2276 image.appliedSkinName = newSkin; 2277 } 2278 } 2279 } 2280 } 2281 return; 2282 } 2283 2284 // Check to see if we need to delay image changes until state change. 2285 if (!isGhost()) { 2286 if (imageData && image.dataBlock && !image.state->allowImageChange) { 2287 image.nextImage = imageData; 2288 image.nextSkinNameHandle = skinNameHandle; 2289 image.nextLoaded = loaded; 2290 return; 2291 } 2292 } 2293 2294 // Mark that updates are happenin'. 2295 setMaskBits(ImageMaskN << imageSlot); 2296 2297 // Notify script unmount since we're swapping datablocks. 2298 if (image.dataBlock && !isGhost()) { 2299 F32 dt = image.dataBlock->useRemainderDT ? image.rDT : 0.0f; 2300 image.dataBlock->onUnmount_callback( this, imageSlot, dt ); 2301 } 2302 2303 // Stop anything currently going on with the image. 2304 resetImageSlot(imageSlot); 2305 2306 // If we're just unselecting the current shape without swapping 2307 // in a new one, then bail. 2308 if (!imageData) { 2309 onImage( imageSlot, true); 2310 return; 2311 } 2312 2313 // Otherwise, init the new shape. 2314 image.dataBlock = imageData; 2315 image.state = &image.dataBlock->state[0]; 2316 image.skinNameHandle = skinNameHandle; 2317 image.updateDoAnimateAllShapes(this); 2318 2319 for (U32 i=0; i<ShapeBaseImageData::MaxShapes; ++i) 2320 { 2321 if (image.dataBlock->shapeIsValid[i]) 2322 image.shapeInstance[i] = new TSShapeInstance(image.dataBlock->shape[i], isClientObject()); 2323 } 2324 2325 if (isClientObject()) 2326 { 2327 for (U32 i=0; i<ShapeBaseImageData::MaxShapes; ++i) 2328 { 2329 if (image.shapeInstance[i]) 2330 { 2331 image.shapeInstance[i]->cloneMaterialList(); 2332 String newSkin = skinNameHandle.getString(); 2333 image.shapeInstance[i]->reSkin(newSkin, image.appliedSkinName); 2334 image.appliedSkinName = newSkin; 2335 } 2336 } 2337 } 2338 image.loaded = loaded; 2339 image.ammo = ammo; 2340 image.triggerDown = triggerDown; 2341 image.altTriggerDown = altTriggerDown; 2342 image.target = target; 2343 image.motion = motion; 2344 image.genericTrigger[0] = genericTrigger0; 2345 image.genericTrigger[1] = genericTrigger1; 2346 image.genericTrigger[2] = genericTrigger2; 2347 image.genericTrigger[3] = genericTrigger3; 2348 2349 // The server needs the shape loaded for muzzle mount nodes 2350 // but it doesn't need to run any of the animations, unless the image 2351 // has animateOnServer set. Then the server needs to animate as well. 2352 // This is often set when using useEyeNode. 2353 for (U32 i=0; i<ShapeBaseImageData::MaxShapes; ++i) 2354 { 2355 image.ambientThread[i] = 0; 2356 image.animThread[i] = 0; 2357 image.flashThread[i] = 0; 2358 image.spinThread[i] = 0; 2359 } 2360 2361 if (imageData->animateOnServer || isGhost()) 2362 { 2363 for (U32 i=0; i<ShapeBaseImageData::MaxShapes; ++i) 2364 { 2365 if (!image.shapeInstance[i]) 2366 continue; 2367 2368 if (image.dataBlock->isAnimated[i]) { 2369 image.animThread[i] = image.shapeInstance[i]->addThread(); 2370 image.shapeInstance[i]->setTimeScale(image.animThread[i],0); 2371 } 2372 if (image.dataBlock->hasFlash[i]) { 2373 image.flashThread[i] = image.shapeInstance[i]->addThread(); 2374 image.shapeInstance[i]->setTimeScale(image.flashThread[i],0); 2375 } 2376 if (image.dataBlock->ambientSequence[i] != -1) { 2377 image.ambientThread[i] = image.shapeInstance[i]->addThread(); 2378 image.shapeInstance[i]->setTimeScale(image.ambientThread[i],1); 2379 image.shapeInstance[i]->setSequence(image.ambientThread[i], 2380 image.dataBlock->ambientSequence[i],0); 2381 } 2382 if (image.dataBlock->spinSequence[i] != -1) { 2383 image.spinThread[i] = image.shapeInstance[i]->addThread(); 2384 image.shapeInstance[i]->setTimeScale(image.spinThread[i],1); 2385 image.shapeInstance[i]->setSequence(image.spinThread[i], 2386 image.dataBlock->spinSequence[i],0); 2387 } 2388 } 2389 } 2390 2391 // Set the image to its starting state. 2392 setImageState(imageSlot, (U32)0, true); 2393 2394 // Update the mass for the mount object. 2395 updateMass(); 2396 2397 // Notify script mount. 2398 if ( !isGhost() ) 2399 { 2400 F32 dt = image.dataBlock->useRemainderDT ? image.rDT : 0.0f; 2401 image.dataBlock->onMount_callback( this, imageSlot, dt ); 2402 } 2403 else 2404 { 2405 if ( imageData->lightType == ShapeBaseImageData::PulsingLight ) 2406 image.lightStart = Sim::getCurrentTime(); 2407 } 2408 2409 onImage(imageSlot, false); 2410 2411 // Done. 2412} 2413 2414 2415//---------------------------------------------------------------------------- 2416 2417void ShapeBase::resetImageSlot(U32 imageSlot) 2418{ 2419 AssertFatal(imageSlot<MaxMountedImages,"Out of range image slot"); 2420 2421 // Clear out current image 2422 MountedImage& image = mMountedImageList[imageSlot]; 2423 for (U32 i=0; i<ShapeBaseImageData::MaxShapes; ++i) 2424 { 2425 delete image.shapeInstance[i]; 2426 image.shapeInstance[i] = 0; 2427 } 2428 2429 // stop sound 2430 for(Vector<SFXSource*>::iterator i = image.mSoundSources.begin(); i != image.mSoundSources.end(); i++) 2431 { 2432 SFX_DELETE((*i)); 2433 } 2434 image.mSoundSources.clear(); 2435 2436 for (S32 i = 0; i < MaxImageEmitters; i++) { 2437 MountedImage::ImageEmitter& em = image.emitter[i]; 2438 if (bool(em.emitter)) { 2439 em.emitter->deleteWhenEmpty(); 2440 em.emitter = 0; 2441 } 2442 } 2443 2444 image.dataBlock = 0; 2445 image.nextImage = InvalidImagePtr; 2446 image.skinNameHandle = NetStringHandle(); 2447 image.nextSkinNameHandle = NetStringHandle(); 2448 image.state = 0; 2449 image.delayTime = 0; 2450 image.rDT = 0; 2451 image.ammo = false; 2452 image.triggerDown = false; 2453 image.altTriggerDown = false; 2454 image.loaded = false; 2455 image.motion = false; 2456 2457 for (U32 i=0; i<ShapeBaseImageData::MaxGenericTriggers; ++i) 2458 { 2459 image.genericTrigger[i] = false; 2460 } 2461 2462 image.lightStart = 0; 2463 if ( image.lightInfo != NULL ) 2464 SAFE_DELETE( image.lightInfo ); 2465 2466 updateMass(); 2467} 2468 2469 2470//---------------------------------------------------------------------------- 2471 2472bool ShapeBase::getImageTriggerState(U32 imageSlot) 2473{ 2474 if (isGhost() || !mMountedImageList[imageSlot].dataBlock) 2475 return false; 2476 return mMountedImageList[imageSlot].triggerDown; 2477} 2478 2479void ShapeBase::setImageTriggerState(U32 imageSlot,bool trigger) 2480{ 2481 if (isGhost() || !mMountedImageList[imageSlot].dataBlock) 2482 return; 2483 MountedImage& image = mMountedImageList[imageSlot]; 2484 2485 if (trigger) { 2486 if (!image.triggerDown && image.dataBlock) { 2487 image.triggerDown = true; 2488 if (!isGhost()) { 2489 setMaskBits(ImageMaskN << imageSlot); 2490 updateImageState(imageSlot,0); 2491 } 2492 } 2493 } 2494 else 2495 if (image.triggerDown) { 2496 image.triggerDown = false; 2497 if (!isGhost()) { 2498 setMaskBits(ImageMaskN << imageSlot); 2499 updateImageState(imageSlot,0); 2500 } 2501 } 2502} 2503 2504bool ShapeBase::getImageAltTriggerState(U32 imageSlot) 2505{ 2506 if (isGhost() || !mMountedImageList[imageSlot].dataBlock) 2507 return false; 2508 return mMountedImageList[imageSlot].altTriggerDown; 2509} 2510 2511void ShapeBase::setImageAltTriggerState(U32 imageSlot,bool trigger) 2512{ 2513 if (isGhost() || !mMountedImageList[imageSlot].dataBlock) 2514 return; 2515 MountedImage& image = mMountedImageList[imageSlot]; 2516 2517 if (trigger) { 2518 if (!image.altTriggerDown && image.dataBlock) { 2519 image.altTriggerDown = true; 2520 if (!isGhost()) { 2521 setMaskBits(ImageMaskN << imageSlot); 2522 updateImageState(imageSlot,0); 2523 } 2524 } 2525 } 2526 else 2527 if (image.altTriggerDown) { 2528 image.altTriggerDown = false; 2529 if (!isGhost()) { 2530 setMaskBits(ImageMaskN << imageSlot); 2531 updateImageState(imageSlot,0); 2532 } 2533 } 2534} 2535 2536//---------------------------------------------------------------------------- 2537 2538U32 ShapeBase::getImageFireState(U32 imageSlot) 2539{ 2540 MountedImage& image = mMountedImageList[imageSlot]; 2541 // If there is no fire state, then try state 0 2542 if (image.dataBlock && image.dataBlock->fireState != -1) 2543 return image.dataBlock->fireState; 2544 return 0; 2545} 2546 2547U32 ShapeBase::getImageAltFireState(U32 imageSlot) 2548{ 2549 MountedImage& image = mMountedImageList[imageSlot]; 2550 // If there is no alternate fire state, then try state 0 2551 if (image.dataBlock && image.dataBlock->altFireState != -1) 2552 return image.dataBlock->altFireState; 2553 return 0; 2554} 2555 2556U32 ShapeBase::getImageReloadState(U32 imageSlot) 2557{ 2558 MountedImage& image = mMountedImageList[imageSlot]; 2559 // If there is no reload state, then try state 0 2560 if (image.dataBlock && image.dataBlock->reloadState != -1) 2561 return image.dataBlock->reloadState; 2562 return 0; 2563} 2564 2565 2566//---------------------------------------------------------------------------- 2567 2568bool ShapeBase::hasImageState(U32 imageSlot, const char* state) 2569{ 2570 if (!state || !state[0]) 2571 return false; 2572 2573 MountedImage& image = mMountedImageList[imageSlot]; 2574 if (image.dataBlock) 2575 { 2576 for (U32 i = 0; i < ShapeBaseImageData::MaxStates; i++) 2577 { 2578 ShapeBaseImageData::StateData& sd = image.dataBlock->state[i]; 2579 if (sd.name && !dStricmp(state, sd.name)) 2580 return true; 2581 } 2582 } 2583 2584 return false; 2585} 2586 2587void ShapeBase::setImageState(U32 imageSlot, U32 newState,bool force) 2588{ 2589 if (!mMountedImageList[imageSlot].dataBlock) 2590 return; 2591 MountedImage& image = mMountedImageList[imageSlot]; 2592 2593 2594 // The client never enters the initial fire state on its own, but it 2595 // will continue to set that state... 2596 if (isGhost() && !force && newState == image.dataBlock->fireState) { 2597 if (image.state != &image.dataBlock->state[newState]) 2598 return; 2599 } 2600 2601 // The client never enters the initial alternate fire state on its own, but it 2602 // will continue to set that state... 2603 if (isGhost() && !force && newState == image.dataBlock->altFireState) { 2604 if (image.state != &image.dataBlock->state[newState]) 2605 return; 2606 } 2607 2608 // The client never enters the initial reload state on its own, but it 2609 // will continue to set that state... 2610 if (isGhost() && !force && newState == image.dataBlock->reloadState) { 2611 if (image.state != &image.dataBlock->state[newState]) 2612 return; 2613 } 2614 2615 // Eject shell casing on every state change (client side only) 2616 ShapeBaseImageData::StateData& nextStateData = image.dataBlock->state[newState]; 2617 if (isGhost() && nextStateData.ejectShell) { 2618 ejectShellCasing( imageSlot ); 2619 } 2620 2621 // Shake camera on client. 2622 if (isGhost() && nextStateData.fire && image.dataBlock->shakeCamera) { 2623 shakeCamera( imageSlot ); 2624 } 2625 2626 // Server must animate the shape if it is a firestate... 2627 if (isServerObject() && (image.dataBlock->state[newState].fire || image.dataBlock->state[newState].altFire)) 2628 mShapeInstance->animate(); 2629 2630 // Obtain the image's shape index for future use. 2631 U32 imageShapeIndex = getImageShapeIndex(image); 2632 image.lastShapeIndex = imageShapeIndex; 2633 2634 // If going back into the same state, just reset the timer 2635 // and invoke the script callback 2636 if (!force && image.state == &image.dataBlock->state[newState]) { 2637 image.delayTime = image.state->timeoutValue; 2638 if (image.state->script && !isGhost()) 2639 scriptCallback(imageSlot,image.state->script); 2640 2641 // If this is a flash sequence, we need to select a new position for the 2642 // animation if we're returning to that state... 2643 F32 randomPos = Platform::getRandom(); 2644 for (U32 i=0; i<ShapeBaseImageData::MaxShapes; ++i) 2645 { 2646 if (!image.dataBlock->shapeIsValid[i] || (i != imageShapeIndex && !image.doAnimateAllShapes)) 2647 continue; 2648 2649 if (image.animThread[i] && image.state->sequence[i] != -1 && image.state->flashSequence[i]) { 2650 image.shapeInstance[i]->setPos(image.animThread[i], randomPos); 2651 image.shapeInstance[i]->setTimeScale(image.animThread[i], 0); 2652 if (image.flashThread[i]) 2653 image.shapeInstance[i]->setPos(image.flashThread[i], 0); 2654 } 2655 } 2656 2657 return; 2658 } 2659 2660 F32 lastDelay = image.delayTime; 2661 ShapeBaseImageData::StateData* lastState = image.state; 2662 image.state = &image.dataBlock->state[newState]; 2663 2664 // 2665 // Do state cleanup first... 2666 // 2667 ShapeBaseImageData::StateData& stateData = *image.state; 2668 image.delayTime = stateData.timeoutValue; 2669 2670 // Mount pending images 2671 if (image.nextImage != InvalidImagePtr && stateData.allowImageChange) { 2672 setImage(imageSlot,image.nextImage,image.nextSkinNameHandle,image.nextLoaded); 2673 return; 2674 } 2675 2676 // Reset cyclic sequences back to the first frame to turn it off 2677 // (the first key frame should be it's off state). 2678 // We need to do this across all image shapes to make sure we have no hold overs when switching 2679 // rendering shapes while in the middle of a state change. 2680 for (U32 i=0; i<ShapeBaseImageData::MaxShapes; ++i) 2681 { 2682 // If we are to do a sequence transition then we need to keep the previous animThread active 2683 if (image.animThread[i] && image.animThread[i]->getSequence()->isCyclic() && (stateData.sequenceNeverTransition || !(stateData.sequenceTransitionIn || lastState->sequenceTransitionOut))) { 2684 image.shapeInstance[i]->setPos(image.animThread[i],0); 2685 image.shapeInstance[i]->setTimeScale(image.animThread[i],0); 2686 } 2687 if (image.flashThread[i]) { 2688 image.shapeInstance[i]->setPos(image.flashThread[i],0); 2689 image.shapeInstance[i]->setTimeScale(image.flashThread[i],0); 2690 } 2691 } 2692 2693 // Broadcast the reset 2694 onImageAnimThreadChange(imageSlot, imageShapeIndex, lastState, NULL, 0, 0, true); 2695 2696 // Check for immediate transitions, but only if we don't need to wait for 2697 // a time out. Only perform this wait if we're not forced to change. 2698 S32 ns; 2699 if (image.delayTime <= 0 || !stateData.waitForTimeout) 2700 { 2701 if ((ns = stateData.transition.loaded[image.loaded]) != -1) { 2702 setImageState(imageSlot,ns); 2703 return; 2704 } 2705 for (U32 i=0; i<ShapeBaseImageData::MaxGenericTriggers; ++i) 2706 { 2707 if ((ns = stateData.transition.genericTrigger[i][image.genericTrigger[i]]) != -1) { 2708 setImageState(imageSlot, ns); 2709 return; 2710 } 2711 } 2712 //if (!imageData.usesEnergy) 2713 if ((ns = stateData.transition.ammo[image.ammo]) != -1) { 2714 setImageState(imageSlot,ns); 2715 return; 2716 } 2717 if ((ns = stateData.transition.target[image.target]) != -1) { 2718 setImageState(imageSlot, ns); 2719 return; 2720 } 2721 if ((ns = stateData.transition.wet[image.wet]) != -1) { 2722 setImageState(imageSlot, ns); 2723 return; 2724 } 2725 if ((ns = stateData.transition.motion[image.motion]) != -1) { 2726 setImageState(imageSlot, ns); 2727 return; 2728 } 2729 if ((ns = stateData.transition.trigger[image.triggerDown]) != -1) { 2730 setImageState(imageSlot,ns); 2731 return; 2732 } 2733 if ((ns = stateData.transition.altTrigger[image.altTriggerDown]) != -1) { 2734 setImageState(imageSlot,ns); 2735 return; 2736 } 2737 } 2738 2739 // 2740 // Initialize the new state... 2741 // 2742 if (stateData.loaded != ShapeBaseImageData::StateData::IgnoreLoaded) 2743 image.loaded = stateData.loaded == ShapeBaseImageData::StateData::Loaded; 2744 if (!isGhost() && image.dataBlock->state[newState].fire) { 2745 setMaskBits(ImageMaskN << imageSlot); 2746 image.fireCount = (image.fireCount + 1) & 0x7; 2747 } 2748 if (!isGhost() && image.dataBlock->state[newState].altFire) { 2749 setMaskBits(ImageMaskN << imageSlot); 2750 image.altFireCount = (image.altFireCount + 1) & 0x7; 2751 } 2752 if (!isGhost() && image.dataBlock->state[newState].reload) { 2753 setMaskBits(ImageMaskN << imageSlot); 2754 image.reloadCount = (image.reloadCount + 1) & 0x7; 2755 } 2756 2757 // Apply recoil 2758 if (stateData.recoil != ShapeBaseImageData::StateData::NoRecoil) 2759 onImageRecoil(imageSlot,stateData.recoil); 2760 2761 // Apply image state animation on mounting shape 2762 if (stateData.shapeSequence && stateData.shapeSequence[0]) 2763 { 2764 onImageStateAnimation(imageSlot, stateData.shapeSequence, stateData.direction, stateData.shapeSequenceScale, stateData.timeoutValue); 2765 } 2766 2767 // Delete any loooping sounds that were in the previous state. 2768 if (lastState->sound && lastState->sound->getDescription()->mIsLooping) 2769 { 2770 for(Vector<SFXSource*>::iterator i = image.mSoundSources.begin(); i != image.mSoundSources.end(); i++) 2771 SFX_DELETE((*i)); 2772 2773 image.mSoundSources.clear(); 2774 } 2775 2776 // Play sound 2777 if( stateData.sound && isGhost() ) 2778 { 2779 const Point3F& velocity = getVelocity(); 2780 image.addSoundSource(SFX->createSource( stateData.sound, &getRenderTransform(), &velocity )); 2781 } 2782 2783 // Play animation 2784 updateAnimThread(imageSlot, imageShapeIndex, lastState); 2785 for (U32 i=0; i<ShapeBaseImageData::MaxShapes; ++i) 2786 { 2787 if (!image.dataBlock->shapeIsValid[i] || (i != imageShapeIndex && !image.doAnimateAllShapes)) 2788 continue; 2789 2790 // Start spin thread 2791 if (image.spinThread[i]) { 2792 switch (stateData.spin) { 2793 case ShapeBaseImageData::StateData::IgnoreSpin: 2794 image.shapeInstance[i]->setTimeScale(image.spinThread[i], image.shapeInstance[i]->getTimeScale(image.spinThread[i])); 2795 break; 2796 case ShapeBaseImageData::StateData::NoSpin: 2797 image.shapeInstance[i]->setTimeScale(image.spinThread[i],0); 2798 break; 2799 case ShapeBaseImageData::StateData::SpinUp: 2800 if (lastState->spin == ShapeBaseImageData::StateData::SpinDown) 2801 image.delayTime *= 1.0f - (lastDelay / stateData.timeoutValue); 2802 break; 2803 case ShapeBaseImageData::StateData::SpinDown: 2804 if (lastState->spin == ShapeBaseImageData::StateData::SpinUp) 2805 image.delayTime *= 1.0f - (lastDelay / stateData.timeoutValue); 2806 break; 2807 case ShapeBaseImageData::StateData::FullSpin: 2808 image.shapeInstance[i]->setTimeScale(image.spinThread[i],1); 2809 break; 2810 } 2811 } 2812 } 2813 2814 // Start particle emitter on the client (client side only) 2815 if (isGhost() && stateData.emitter) 2816 startImageEmitter(image,stateData); 2817 2818 // Script callback on server 2819 if (stateData.script && stateData.script[0] && !isGhost()) 2820 scriptCallback(imageSlot,stateData.script); 2821 2822 // If there is a zero timeout, and a timeout transition, then 2823 // go ahead and transition imediately. 2824 if (!image.delayTime) 2825 { 2826 if ((ns = stateData.transition.timeout) != -1) 2827 { 2828 setImageState(imageSlot,ns); 2829 return; 2830 } 2831 } 2832} 2833 2834void ShapeBase::updateAnimThread(U32 imageSlot, S32 imageShapeIndex, ShapeBaseImageData::StateData* lastState) 2835{ 2836 MountedImage& image = mMountedImageList[imageSlot]; 2837 ShapeBaseImageData::StateData& stateData = *image.state; 2838 2839 F32 randomPos = Platform::getRandom(); 2840 for (U32 i=0; i<ShapeBaseImageData::MaxShapes; ++i) 2841 { 2842 if (!image.dataBlock->shapeIsValid[i] || (i != imageShapeIndex && !image.doAnimateAllShapes)) 2843 continue; 2844 2845 if (image.animThread[i] && stateData.sequence[i] != -1) 2846 { 2847 S32 seqIndex = stateData.sequence[i]; // Standard index without any prefix 2848 bool scaleAnim = stateData.scaleAnimation; 2849 if (i == ShapeBaseImageData::FirstPersonImageShape) 2850 scaleAnim = stateData.scaleAnimationFP; 2851 2852 // We're going to apply various prefixes to determine the final sequence to use. 2853 // Here is the order: 2854 // shapeBasePrefix_scriptPrefix_baseAnimName 2855 // shapeBasePrefix_baseAnimName 2856 // scriptPrefix_baseAnimName 2857 // baseAnimName 2858 2859 // Collect the prefixes 2860 const char* shapeBasePrefix = getImageAnimPrefix(imageSlot, i); 2861 bool hasShapeBasePrefix = shapeBasePrefix && shapeBasePrefix[0]; 2862 const char* scriptPrefix = getImageScriptAnimPrefix(imageSlot).getString(); 2863 bool hasScriptPrefix = scriptPrefix && scriptPrefix[0]; 2864 2865 // Find the final sequence based on the prefix combinations 2866 if (hasShapeBasePrefix || hasScriptPrefix) 2867 { 2868 bool found = false; 2869 String baseSeqName(image.shapeInstance[i]->getShape()->getSequenceName(stateData.sequence[i])); 2870 2871 if (!found && hasShapeBasePrefix && hasScriptPrefix) 2872 { 2873 String seqName = String(shapeBasePrefix) + String("_") + String(scriptPrefix) + String("_") + baseSeqName; 2874 S32 index = image.shapeInstance[i]->getShape()->findSequence(seqName); 2875 if (index != -1) 2876 { 2877 seqIndex = index; 2878 found = true; 2879 } 2880 } 2881 2882 if (!found && hasShapeBasePrefix) 2883 { 2884 String seqName = String(shapeBasePrefix) + String("_") + baseSeqName; 2885 S32 index = image.shapeInstance[i]->getShape()->findSequence(seqName); 2886 if (index != -1) 2887 { 2888 seqIndex = index; 2889 found = true; 2890 } 2891 } 2892 2893 if (!found && hasScriptPrefix) 2894 { 2895 String seqName = String(scriptPrefix) + String("_") + baseSeqName; 2896 S32 index = image.shapeInstance[i]->getShape()->findSequence(seqName); 2897 if (index != -1) 2898 { 2899 seqIndex = index; 2900 found = true; 2901 } 2902 } 2903 } 2904 2905 if (seqIndex != -1) 2906 { 2907 if (!lastState) 2908 { 2909 // No lastState indicates that we are just switching animation sequences, not states. Transition into this new sequence, but only 2910 // if it is different than what we're currently playing. 2911 S32 prevSeq = -1; 2912 if (image.animThread[i]->hasSequence()) 2913 { 2914 prevSeq = image.shapeInstance[i]->getSequence(image.animThread[i]); 2915 } 2916 if (seqIndex != prevSeq) 2917 { 2918 image.shapeInstance[i]->transitionToSequence(image.animThread[i], seqIndex, stateData.direction ? 0.0f : 1.0f, image.dataBlock->scriptAnimTransitionTime, true); 2919 } 2920 } 2921 else if (!stateData.sequenceNeverTransition && stateData.sequenceTransitionTime && (stateData.sequenceTransitionIn || lastState->sequenceTransitionOut)) 2922 { 2923 image.shapeInstance[i]->transitionToSequence(image.animThread[i], seqIndex, stateData.direction ? 0.0f : 1.0f, stateData.sequenceTransitionTime, true); 2924 } 2925 else 2926 { 2927 image.shapeInstance[i]->setSequence(image.animThread[i], seqIndex, stateData.direction ? 0.0f : 1.0f); 2928 } 2929 2930 if (stateData.flashSequence[i] == false) 2931 { 2932 F32 timeScale = (scaleAnim && stateData.timeoutValue) ? 2933 image.shapeInstance[i]->getDuration(image.animThread[i]) / stateData.timeoutValue : 1.0f; 2934 image.shapeInstance[i]->setTimeScale(image.animThread[i], stateData.direction ? timeScale : -timeScale); 2935 2936 // Broadcast the sequence change 2937 String seqName = image.shapeInstance[i]->getShape()->getSequenceName(stateData.sequence[i]); 2938 onImageAnimThreadChange(imageSlot, imageShapeIndex, lastState, seqName, stateData.direction ? 0.0f : 1.0f, stateData.direction ? timeScale : -timeScale); 2939 } 2940 else 2941 { 2942 image.shapeInstance[i]->setPos(image.animThread[i], randomPos); 2943 image.shapeInstance[i]->setTimeScale(image.animThread[i], 0); 2944 2945 S32 seqVisIndex = stateData.sequenceVis[i]; 2946 2947 // Go through the same process as the animThread sequence to find the flashThread sequence 2948 if (hasShapeBasePrefix || hasScriptPrefix) 2949 { 2950 bool found = false; 2951 String baseVisSeqName(image.shapeInstance[i]->getShape()->getSequenceName(stateData.sequenceVis[i])); 2952 2953 if (!found && hasShapeBasePrefix && hasScriptPrefix) 2954 { 2955 String seqName = String(shapeBasePrefix) + String("_") + String(scriptPrefix) + String("_") + baseVisSeqName; 2956 S32 index = image.shapeInstance[i]->getShape()->findSequence(seqName); 2957 if (index != -1) 2958 { 2959 seqVisIndex = index; 2960 found = true; 2961 } 2962 } 2963 2964 if (!found && hasShapeBasePrefix) 2965 { 2966 String seqName = String(shapeBasePrefix) + String("_") + baseVisSeqName; 2967 S32 index = image.shapeInstance[i]->getShape()->findSequence(seqName); 2968 if (index != -1) 2969 { 2970 seqVisIndex = index; 2971 found = true; 2972 } 2973 } 2974 2975 if (!found && hasScriptPrefix) 2976 { 2977 String seqName = String(scriptPrefix) + String("_") + baseVisSeqName; 2978 S32 index = image.shapeInstance[i]->getShape()->findSequence(seqName); 2979 if (index != -1) 2980 { 2981 seqVisIndex = index; 2982 found = true; 2983 } 2984 } 2985 } 2986 2987 image.shapeInstance[i]->setSequence(image.flashThread[i], seqVisIndex, 0); 2988 image.shapeInstance[i]->setPos(image.flashThread[i], 0); 2989 F32 timeScale = (scaleAnim && stateData.timeoutValue) ? 2990 image.shapeInstance[i]->getDuration(image.flashThread[i]) / stateData.timeoutValue : 1.0f; 2991 image.shapeInstance[i]->setTimeScale(image.flashThread[i], timeScale); 2992 2993 // Broadcast the sequence change 2994 String seqName = image.shapeInstance[i]->getShape()->getSequenceName(stateData.sequenceVis[i]); 2995 onImageAnimThreadChange(imageSlot, imageShapeIndex, lastState, seqName, stateData.direction ? 0.0f : 1.0f, stateData.direction ? timeScale : -timeScale); 2996 } 2997 } 2998 } 2999 } 3000} 3001 3002 3003//---------------------------------------------------------------------------- 3004 3005void ShapeBase::updateImageState(U32 imageSlot,F32 dt) 3006{ 3007 if (!mMountedImageList[imageSlot].dataBlock) 3008 return; 3009 MountedImage& image = mMountedImageList[imageSlot]; 3010 ShapeBaseImageData& imageData = *image.dataBlock; 3011 3012 image.rDT = dt; 3013 F32 elapsed; 3014 3015TICKAGAIN: 3016 3017 ShapeBaseImageData::StateData& stateData = *image.state; 3018 3019 if ( image.delayTime > dt ) 3020 elapsed = dt; 3021 else 3022 elapsed = image.delayTime; 3023 3024 dt = elapsed; 3025 image.rDT -= elapsed; 3026 3027 image.delayTime -= dt; 3028 3029 // Energy management 3030 if (imageData.usesEnergy) 3031 { 3032 F32 newEnergy = getEnergyLevel() - stateData.energyDrain * dt; 3033 if (newEnergy < 0) 3034 newEnergy = 0; 3035 setEnergyLevel(newEnergy); 3036 3037 if (!isGhost()) 3038 { 3039 bool ammo = newEnergy > imageData.minEnergy; 3040 if (ammo != image.ammo) 3041 { 3042 setMaskBits(ImageMaskN << imageSlot); 3043 image.ammo = ammo; 3044 } 3045 } 3046 } 3047 3048 // Check for transitions. On some states we must wait for the 3049 // full timeout value before moving on. 3050 if (image.delayTime <= 0 || !stateData.waitForTimeout) 3051 { 3052 S32 ns; 3053 3054 if ((ns = stateData.transition.loaded[image.loaded]) != -1) 3055 setImageState(imageSlot,ns); 3056 else if ((ns = stateData.transition.genericTrigger[0][image.genericTrigger[0]]) != -1) 3057 setImageState(imageSlot,ns); 3058 else if ((ns = stateData.transition.genericTrigger[1][image.genericTrigger[1]]) != -1) 3059 setImageState(imageSlot,ns); 3060 else if ((ns = stateData.transition.genericTrigger[2][image.genericTrigger[2]]) != -1) 3061 setImageState(imageSlot,ns); 3062 else if ((ns = stateData.transition.genericTrigger[3][image.genericTrigger[3]]) != -1) 3063 setImageState(imageSlot,ns); 3064 else if ((ns = stateData.transition.ammo[image.ammo]) != -1) 3065 setImageState(imageSlot,ns); 3066 else if ((ns = stateData.transition.target[image.target]) != -1) 3067 setImageState(imageSlot,ns); 3068 else if ((ns = stateData.transition.wet[image.wet]) != -1) 3069 setImageState(imageSlot,ns); 3070 else if ((ns = stateData.transition.motion[image.motion]) != -1) 3071 setImageState(imageSlot,ns); 3072 else if ((ns = stateData.transition.trigger[image.triggerDown]) != -1) 3073 setImageState(imageSlot,ns); 3074 else if ((ns = stateData.transition.altTrigger[image.altTriggerDown]) != -1) 3075 setImageState(imageSlot,ns); 3076 else if (image.delayTime <= 0 && (ns = stateData.transition.timeout) != -1) 3077 setImageState(imageSlot,ns); 3078 } 3079 3080 // Update the spinning thread timeScale 3081 U32 imageShapeIndex = getImageShapeIndex(image); 3082 for (U32 i=0; i<ShapeBaseImageData::MaxShapes; ++i) 3083 { 3084 if (!image.dataBlock->shapeIsValid[i] || (i != imageShapeIndex && !image.doAnimateAllShapes)) 3085 continue; 3086 3087 if (image.spinThread[i]) 3088 { 3089 F32 timeScale; 3090 3091 switch (stateData.spin) 3092 { 3093 case ShapeBaseImageData::StateData::IgnoreSpin: 3094 case ShapeBaseImageData::StateData::NoSpin: 3095 case ShapeBaseImageData::StateData::FullSpin: 3096 { 3097 timeScale = 0; 3098 image.shapeInstance[i]->setTimeScale(image.spinThread[i], image.shapeInstance[i]->getTimeScale(image.spinThread[i])); 3099 break; 3100 } 3101 3102 case ShapeBaseImageData::StateData::SpinUp: 3103 { 3104 timeScale = 1.0f - image.delayTime / stateData.timeoutValue; 3105 image.shapeInstance[i]->setTimeScale(image.spinThread[i],timeScale); 3106 break; 3107 } 3108 3109 case ShapeBaseImageData::StateData::SpinDown: 3110 { 3111 timeScale = image.delayTime / stateData.timeoutValue; 3112 image.shapeInstance[i]->setTimeScale(image.spinThread[i],timeScale); 3113 break; 3114 } 3115 } 3116 } 3117 } 3118 3119 if ( image.rDT > 0.0f && image.delayTime > 0.0f && imageData.useRemainderDT && dt != 0.0f ) 3120 { 3121 dt = image.rDT; 3122 goto TICKAGAIN; 3123 } 3124} 3125 3126 3127//---------------------------------------------------------------------------- 3128 3129void ShapeBase::updateImageAnimation(U32 imageSlot, F32 dt) 3130{ 3131 if (!mMountedImageList[imageSlot].dataBlock) 3132 return; 3133 MountedImage& image = mMountedImageList[imageSlot]; 3134 U32 imageShapeIndex = getImageShapeIndex(image); 3135 3136 // Advance animation threads 3137 for (U32 i=0; i<ShapeBaseImageData::MaxShapes; ++i) 3138 { 3139 if (!image.dataBlock->shapeIsValid[i] || (i != imageShapeIndex && !image.doAnimateAllShapes)) 3140 continue; 3141 3142 if (image.ambientThread[i]) 3143 image.shapeInstance[i]->advanceTime(dt,image.ambientThread[i]); 3144 if (image.animThread[i]) 3145 image.shapeInstance[i]->advanceTime(dt,image.animThread[i]); 3146 if (image.spinThread[i]) 3147 image.shapeInstance[i]->advanceTime(dt,image.spinThread[i]); 3148 if (image.flashThread[i]) 3149 image.shapeInstance[i]->advanceTime(dt,image.flashThread[i]); 3150 } 3151 3152 // Broadcast the update 3153 onImageAnimThreadUpdate(imageSlot, imageShapeIndex, dt); 3154 3155 image.updateSoundSources(getRenderTransform()); 3156 3157 // Particle emission 3158 for (S32 i = 0; i < MaxImageEmitters; i++) { 3159 MountedImage::ImageEmitter& em = image.emitter[i]; 3160 if (bool(em.emitter)) { 3161 if (em.time > 0) { 3162 em.time -= dt; 3163 3164 // Do we need to update the emitter's node due to the current shape changing? 3165 if (imageShapeIndex != image.lastShapeIndex) 3166 { 3167 em.node = image.state->emitterNode[imageShapeIndex]; 3168 } 3169 3170 MatrixF mat; 3171 getRenderImageTransform(imageSlot,em.node,&mat); 3172 Point3F pos,axis; 3173 mat.getColumn(3,&pos); 3174 mat.getColumn(1,&axis); 3175 em.emitter->emitParticles(pos,true,axis,getVelocity(),(U32) (dt * 1000)); 3176 } 3177 else { 3178 em.emitter->deleteWhenEmpty(); 3179 em.emitter = 0; 3180 } 3181 } 3182 } 3183 3184 image.lastShapeIndex = imageShapeIndex; 3185} 3186 3187 3188//---------------------------------------------------------------------------- 3189 3190void ShapeBase::setImageScriptAnimPrefix(U32 imageSlot, NetStringHandle prefix) 3191{ 3192 MountedImage& image = mMountedImageList[imageSlot]; 3193 if (image.dataBlock) { 3194 setMaskBits(ImageMaskN << imageSlot); 3195 image.scriptAnimPrefix = prefix; 3196 } 3197} 3198 3199NetStringHandle ShapeBase::getImageScriptAnimPrefix(U32 imageSlot) 3200{ 3201 MountedImage& image = mMountedImageList[imageSlot]; 3202 return image.dataBlock? image.scriptAnimPrefix : NetStringHandle(); 3203} 3204 3205 3206//---------------------------------------------------------------------------- 3207 3208U32 ShapeBase::getImageShapeIndex(const MountedImage& image) const 3209{ 3210 U32 shapeIndex = ShapeBaseImageData::StandardImageShape; 3211 3212 const ShapeBaseImageData* data = image.dataBlock; 3213 if (data && data->useFirstPersonShape && isFirstPerson()) 3214 shapeIndex = ShapeBaseImageData::FirstPersonImageShape; 3215 3216 return shapeIndex; 3217} 3218 3219 3220//---------------------------------------------------------------------------- 3221 3222void ShapeBase::startImageEmitter(MountedImage& image,ShapeBaseImageData::StateData& state) 3223{ 3224 MountedImage::ImageEmitter* bem = 0; 3225 MountedImage::ImageEmitter* em = image.emitter; 3226 MountedImage::ImageEmitter* ee = &image.emitter[MaxImageEmitters]; 3227 3228 U32 imageShapeIndex = getImageShapeIndex(image); 3229 3230 // If we are already emitting the same particles from the same 3231 // node, then simply extend the time. Otherwise, find an empty 3232 // emitter slot, or grab the one with the least amount of time left. 3233 for (; em != ee; em++) { 3234 if (bool(em->emitter)) { 3235 if (state.emitter == em->emitter->getDataBlock() && state.emitterNode[imageShapeIndex] == em->node) { 3236 if (state.emitterTime > em->time) 3237 em->time = state.emitterTime; 3238 return; 3239 } 3240 if (!bem || (bool(bem->emitter) && bem->time > em->time)) 3241 bem = em; 3242 } 3243 else 3244 bem = em; 3245 } 3246 3247 bem->time = state.emitterTime; 3248 bem->node = state.emitterNode[imageShapeIndex]; 3249 bem->emitter = new ParticleEmitter; 3250 bem->emitter->onNewDataBlock(state.emitter,false); 3251 if( !bem->emitter->registerObject() ) 3252 { 3253 bem->emitter.getPointer()->destroySelf(); 3254 bem->emitter = NULL; 3255 } 3256} 3257 3258void ShapeBase::submitLights( LightManager *lm, bool staticLighting ) 3259{ 3260 if ( staticLighting ) 3261 return; 3262 3263 // Submit lights for MountedImage(s) 3264 for ( S32 i = 0; i < MaxMountedImages; i++ ) 3265 { 3266 ShapeBaseImageData *imageData = getMountedImage( i ); 3267 3268 if ( imageData != NULL && imageData->lightType != ShapeBaseImageData::NoLight ) 3269 { 3270 MountedImage &image = mMountedImageList[i]; 3271 3272 F32 intensity; 3273 3274 switch ( imageData->lightType ) 3275 { 3276 case ShapeBaseImageData::ConstantLight: 3277 case ShapeBaseImageData::SpotLight: 3278 intensity = 1.0f; 3279 break; 3280 3281 case ShapeBaseImageData::PulsingLight: 3282 intensity = 0.5f + 0.5f * mSin( M_PI_F * (F32)Sim::getCurrentTime() / (F32)imageData->lightDuration + image.lightStart ); 3283 intensity = 0.15f + intensity * 0.85f; 3284 break; 3285 3286 case ShapeBaseImageData::WeaponFireLight: 3287 { 3288 S32 elapsed = Sim::getCurrentTime() - image.lightStart; 3289 if ( elapsed > imageData->lightDuration ) 3290 continue; 3291 intensity = ( 1.0 - (F32)elapsed / (F32)imageData->lightDuration ) * imageData->lightBrightness; 3292 break; 3293 } 3294 default: 3295 intensity = 1.0f; 3296 return; 3297 } 3298 3299 if ( !image.lightInfo ) 3300 image.lightInfo = LightManager::createLightInfo(); 3301 3302 image.lightInfo->setColor( imageData->lightColor ); 3303 image.lightInfo->setBrightness( intensity ); 3304 image.lightInfo->setRange( imageData->lightRadius ); 3305 3306 if ( imageData->lightType == ShapeBaseImageData::SpotLight ) 3307 { 3308 image.lightInfo->setType( LightInfo::Spot ); 3309 // Do we want to expose these or not? 3310 image.lightInfo->setInnerConeAngle( 15 ); 3311 image.lightInfo->setOuterConeAngle( 40 ); 3312 } 3313 else 3314 image.lightInfo->setType( LightInfo::Point ); 3315 3316 MatrixF imageMat; 3317 getRenderImageTransform( i, &imageMat ); 3318 3319 image.lightInfo->setTransform( imageMat ); 3320 3321 lm->registerGlobalLight( image.lightInfo, NULL ); 3322 } 3323 } 3324} 3325 3326 3327//---------------------------------------------------------------------------- 3328 3329void ShapeBase::ejectShellCasing( U32 imageSlot ) 3330{ 3331 MountedImage& image = mMountedImageList[imageSlot]; 3332 ShapeBaseImageData* imageData = image.dataBlock; 3333 3334 if (!imageData->casing) 3335 return; 3336 3337 // Shell casings are client-side only, so use the render transform. 3338 MatrixF ejectTrans; 3339 getRenderImageTransform( imageSlot, imageData->ejectNode[getImageShapeIndex(image)], &ejectTrans ); 3340 3341 Point3F ejectDir = imageData->shellExitDir; 3342 ejectDir.normalize(); 3343 3344 F32 ejectSpread = mDegToRad( imageData->shellExitVariance ); 3345 MatrixF ejectOrient = MathUtils::createOrientFromDir( ejectDir ); 3346 3347 Point3F randomDir; 3348 randomDir.x = mSin( gRandGen.randF( -ejectSpread, ejectSpread ) ); 3349 randomDir.y = 1.0; 3350 randomDir.z = mSin( gRandGen.randF( -ejectSpread, ejectSpread ) ); 3351 randomDir.normalizeSafe(); 3352 3353 ejectOrient.mulV( randomDir ); 3354 3355 MatrixF imageTrans = getRenderTransform(); 3356 imageTrans.mulV( randomDir ); 3357 3358 Point3F shellVel = randomDir * imageData->shellVelocity; 3359 Point3F shellPos = ejectTrans.getPosition(); 3360 3361 3362 Debris *casing = new Debris; 3363 casing->onNewDataBlock( imageData->casing, false ); 3364 casing->setTransform( imageTrans ); 3365 3366 if (!casing->registerObject()) 3367 delete casing; 3368 else 3369 casing->init( shellPos, shellVel ); 3370} 3371 3372void ShapeBase::shakeCamera( U32 imageSlot ) 3373{ 3374 MountedImage& image = mMountedImageList[imageSlot]; 3375 ShapeBaseImageData* imageData = image.dataBlock; 3376 3377 if (!imageData->shakeCamera) 3378 return; 3379 3380 // Warning: this logic was duplicated from Explosion. 3381 3382 // first check if explosion is near camera 3383 GameConnection* connection = GameConnection::getConnectionToServer(); 3384 ShapeBase *obj = dynamic_cast<ShapeBase*>(connection->getControlObject()); 3385 3386 bool applyShake = true; 3387 3388 if (obj) 3389 { 3390 ShapeBase* cObj = obj; 3391 while ((cObj = cObj->getControlObject()) != 0) 3392 { 3393 if (cObj->useObjsEyePoint()) 3394 { 3395 applyShake = false; 3396 break; 3397 } 3398 } 3399 } 3400 3401 if (applyShake && obj) 3402 { 3403 VectorF diff; 3404 getMuzzlePoint(imageSlot, &diff); 3405 diff = obj->getPosition() - diff; 3406 F32 dist = diff.len(); 3407 if (dist < imageData->camShakeRadius) 3408 { 3409 CameraShake *camShake = new CameraShake; 3410 camShake->setDuration(imageData->camShakeDuration); 3411 camShake->setFrequency(imageData->camShakeFreq); 3412 3413 F32 falloff = dist / imageData->camShakeRadius; 3414 falloff = 1.0f + falloff * 10.0f; 3415 falloff = 1.0f / (falloff * falloff); 3416 3417 VectorF shakeAmp = imageData->camShakeAmp * falloff; 3418 camShake->setAmplitude(shakeAmp); 3419 camShake->setFalloff(imageData->camShakeFalloff); 3420 camShake->init(); 3421 gCamFXMgr.addFX(camShake); 3422 } 3423 } 3424} 3425