/* ================ sdClientScriptEntity::Event_GetJointHandle ================ */ void sdClientScriptEntity::Event_GetJointHandle( const char* jointName ) { if ( GetAnimator() != NULL ) { sdProgram::ReturnInteger( GetAnimator()->GetJointHandle( jointName ) ); } else { sdProgram::ReturnInteger( INVALID_JOINT ); } }
/* ===================== sdClientScriptEntity::Event_PlayEffect ===================== */ void sdClientScriptEntity::Event_PlayEffect( const char* effectName, const char* jointName, bool loop ) { jointHandle_t joint; joint = GetAnimator() ? GetAnimator()->GetJointHandle( jointName ) : INVALID_JOINT; rvClientEntityPtr< rvClientEffect > eff; eff = PlayEffect( effectName, colorWhite.ToVec3(), NULL, joint, loop ); sdProgram::ReturnHandle( eff.GetSpawnId() ); }
void hhPodSpawner::SpawnMine() { idVec3 offset; if (spawning || health < 0 || fl.isDormant) { return; } population++; spawning = true; offset = idVec3(0, 0, -64); // Move up to top idDict args; args.Clear(); args.Set( "origin", (GetPhysics()->GetOrigin() + offset).ToString() ); args.Set( "nodrop", "1" ); // Don't put on the floor, wait for release args.Set( "deformType", "0" ); // Don't start deforming until released // pass along any spawn keys const idKeyValue *arg = spawnArgs.MatchPrefix("spawn_"); while( arg ) { args.Set( arg->GetKey().Right( arg->GetKey().Length() - 6 ), arg->GetValue() ); arg = spawnArgs.MatchPrefix( "spawn_", arg ); } // spawn a pod pod = static_cast<hhPod*>(gameLocal.SpawnObject(spawnArgs.GetString("def_pod"), &args)); // attach to bone const char *podBone = "PodPosition"; pod->MoveToJoint(this, podBone); pod->AlignToJoint(this, podBone); pod->BindToJoint(this, podBone, true); pod->fl.takedamage = false; // No damage while attached pod->SetSpawner(this); // Due to the animation, bone isn't in position yet, so wait a little // before making it visible pod->Hide(); pod->PostEventMS(&EV_Show, 500); // start animation if (spawnAnim) { GetAnimator()->ClearAllAnims(gameLocal.time, 0); GetAnimator()->PlayAnim(ANIMCHANNEL_ALL, spawnAnim, gameLocal.time, 500); int opentime = GetAnimator()->GetAnim( spawnAnim )->Length(); PostEventMS( &EV_PlayIdle, opentime ); //TODO: Move this to a frame command (event) PostEventMS( &EV_DropPod, 3700 ); PostEventMS( &EV_DoneSpawning, opentime ); StartSound( "snd_spawn", SND_CHANNEL_ANY ); } }
/* ================ rvTramGate::CycleAnim ================ */ void rvTramGate::CycleAnim( int channel, const char* animName, int blendFrames ) { int animHandle = GetAnimator()->GetAnim( animName ); if( !animHandle ) { ClearAllAnims( blendFrames ); return; } GetAnimator()->CycleAnim( channel, animHandle, gameLocal.GetTime(), FRAME2MS(blendFrames) ); }
/* ================ rvTramGate::PlayAnim ================ */ int rvTramGate::PlayAnim( int channel, const char* animName, int blendFrames ) { int animHandle = GetAnimator()->GetAnim( animName ); if( !animHandle ) { ClearAllAnims( blendFrames ); return 0; } GetAnimator()->PlayAnim( channel, animHandle, gameLocal.GetTime(), FRAME2MS(blendFrames) ); return GetAnimator()->CurrentAnim(channel)->PlayLength(); }
END_CLASS //========================================================================== // // hhAnimatedGui::Spawn // //========================================================================== void hhAnimatedGui::Spawn(void) { idDict args; GetPhysics()->SetContents( CONTENTS_BODY ); idleOpenAnim = GetAnimator()->GetAnim("idleopen"); idleCloseAnim = GetAnimator()->GetAnim("idleclose"); openAnim = GetAnimator()->GetAnim("open"); closeAnim = GetAnimator()->GetAnim("close"); bOpen = false; guiScale.Init(gameLocal.time, 0, AG_SMALL_SCALE, AG_SMALL_SCALE); // Spawn and bind the console on const char *consoleName = spawnArgs.GetString("def_gui"); if (consoleName && *consoleName) { args.Clear(); args.Set("gui", spawnArgs.GetString("gui_topass")); args.Set("origin", GetOrigin().ToString()); // need the joint position args.Set("rotation", GetAxis().ToString()); attachedConsole = gameLocal.SpawnObject(consoleName, &args); assert(attachedConsole); attachedConsole->SetOrigin(GetOrigin() + GetAxis()[0]*10); attachedConsole->Bind(this, true); attachedConsole->Hide(); } // Spawn the trigger const char *triggerName = spawnArgs.GetString("def_trigger"); if (triggerName && *triggerName) { args.Clear(); args.Set( "target", name.c_str() ); args.Set( "mins", spawnArgs.GetString("triggerMins") ); args.Set( "maxs", spawnArgs.GetString("triggerMaxs") ); args.Set( "bind", name.c_str() ); args.SetVector( "origin", GetOrigin() ); args.SetMatrix( "rotation", GetAxis() ); idEntity *trigger = gameLocal.SpawnObject( triggerName, &args ); } if (idleOpenAnim && idleCloseAnim) { PostEventMS(&EV_PlayIdle, 0); } }
void hhPodSpawner::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) { // Don't actually take damage, but give feedback // Play pain if (painAnim) { GetAnimator()->ClearAllAnims(gameLocal.time, 0); GetAnimator()->PlayAnim(ANIMCHANNEL_ALL, painAnim, gameLocal.time, 500); int opentime = GetAnimator()->GetAnim( painAnim )->Length(); PostEventMS( &EV_PlayIdle, opentime ); StartSound( "snd_pain", SND_CHANNEL_ANY ); } }
//----------------------------------------------------------------------------- // Purpose: Renders the connecting lines between the keyframes // Input : pRender - //----------------------------------------------------------------------------- void CMapKeyFrame::Render3D( CRender3D *pRender ) { if ( m_bRebuildPath ) { if (GetAnimator() != NULL) { GetAnimator()->RebuildPath(); } } // only draw if we have a valid connection if ( m_pNextKeyFrame && m_flSpeed > 0 ) { // only draw if we haven't already been drawn this frame if ( GetRenderFrame() != pRender->GetRenderFrame() ) { pRender->PushRenderMode( RENDER_MODE_WIREFRAME ); SetRenderFrame( pRender->GetRenderFrame() ); Vector o1, o2; GetOrigin( o1 ); m_pNextKeyFrame->GetOrigin( o2 ); CMeshBuilder meshBuilder; CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); IMesh *pMesh = pRenderContext->GetDynamicMesh(); // draw connecting line going from green to red meshBuilder.Begin( pMesh, MATERIAL_LINE_STRIP, MAX_LINE_POINTS ); // start point meshBuilder.Color3f( 0, 1.0f, 0 ); meshBuilder.Position3f( o1[0], o1[1], o1[2] ); meshBuilder.AdvanceVertex(); for ( int i = 0; i < MAX_LINE_POINTS; i++ ) { float red = (float)(i+1) / (float)MAX_LINE_POINTS; meshBuilder.Color3f( red, 1.0f - red, 0 ); meshBuilder.Position3f( m_LinePoints[i][0], m_LinePoints[i][1], m_LinePoints[i][2] ); meshBuilder.AdvanceVertex(); } meshBuilder.End(); pMesh->Draw(); pRender->PopRenderMode(); } } }
//========================================================================== // // hhAnimatedGui::Event_PlayIdle // //========================================================================== void hhAnimatedGui::Event_PlayIdle() { GetAnimator()->ClearAllAnims(gameLocal.time, 0); if (bOpen) { GetAnimator()->CycleAnim(ANIMCHANNEL_ALL, idleOpenAnim, gameLocal.time, 0); FadeInGui(); } else { GetAnimator()->CycleAnim(ANIMCHANNEL_ALL, idleCloseAnim, gameLocal.time, 0); } }
END_CLASS void hhPodSpawner::Spawn(void) { fl.takedamage = false; GetPhysics()->SetContents( CONTENTS_BODY ); pod = NULL; spawning = false; idleAnim = GetAnimator()->GetAnim("idle"); painAnim = GetAnimator()->GetAnim("pain"); spawnAnim = GetAnimator()->GetAnim("spawn"); PostEventMS(&EV_PlayIdle, 0); }
/* ================ rvHealingStation::CreateFrame ================ */ void rvHealingStation::CreateFrame ( float station_health ) { // Update the GUI if ( renderEntity.gui[ 0 ] ) { renderEntity.gui[ 0 ]->SetStateFloat( "station_health", 1.0f - station_health ); renderEntity.gui[ 0 ]->StateChanged( gameLocal.time, true ); } // Update the Animation int numFrames = GetAnimator()->GetAnim( dispenseAnim )->NumFrames(); float lerp = numFrames * station_health; int frame = lerp; lerp = lerp - frame; frameBlend_t frameBlend = { 0, frame, frame + 1, 1.0f - lerp, lerp }; GetAnimator()->SetFrame( ANIMCHANNEL_ALL, dispenseAnim, frameBlend ); }
/* ================ sdWalker::UpdateAnimationControllers ================ */ bool sdWalker::UpdateAnimationControllers( void ) { if ( !gameLocal.isNewFrame ) { return false; } if ( ik.IsInitialized() ) { bool animating = GetAnimator()->IsAnimating( gameLocal.time ); if ( ( lastIKPos - physicsObj.GetOrigin() ).LengthSqr() > Square( 1.f ) ) { lastIKTime = gameLocal.time; } if ( animating || ( gameLocal.time - lastIKTime ) < SEC2MS( 0.5f ) || ik_debug.GetBool() ) { if ( animating ) { lastIKTime = gameLocal.time; } lastIKPos = physicsObj.GetOrigin(); if ( !ik.IsInhibited() ) { return ik.Evaluate(); } return false; } } else { ik.ClearJointMods(); } return false; }
//----------------------------------------------------------------------------- // Purpose: Builds the spline points between this keyframe and the previous // keyframe. // Input : pPrev - //----------------------------------------------------------------------------- void CMapKeyFrame::BuildPathSegment( CMapKeyFrame *pPrev ) { RecalculateTimeFromSpeed(); CMapAnimator *pAnim = GetAnimator(); Quaternion qAngles; for ( int i = 0; i < MAX_LINE_POINTS; i++ ) { if (pAnim != NULL) { CMapAnimator::GetAnimationAtTime( this, pPrev, MoveTime() * ( float )( i + 1 ) / (float)MAX_LINE_POINTS, m_LinePoints[i], qAngles, pAnim->m_iPositionInterpolator, pAnim->m_iRotationInterpolator ); } else { // FIXME: If we aren't connected to an animator yet, just draw straight lines. This code is never hit, because // BuildPathSegment is only called from CMapAnimator. To make matters worse, we can only reliably find // pPrev through an animator. CMapAnimator::GetAnimationAtTime( this, pPrev, MoveTime() * (float)( i + 1) / (float)MAX_LINE_POINTS, m_LinePoints[i], qAngles, 0, 0 ); } } // HACK: we shouldn't need to do this. CalcBounds alone should work (but it doesn't because of where we // call RebuildPath from). Make this work more like other objects. if ( m_pParent ) { GetParent()->CalcBounds( true ); } else { CalcBounds(); } m_bRebuildPath = false; }
/* ================= sdWalker::Event_GroundPound ================= */ void sdWalker::Event_GroundPound( float force, float damageScale, float shockWaveRange ) { GroundPound( force, damageScale, shockWaveRange ); bool leftleg = false; jointHandle_t jh = GetAnimator()->GetJointHandle( spawnArgs.GetString( leftleg ? "joint_foot_left" : "joint_foot_right" ) ); idVec3 traceOrig; GetWorldOrigin( jh, traceOrig ); idVec3 traceEnd = traceOrig; traceOrig.z += 100.0f; traceEnd.z -= 10.0f; trace_t traceObject; gameLocal.clip.TracePoint( CLIP_DEBUG_PARMS traceObject, traceOrig, traceEnd, MASK_SOLID | CONTENTS_WATER | MASK_OPAQUE, this ); traceEnd = traceObject.endpos; if ( traceObject.fraction < 1.f ) { int cont = gameLocal.clip.Contents( CLIP_DEBUG_PARMS traceEnd, NULL, mat3_identity, CONTENTS_WATER, this ); if ( !cont ) { const char* surfaceTypeName = NULL; if ( traceObject.c.surfaceType ) { surfaceTypeName = traceObject.c.surfaceType->GetName(); } idVec3 colorWhite(1.f,1.f,1.f); idVec3 xaxis(-1.f, 0.f, 0.f); PlayEffect( "fx_ground_pound", colorWhite, surfaceTypeName, traceEnd, xaxis.ToMat3() ); } } }
LTBOOL CAISharkStrategyFollowPath::UpdateMoveTo(CAIPathWaypoint* pWaypoint) { if ( m_AIMovement.IsSet() ) { if ( !m_AIMovement.Update() ) { return LTFALSE; } if ( m_AIMovement.IsDone() ) { m_pPath->IncrementWaypointIndex(); } } else if ( m_AIMovement.IsUnset() || m_AIMovement.IsDone() ) { m_AIMovement.Set(pWaypoint->GetArgumentVector1()); } else // Stuck { } GetAnimator()->SetMain(m_eMovement); return LTTRUE; }
void AI_Helicopter::UpdateAnimator() { if ( !m_damage.IsDead() ) { CAnimatorAIVehicle::Main eAnimation = CAnimatorAIVehicle::eIdle; if ( m_bWantsRightDoorOpened && !m_bRightDoorOpened ) { if ( GetAnimator()->IsAnimatingMainDone(CAnimatorAIVehicle::eOpenRightDoor) ) { m_bWantsRightDoorOpened = LTFALSE; m_bRightDoorOpened = LTTRUE; eAnimation = CAnimatorAIVehicle::eOpenedRightDoor; } else { eAnimation = CAnimatorAIVehicle::eOpenRightDoor; } } else if ( m_bWantsRightDoorClosed && m_bRightDoorOpened ) { if ( GetAnimator()->IsAnimatingMainDone(CAnimatorAIVehicle::eCloseRightDoor) ) { m_bWantsRightDoorClosed = LTFALSE; m_bRightDoorOpened = LTFALSE; eAnimation = CAnimatorAIVehicle::eClosedRightDoor; } else { eAnimation = CAnimatorAIVehicle::eCloseRightDoor; } } else if ( m_bRightDoorOpened ) { eAnimation = CAnimatorAIVehicle::eOpenedRightDoor; } else if ( !m_bRightDoorOpened ) { eAnimation = CAnimatorAIVehicle::eClosedRightDoor; } GetAnimator()->SetMain(eAnimation); } }
/* ================ idActor::LoadAF ================ */ bool idActor::LoadAF( void ) { idStr fileName; if ( !spawnArgs.GetString( "ragdoll", "*unknown*", fileName ) || !fileName.Length() ) { return false; } af.SetAnimator( GetAnimator() ); return af.Load( this, fileName ); }
/* ===================== sdWalker::GetMoveDelta ===================== */ void sdWalker::GetMoveDelta( idVec3& delta ) { GetAnimator()->GetDelta( gameLocal.time - gameLocal.msec, gameLocal.time, delta, 1 ); // if ( delta.Length() > 0.f ) { // gameLocal.Printf( "Delta: %s Time: %i %s\n", delta.ToString(), gameLocal.time, gameLocal.isNewFrame ? "" : "re-prediction" ); // } delta = viewAxis * delta; }
LTBOOL CAIDogStrategyOneShotAni::Update() { super::Update(); if ( !m_bAnimating ) { return LTFALSE; } if ( GetAnimator()->IsAnimatingMainDone(m_eMain) ) { m_bAnimating = LTFALSE; } GetAnimator()->SetMain(m_eMain); return LTTRUE; }
LTBOOL CAIDogStrategyOneShotAni::Set(CAnimatorAIAnimal::Main eMain) { GetAnimator()->SetMain(eMain); m_eMain = eMain; m_bAnimating = LTTRUE; return LTTRUE; }
/* ================ rvHealingStation::Spawn ================ */ void rvHealingStation::Spawn ( void ) { entityToHeal = 0; nextHealTime = 0; healFrequency = spawnArgs.GetInt( "heal_frequency", "24" ); healAmount = spawnArgs.GetInt( "heal_amount", "1" ); healthDispensed = 0; soundStartTime = 0; soundLength = 0; maxHealth = spawnArgs.GetInt( "max_health", "100" ); dispenseAnim = GetAnimator()->GetAnim( spawnArgs.GetString( "dispense_anim", "dispense" ) ); CreateFrame( 0 ); stateThread.SetOwner( this ); stateThread.SetName( GetName() ); GetAnimator()->CycleAnim( ANIMCHANNEL_ALL, GetAnimator()->GetAnim( spawnArgs.GetString( "anim", "idle" ) ), gameLocal.time, 4 ); }
/* ================ sdWalker::LoadAF ================ */ void sdWalker::LoadAF( void ) { idStr fileName; if ( !spawnArgs.GetString( "ragdoll", "*unknown*", fileName ) || !fileName.Length() ) { return; } af.SetAnimator( GetAnimator() ); if( !af.Load( this, fileName ) ) { return; } }
void hhGibbable::Spawn(void) { bVertexColorFade = spawnArgs.GetBool("materialFade"); if (bVertexColorFade) { SetDeformation(DEFORMTYPE_VERTEXCOLOR, 1.0f); } //HUMANHEAD: aob - Flynn wanted some gibbables to be triggered only fl.takedamage = !spawnArgs.GetBool("noDamage", "0"); // setup the clipModel // GetPhysics()->SetContents( CONTENTS_SOLID ); GetPhysics()->SetContents( CONTENTS_BODY | CONTENTS_RENDERMODEL ); idleAnim = GetAnimator()->GetAnim("idle"); painAnim = GetAnimator()->GetAnim("pain"); idleChannel = GetChannelForAnim( "idle" ); painChannel = GetChannelForAnim( "pain" ); PostEventMS(&EV_PlayIdle, 1000); }
END_CLASS void hhControlHand::Spawn() { bProcessControls = false; oldStatus = 0; anims[ 0 ][ 0 ] = GetAnimator()->GetAnim("bottom_backward"); anims[ 0 ][ 1 ] = GetAnimator()->GetAnim("bottom_center"); anims[ 0 ][ 2 ] = GetAnimator()->GetAnim("bottom_forward"); anims[ 1 ][ 0 ] = GetAnimator()->GetAnim("center_backward"); anims[ 1 ][ 1 ] = GetAnimator()->GetAnim("center_center"); anims[ 1 ][ 2 ] = GetAnimator()->GetAnim("center_forward"); anims[ 2 ][ 0 ] = GetAnimator()->GetAnim("top_backward"); anims[ 2 ][ 1 ] = GetAnimator()->GetAnim("top_center"); anims[ 2 ][ 2 ] = GetAnimator()->GetAnim("top_forward"); fl.networkSync = true; }
void hhHarvesterSimple::Event_ExitPassageway(hhAIPassageway *pn) { if(currPassageway == NULL) { gameLocal.Error("%s tried to EXIT a passageway but did NOT have a curr passageway!", (const char*)name); } HH_ASSERT(currPassageway != NULL); // Store this passageway so we don't jump right back into it. lastPassageway = idEntityPtr<idEntity> (pn); lastPassagewayTime = gameLocal.time; if(GetPhysics()) GetPhysics()->SetContents(CONTENTS_BODY); currPassageway = NULL; AI_ACTIVATED = true; // Sometimes this is NOT true?!?! StopSound(SND_CHANNEL_VOICE, true); // Teleport to new pos idAngles angs = pn->GetAxis().ToAngles(); idVec3 pos = pn->GetExitPos(); Teleport( pos, angs, pn ); Show(); current_yaw = angs.yaw; ideal_yaw = angs.yaw; turnVel = 0.0f; HH_ASSERT(FacingIdeal()); // Exit anim to play? idStr exitAnim = pn->spawnArgs.GetString("exit_anim"); if(exitAnim.Length() && GetAnimator()->HasAnim(exitAnim)) { GetAnimator()->ClearAllAnims(gameLocal.GetTime(), 0); torsoAnim.UpdateState(); legsAnim.UpdateState(); torsoAnim.PlayAnim( GetAnimator()->GetAnim( exitAnim ) ); legsAnim.PlayAnim( GetAnimator()->GetAnim( exitAnim ) ); } }
bool hhGibbable::Pain( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) { // Adjust vertex color if (bVertexColorFade) { float fadeAlpha = idMath::ClampFloat(0.0f, 1.0f, ((float)health / spawnArgs.GetFloat("health"))); SetDeformation(DEFORMTYPE_VERTEXCOLOR, fadeAlpha); } if (painAnim) { GetAnimator()->PlayAnim( painChannel, painAnim, gameLocal.time, 0); } return( hhAnimatedEntity::Pain(inflictor, attacker, damage, dir, location) ); }
void hhControlHand::UpdateControlDirection(idVec3 &dir) { int anim; int curStatus = dir.DirectionMask(); if (bProcessControls && oldStatus != curStatus) { // Determine which anim group to play. (Up/Down/Normal & Forward/Center/Back) int z_index = dir.z < 0 ? 0 : dir.z > 0 ? 2 : 1; int x_index = dir.x < 0 ? 0 : dir.x > 0 ? 2 : 1; anim = anims[ z_index ][ x_index ]; GetAnimator()->CycleAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, 250); // Now determine which left and right to play float left = dir.y > 0 ? 1.0f : 0.0f; float right = dir.y < 0 ? 1.0f : 0.0f; GetAnimator()->CurrentAnim( ANIMCHANNEL_ALL )->SetSyncedAnimWeight( 0, left ); GetAnimator()->CurrentAnim( ANIMCHANNEL_ALL )->SetSyncedAnimWeight( 1, 1.0f ); GetAnimator()->CurrentAnim( ANIMCHANNEL_ALL )->SetSyncedAnimWeight( 2, right ); oldStatus = curStatus; } }
void hhAnimatedGui::Event_Trigger( idEntity *activator ) { if (!openAnim || !closeAnim) { return; } GetAnimator()->ClearAllAnims(gameLocal.time, 0); int ms; if(bOpen) { GetAnimator()->PlayAnim(ANIMCHANNEL_ALL, closeAnim, gameLocal.time, 0); ms = GetAnimator()->GetAnim( closeAnim )->Length(); bOpen = false; FadeOutGui(); } else { GetAnimator()->PlayAnim(ANIMCHANNEL_ALL, openAnim, gameLocal.time, 0); ms = GetAnimator()->GetAnim( openAnim )->Length(); bOpen = true; } PostEventMS(&EV_PlayIdle, ms); }
LTBOOL CAIPoodleStrategyFollowPath::UpdateOpenDoors(CAIPathWaypoint* pWaypoint) { LTBOOL bDoorsOpen = LTTRUE; HOBJECT hDoor1, hDoor2; hDoor1 = pWaypoint->GetArgumentObject1(); hDoor2 = pWaypoint->GetArgumentObject2(); if ( hDoor1 || hDoor2 ) { GetAnimator()->SetMain(CAnimatorAIAnimal::eIdle); } return LTTRUE; }
/* ================ bmVehicle_Jeep::Spawn ================ */ void bmVehicle_Jeep::Spawn( void ) { static const char *wheelJointKeys[] = { "wheelJointFrontLeft", "wheelJointFrontRight", "wheelJointRearLeft", "wheelJointRearRight" }; static idVec3 wheelPoly[4] = { idVec3( 2, 2, 0 ), idVec3( 2, -2, 0 ), idVec3( -2, -2, 0 ), idVec3( -2, 2, 0 ) }; int i; idVec3 origin; idMat3 axis; idTraceModel trm; trm.SetupPolygon( wheelPoly, 4 ); trm.Translate( idVec3( 0, 0, -wheelRadius ) ); wheelModel = new idClipModel( trm ); InitEmergencyLights(); for ( i = 0; i < 4; i++ ) { const char *wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" ); if ( !wheelJointName[0] ) { gameLocal.Error( "bmVehicle_Jeep '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] ); } wheelJoints[i] = animator.GetJointHandle( wheelJointName ); if ( wheelJoints[i] == INVALID_JOINT ) { gameLocal.Error( "bmVehicle_Jeep '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName ); } GetAnimator()->GetJointTransform( wheelJoints[i], 0, origin, axis ); origin = renderEntity.origin + origin * renderEntity.axis; suspension[i] = new idAFConstraint_Suspension(); suspension[i]->Setup( va( "suspension%d", i ), af.GetPhysics()->GetBody( 0 ), origin, af.GetPhysics()->GetAxis( 0 ), wheelModel ); suspension[i]->SetSuspension( g_vehicleSuspensionUp.GetFloat(), g_vehicleSuspensionDown.GetFloat(), g_vehicleSuspensionKCompress.GetFloat(), g_vehicleSuspensionDamping.GetFloat(), g_vehicleTireFriction.GetFloat() ); af.GetPhysics()->AddConstraint( suspension[i] ); } memset( wheelAngles, 0, sizeof( wheelAngles ) ); BecomeActive( TH_THINK ); }