bool idMoveable::Collide( const trace_t &collision, const idVec3 &velocity ) { // greebo: Check whether we are colliding with the nearly exact same point again bool sameCollisionAgain = ( lastCollision.fraction != -1 && lastCollision.c.point.Compare( collision.c.point, 0.05f ) ); // greebo: Save the collision info for the next call lastCollision = collision; float v = -( velocity * collision.c.normal ); if( !sameCollisionAgain ) { float bounceSoundMinVelocity = cv_bounce_sound_min_vel.GetFloat(); float bounceSoundMaxVelocity = cv_bounce_sound_max_vel.GetFloat(); if( ( v > bounceSoundMinVelocity ) && ( gameLocal.time > nextSoundTime ) ) { // grayman #3331 - some moveables should not bother with bouncing sounds if( !spawnArgs.GetBool( "no_bounce_sound", "0" ) ) { const idMaterial *material = collision.c.material; idStr sndNameLocal; idStr surfaceName; // "tile", "glass", etc. if( material != NULL ) { surfaceName = g_Global.GetSurfName( material ); // Prepend the snd_bounce_ prefix to check for a surface-specific sound idStr sndNameWithSurface = "snd_bounce_" + surfaceName; if( spawnArgs.FindKey( sndNameWithSurface ) != NULL ) { sndNameLocal = sndNameWithSurface; } else { sndNameLocal = "snd_bounce"; } } const char *sound = spawnArgs.GetString( sndNameLocal ); const idSoundShader *sndShader = declManager->FindSound( sound ); //f = v > BOUNCE_SOUND_MAX_VELOCITY ? 1.0f : idMath::Sqrt( v - BOUNCE_SOUND_MIN_VELOCITY ) * ( 1.0f / idMath::Sqrt( BOUNCE_SOUND_MAX_VELOCITY - BOUNCE_SOUND_MIN_VELOCITY ) ); // angua: modify the volume set in the def instead of setting a fixed value. // At minimum velocity, the volume should be "min_velocity_volume_decrease" lower (in db) than the one specified in the def float f = ( v > bounceSoundMaxVelocity ) ? 0.0f : spawnArgs.GetFloat( "min_velocity_volume_decrease", "0" ) * ( idMath::Sqrt( v - bounceSoundMinVelocity ) * ( 1.0f / idMath::Sqrt( bounceSoundMaxVelocity - bounceSoundMinVelocity ) ) - 1 ); float volume = sndShader->GetParms()->volume + f; if( cv_moveable_collision.GetBool() ) { gameRenderWorld->DrawText( va( "Velocity: %f", v ), ( physicsObj.GetOrigin() + idVec3( 0, 0, 20 ) ), 0.25f, colorGreen, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1, 100 * gameLocal.msec ); gameRenderWorld->DrawText( va( "Volume: %f", volume ), ( physicsObj.GetOrigin() + idVec3( 0, 0, 10 ) ), 0.25f, colorGreen, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1, 100 * gameLocal.msec ); gameRenderWorld->DebugArrow( colorMagenta, collision.c.point, ( collision.c.point + 30 * collision.c.normal ), 4.0f, 1 ); } SetSoundVolume( volume ); // greebo: We don't use StartSound() here, we want to do the sound propagation call manually StartSoundShader( sndShader, SND_CHANNEL_ANY, 0, false, NULL ); // grayman #2603 - don't propagate a sound if this is a doused torch dropped by an AI if( !spawnArgs.GetBool( "is_torch", "0" ) ) { idStr sndPropName = GetSoundPropNameForMaterial( surfaceName ); // Propagate a suspicious sound, using the "group" convention (soft, hard, small, med, etc.) PropSoundS( NULL, sndPropName, f, 0 ); // grayman #3355 } SetSoundVolume( 0.0f ); nextSoundTime = gameLocal.time + 500; } } // tels: //DM_LOG(LC_ENTITY, LT_INFO)LOGSTRING("Moveable %s might call script_collide %s because m_collideScriptCounter = %i and v = %f and time (%d) > m_nextCollideScriptTime (%d)\r", // name.c_str(), m_scriptCollide.c_str(), m_collideScriptCounter, v, gameLocal.time, m_nextCollideScriptTime ); if( ( m_collideScriptCounter != 0 ) && ( v > m_minScriptVelocity ) && ( gameLocal.time > m_nextCollideScriptTime ) ) { if( m_collideScriptCounter > 0 ) { // if positive, decrement it, so -1 stays as it is (for 0, we never come here) m_collideScriptCounter --; } // call the script const function_t *pScriptFun = scriptObject.GetFunction( m_scriptCollide.c_str() ); if( pScriptFun == NULL ) { // Local function not found, check in global namespace pScriptFun = gameLocal.program.FindFunction( m_scriptCollide.c_str() ); } if( pScriptFun != NULL ) { DM_LOG( LC_ENTITY, LT_INFO )LOGSTRING( "Moveable %s calling script_collide %s.\r", name.c_str(), m_scriptCollide.c_str() ); idThread *pThread = new idThread( pScriptFun ); pThread->CallFunctionArgs( pScriptFun, true, "e", this ); pThread->DelayedStart( 0 ); } else { // script function not found! DM_LOG( LC_ENTITY, LT_ERROR )LOGSTRING( "Moveable %s could not find script_collide %s.\r", name.c_str(), m_scriptCollide.c_str() ); m_collideScriptCounter = 0; } m_nextCollideScriptTime = gameLocal.time + 300; } } idEntity *ent = gameLocal.entities[ collision.c.entityNum ]; trace_t newCollision = collision; // grayman #2816 - in case we need to modify collision // grayman #2816 - if we hit the world, skip all the damage work if( ent && ( ent != gameLocal.world ) ) { idActor *entActor = NULL; if( ent->IsType( idActor::Type ) ) { entActor = static_cast<idActor *>( ent ); // the object hit an actor directly } else if( ent->IsType( idAFAttachment::Type ) ) { newCollision.c.id = JOINT_HANDLE_TO_CLIPMODEL_ID( static_cast<idAFAttachment *>( ent )->GetAttachJoint() ); } // go up the bindMaster chain to see if an Actor is lurking if( entActor == NULL ) { // no actor yet, so ent is an attachment or an attached moveable idEntity *bindMaster = ent->GetBindMaster(); while( bindMaster != NULL ) { if( bindMaster->IsType( idActor::Type ) ) { entActor = static_cast<idActor *>( bindMaster ); // the object hit something attached to an actor // If ent is an idAFAttachment, we can leave ent alone // and pass the damage to it. It will, in turn, pass the // damage to the actor it's attached to. (helmets) // If ent is NOT an attachment, we have to change it to // be the actor we just found. Inventor goggles are an // example of when we have to do this, because they're // an idMoveable, and they DON'T transfer their damage // to the actor they're attached to. if( !ent->IsType( idAFAttachment::Type ) ) { ent = bindMaster; } break; } bindMaster = bindMaster->GetBindMaster(); // go up the chain } } // grayman #2816 - in order to allow knockouts from dropped objects, // we have to allow collisions where the velocity is < minDamageVelocity, // because dropped objects can have low velocity, while at the same time // carrying enough damage to warrant a KO possibility. if( canDamage && damage.Length() && ( gameLocal.time > nextDamageTime ) ) { if( !entActor || !entActor->AI_DEAD ) { float f; if( v < minDamageVelocity ) { f = 0.0f; } else if( v < maxDamageVelocity ) { f = idMath::Sqrt( ( v - minDamageVelocity ) / ( maxDamageVelocity - minDamageVelocity ) ); } else { f = 1.0f; // capped when v >= maxDamageVelocity } // scale the damage by the surface type multiplier, if any idStr SurfTypeName; g_Global.GetSurfName( newCollision.c.material, SurfTypeName ); SurfTypeName = "damage_mult_" + SurfTypeName; f *= spawnArgs.GetFloat( SurfTypeName.c_str(), "1.0" ); idVec3 dir = velocity; dir.NormalizeFast(); // Use a technique similar to what's used for a melee collision // to find a better joint (location), because when the head is // hit, the joint isn't identified correctly w/o it. int location = JOINT_HANDLE_TO_CLIPMODEL_ID( newCollision.c.id ); // If this moveable is attached to an AI, identify that AI. // Otherwise, assume it was put in motion by someone. idEntity *attacker = GetPhysics()->GetClipModel()->GetOwner(); if( attacker == NULL ) { attacker = m_SetInMotionByActor.GetEntity(); } // grayman #3370 - if the entity being hit is the attacker, don't do damage if( attacker != ent ) { int preHealth = ent->health; ent->Damage( this, attacker, dir, damage, f, location, const_cast<trace_t *>( &newCollision ) ); if( ent->health < preHealth ) { // only set the timer if there was damage nextDamageTime = gameLocal.time + 1000; } } } } // Darkmod: Collision stims and a tactile alert if it collides with an AI ProcCollisionStims( ent, newCollision.c.id ); // grayman #2816 - use new collision if( entActor && entActor->IsType( idAI::Type ) ) { static_cast<idAI *>( entActor )->TactileAlert( this ); } } if( fxCollide.Length() && ( gameLocal.time > nextCollideFxTime ) ) { idEntityFx::StartFx( fxCollide, &collision.c.point, NULL, this, false ); nextCollideFxTime = gameLocal.time + 3500; } return false; }
idVec3 v4to3( const idVec4 &v ) { return idVec3( v.x / v.w, v.y / v.w, v.z / v.w ); }
/* ================ idIK_Walk::Init ================ */ bool idIK_Walk::Init( idEntity *self, const char *anim, const idVec3 &modelOffset ) { int i; float footSize; idVec3 verts[4]; idTraceModel trm; const char *jointName; idVec3 dir, ankleOrigin, kneeOrigin, hipOrigin, dirOrigin; idMat3 axis, ankleAxis, kneeAxis, hipAxis; static idVec3 footWinding[4] = { idVec3( 1.0f, 1.0f, 0.0f ), idVec3( -1.0f, 1.0f, 0.0f ), idVec3( -1.0f, -1.0f, 0.0f ), idVec3( 1.0f, -1.0f, 0.0f ) }; if ( !self ) { return false; } numLegs = Min( self->spawnArgs.GetInt( "ik_numLegs", "0" ), MAX_LEGS ); if ( numLegs == 0 ) { return true; } if ( !idIK::Init( self, anim, modelOffset ) ) { return false; } int numJoints = animator->NumJoints(); idJointMat *joints = ( idJointMat * )_alloca16( numJoints * sizeof( joints[0] ) ); // create the animation frame used to setup the IK gameEdit->ANIM_CreateAnimFrame( animator->ModelHandle(), animator->GetAnim( modifiedAnim )->MD5Anim( 0 ), numJoints, joints, 1, animator->ModelDef()->GetVisualOffset() + modelOffset, animator->RemoveOrigin() ); enabledLegs = 0; // get all the joints for ( i = 0; i < numLegs; i++ ) { jointName = self->spawnArgs.GetString( va( "ik_foot%d", i+1 ) ); footJoints[i] = animator->GetJointHandle( jointName ); if ( footJoints[i] == INVALID_JOINT ) { gameLocal.Error( "idIK_Walk::Init: invalid foot joint '%s'", jointName ); } jointName = self->spawnArgs.GetString( va( "ik_ankle%d", i+1 ) ); ankleJoints[i] = animator->GetJointHandle( jointName ); if ( ankleJoints[i] == INVALID_JOINT ) { gameLocal.Error( "idIK_Walk::Init: invalid ankle joint '%s'", jointName ); } jointName = self->spawnArgs.GetString( va( "ik_knee%d", i+1 ) ); kneeJoints[i] = animator->GetJointHandle( jointName ); if ( kneeJoints[i] == INVALID_JOINT ) { gameLocal.Error( "idIK_Walk::Init: invalid knee joint '%s'\n", jointName ); } jointName = self->spawnArgs.GetString( va( "ik_hip%d", i+1 ) ); hipJoints[i] = animator->GetJointHandle( jointName ); if ( hipJoints[i] == INVALID_JOINT ) { gameLocal.Error( "idIK_Walk::Init: invalid hip joint '%s'\n", jointName ); } jointName = self->spawnArgs.GetString( va( "ik_dir%d", i+1 ) ); dirJoints[i] = animator->GetJointHandle( jointName ); enabledLegs |= 1 << i; } jointName = self->spawnArgs.GetString( "ik_waist" ); waistJoint = animator->GetJointHandle( jointName ); if ( waistJoint == INVALID_JOINT ) { gameLocal.Error( "idIK_Walk::Init: invalid waist joint '%s'\n", jointName ); } // get the leg bone lengths and rotation matrices for ( i = 0; i < numLegs; i++ ) { oldAnkleHeights[i] = 0.0f; ankleAxis = joints[ ankleJoints[ i ] ].ToMat3(); ankleOrigin = joints[ ankleJoints[ i ] ].ToVec3(); kneeAxis = joints[ kneeJoints[ i ] ].ToMat3(); kneeOrigin = joints[ kneeJoints[ i ] ].ToVec3(); hipAxis = joints[ hipJoints[ i ] ].ToMat3(); hipOrigin = joints[ hipJoints[ i ] ].ToVec3(); // get the IK direction if ( dirJoints[i] != INVALID_JOINT ) { dirOrigin = joints[ dirJoints[ i ] ].ToVec3(); dir = dirOrigin - kneeOrigin; } else { dir.Set( 1.0f, 0.0f, 0.0f ); } hipForward[i] = dir * hipAxis.Transpose(); kneeForward[i] = dir * kneeAxis.Transpose(); // conversion from upper leg bone axis to hip joint axis upperLegLength[i] = GetBoneAxis( hipOrigin, kneeOrigin, dir, axis ); upperLegToHipJoint[i] = hipAxis * axis.Transpose(); // conversion from lower leg bone axis to knee joint axis lowerLegLength[i] = GetBoneAxis( kneeOrigin, ankleOrigin, dir, axis ); lowerLegToKneeJoint[i] = kneeAxis * axis.Transpose(); } smoothing = self->spawnArgs.GetFloat( "ik_smoothing", "0.75" ); waistSmoothing = self->spawnArgs.GetFloat( "ik_waistSmoothing", "0.75" ); footShift = self->spawnArgs.GetFloat( "ik_footShift", "0" ); waistShift = self->spawnArgs.GetFloat( "ik_waistShift", "0" ); minWaistFloorDist = self->spawnArgs.GetFloat( "ik_minWaistFloorDist", "0" ); minWaistAnkleDist = self->spawnArgs.GetFloat( "ik_minWaistAnkleDist", "0" ); footUpTrace = self->spawnArgs.GetFloat( "ik_footUpTrace", "32" ); footDownTrace = self->spawnArgs.GetFloat( "ik_footDownTrace", "32" ); tiltWaist = self->spawnArgs.GetBool( "ik_tiltWaist", "0" ); usePivot = self->spawnArgs.GetBool( "ik_usePivot", "0" ); // setup a clip model for the feet footSize = self->spawnArgs.GetFloat( "ik_footSize", "4" ) * 0.5f; if ( footSize > 0.0f ) { for ( i = 0; i < 4; i++ ) { verts[i] = footWinding[i] * footSize; } trm.SetupPolygon( verts, 4 ); footModel = new (TAG_PHYSICS_CLIP) idClipModel( trm ); } initialized = true; return true; }
/* ============ idMoveableItem::Event_Gib ============ */ void idMoveableItem::Event_Gib(const char *damageDefName) { Gib(idVec3(0, 0, 1), damageDefName); }
/* ============ idAI::PredictPath Can also be used when there is no AAS file available however ledges are not detected. ============ */ bool idAI::PredictPath( const idEntity *ent, const idAAS *aas, const idVec3 &start, const idVec3 &velocity, int totalTime, int frameTime, int stopEvent, predictedPath_t &path ) { int i, j, step, numFrames, curFrameTime; idVec3 delta, curStart, curEnd, curVelocity, lastEnd, stepUp, tmpStart; idVec3 gravity, gravityDir, invGravityDir; float maxStepHeight, minFloorCos; pathTrace_t trace; if ( aas && aas->GetSettings() ) { gravity = aas->GetSettings()->gravity; gravityDir = aas->GetSettings()->gravityDir; invGravityDir = aas->GetSettings()->invGravityDir; maxStepHeight = aas->GetSettings()->maxStepHeight; minFloorCos = aas->GetSettings()->minFloorCos; } else { gravity = DEFAULT_GRAVITY_VEC3; gravityDir = idVec3( 0, 0, -1 ); invGravityDir = idVec3( 0, 0, 1 ); maxStepHeight = 14.0f; minFloorCos = 0.7f; } path.endPos = start; path.endVelocity = velocity; path.endNormal.Zero(); path.endEvent = 0; path.endTime = 0; path.blockingEntity = NULL; curStart = start; curVelocity = velocity; numFrames = ( totalTime + frameTime - 1 ) / frameTime; curFrameTime = frameTime; for ( i = 0; i < numFrames; i++ ) { if ( i == numFrames-1 ) { curFrameTime = totalTime - i * curFrameTime; } delta = curVelocity * curFrameTime * 0.001f; path.endVelocity = curVelocity; path.endTime = i * frameTime; // allow sliding along a few surfaces per frame for ( j = 0; j < MAX_FRAME_SLIDE; j++ ) { idVec3 lineStart = curStart; // allow stepping up three times per frame for ( step = 0; step < 3; step++ ) { curEnd = curStart + delta; if ( PathTrace( ent, aas, curStart, curEnd, stopEvent, trace, path ) ) { return true; } if ( step ) { // step down at end point tmpStart = trace.endPos; curEnd = tmpStart - stepUp; if ( PathTrace( ent, aas, tmpStart, curEnd, stopEvent, trace, path ) ) { return true; } // if not moved any further than without stepping up, or if not on a floor surface if ( (lastEnd - start).LengthSqr() > (trace.endPos - start).LengthSqr() - 0.1f || ( trace.normal * invGravityDir ) < minFloorCos ) { if ( stopEvent & SE_BLOCKED ) { path.endPos = lastEnd; path.endEvent = SE_BLOCKED; if ( ai_debugMove.GetBool() ) { gameRenderWorld->DebugLine( colorRed, lineStart, lastEnd ); } return true; } curStart = lastEnd; break; } } path.endNormal = trace.normal; path.blockingEntity = trace.blockingEntity; // if the trace is not blocked or blocked by a floor surface if ( trace.fraction >= 1.0f || ( trace.normal * invGravityDir ) > minFloorCos ) { curStart = trace.endPos; break; } // save last result lastEnd = trace.endPos; // step up stepUp = invGravityDir * maxStepHeight; if ( PathTrace( ent, aas, curStart, curStart + stepUp, stopEvent, trace, path ) ) { return true; } stepUp *= trace.fraction; curStart = trace.endPos; } if ( ai_debugMove.GetBool() ) { gameRenderWorld->DebugLine( colorRed, lineStart, curStart ); } if ( trace.fraction >= 1.0f ) { break; } delta.ProjectOntoPlane( trace.normal, OVERCLIP ); curVelocity.ProjectOntoPlane( trace.normal, OVERCLIP ); if ( stopEvent & SE_BLOCKED ) { // if going backwards if ( (curVelocity - gravityDir * curVelocity * gravityDir ) * (velocity - gravityDir * velocity * gravityDir) < 0.0f ) { path.endPos = curStart; path.endEvent = SE_BLOCKED; return true; } } } if ( j >= MAX_FRAME_SLIDE ) { if ( stopEvent & SE_BLOCKED ) { path.endPos = curStart; path.endEvent = SE_BLOCKED; return true; } } // add gravity curVelocity += gravity * frameTime * 0.001f; } path.endTime = totalTime; path.endVelocity = curVelocity; path.endPos = curStart; path.endEvent = 0; return false; }
END_CLASS /* ================ idTrigger::DrawDebugInfo ================ */ void idTrigger::DrawDebugInfo() { idMat3 axis = gameLocal.GetLocalPlayer()->viewAngles.ToMat3(); idVec3 up = axis[ 2 ] * 5.0f; idBounds viewTextBounds( gameLocal.GetLocalPlayer()->GetPhysics()->GetOrigin() ); idBounds viewBounds( gameLocal.GetLocalPlayer()->GetPhysics()->GetOrigin() ); idBounds box( idVec3( -4.0f, -4.0f, -4.0f ), idVec3( 4.0f, 4.0f, 4.0f ) ); idEntity* ent; idEntity* target; int i; bool show; const function_t* func; viewTextBounds.ExpandSelf( 128.0f ); viewBounds.ExpandSelf( 512.0f ); for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { if( ent->GetPhysics()->GetContents() & ( CONTENTS_TRIGGER | CONTENTS_FLASHLIGHT_TRIGGER ) ) { show = viewBounds.IntersectsBounds( ent->GetPhysics()->GetAbsBounds() ); if( !show ) { for( i = 0; i < ent->targets.Num(); i++ ) { target = ent->targets[ i ].GetEntity(); if( target != NULL && viewBounds.IntersectsBounds( target->GetPhysics()->GetAbsBounds() ) ) { show = true; break; } } } if( !show ) { continue; } gameRenderWorld->DebugBounds( colorOrange, ent->GetPhysics()->GetAbsBounds() ); if( viewTextBounds.IntersectsBounds( ent->GetPhysics()->GetAbsBounds() ) ) { gameRenderWorld->DrawText( ent->name.c_str(), ent->GetPhysics()->GetAbsBounds().GetCenter(), 0.1f, colorWhite, axis, 1 ); gameRenderWorld->DrawText( ent->GetEntityDefName(), ent->GetPhysics()->GetAbsBounds().GetCenter() + up, 0.1f, colorWhite, axis, 1 ); if( ent->IsType( idTrigger::Type ) ) { func = static_cast<idTrigger*>( ent )->GetScriptFunction(); } else { func = NULL; } if( func ) { gameRenderWorld->DrawText( va( "call script '%s'", func->Name() ), ent->GetPhysics()->GetAbsBounds().GetCenter() - up, 0.1f, colorWhite, axis, 1 ); } } for( i = 0; i < ent->targets.Num(); i++ ) { target = ent->targets[ i ].GetEntity(); if( target ) { gameRenderWorld->DebugArrow( colorYellow, ent->GetPhysics()->GetAbsBounds().GetCenter(), target->GetPhysics()->GetOrigin(), 10, 0 ); gameRenderWorld->DebugBounds( colorGreen, box, target->GetPhysics()->GetOrigin() ); if( viewTextBounds.IntersectsBounds( target->GetPhysics()->GetAbsBounds() ) ) { gameRenderWorld->DrawText( target->name.c_str(), target->GetPhysics()->GetAbsBounds().GetCenter(), 0.1f, colorWhite, axis, 1 ); } } } } } }
void sdClientAnimated::Event_GetColor() { sdProgram::ReturnVector( idVec3( renderEntity.shaderParms[ SHADERPARM_RED ], renderEntity.shaderParms[ SHADERPARM_GREEN ], renderEntity.shaderParms[ SHADERPARM_BLUE ] ) ); }
/* =============== idBotAI::BotAI_CheckThinkState Should the bot think this frame? =============== */ bool idBotAI::BotAI_CheckThinkState() { if ( botWorld->gameLocalInfo.botsPaused ) { return false; } if ( botWorld->gameLocalInfo.botsSleep ) { return false; } if ( botInfo->team == NOTEAM || botInfo->classType == NOCLASS ) { if ( botThreadData.AllowDebugData() ) { // assert( false ); } return false; } if ( botThreadData.AllowDebugData() ) { if ( bot_skipThinkClient.GetInteger() == botNum ) { botUcmd->botCmds.godMode = true; return false; } if ( bot_godMode.GetInteger() == botNum ) { botUcmd->botCmds.godMode = true; } } if ( botInfo->isTeleporting ) { return false; } if ( botAAS.aas == NULL ) { if ( botThreadData.random.RandomInt( 100 ) > 98 ) { botUcmd->botCmds.hasNoGoals = true; //mal: pass a warning to the player to let them know we have no goals } return false; } if ( !botInfo->hasGroundContact && botInfo->invulnerableEndTime > botWorld->gameLocalInfo.time ) { return false; } //mal: dont start thinking until we've actually touched the ground after spawning in. if ( botInfo->justSpawned ) { UpdateBotsInformation( botInfo->classType, botInfo->team, false ); return false; //mal: dont think til we have some team/class stuff to think about! } if ( botWorld->gameLocalInfo.debugBotWeapons ) { botUcmd->botCmds.lookDown = true; botUcmd->idealWeaponSlot = GUN; botUcmd->botCmds.attack = true; if ( !botInfo->weapInfo.primaryWeapHasAmmo && !botInfo->weapInfo.sideArmHasAmmo && !botInfo->justSpawned ) { botUcmd->botCmds.suicide = true; } Bot_MoveToGoal( botInfo->origin + idVec3( 5.0f, 5.0f, 0.0f ), vec3_zero, RUN, ( botThreadData.random.RandomInt( 100 ) > 80 ) ? RANDOM_JUMP : NULLMOVETYPE ); return false; } if ( botInfo->resetState > 0 ) { ResetBotsAI( ( botInfo->resetState == MINOR_RESET_EVENT ) ? false : true ); return false; } return true; }
// Gets called each time the mind is thinking void CombatState::Think(idAI* owner) { // Do we have an expiry date? if (_endTime > 0) { if (gameLocal.time >= _endTime) { owner->GetMind()->EndState(); } return; } // Ensure we are in the correct alert level if (!CheckAlertLevel(owner)) { // owner->GetMind()->EndState(); // grayman #3182 - already done in CheckAlertLevel() return; } // grayman #3331 - make sure you're still fighting the same enemy. // grayman #3355 - fight the closest enemy idActor* enemy = _enemy.GetEntity(); idActor* newEnemy = owner->GetEnemy(); if ( enemy ) { if ( newEnemy && ( newEnemy != enemy ) ) { idVec3 ownerOrigin = owner->GetPhysics()->GetOrigin(); float dist2EnemySqr = ( enemy->GetPhysics()->GetOrigin() - ownerOrigin ).LengthSqr(); float dist2NewEnemySqr = ( newEnemy->GetPhysics()->GetOrigin() - ownerOrigin ).LengthSqr(); if ( dist2NewEnemySqr < dist2EnemySqr ) { owner->GetMind()->EndState(); return; // state has ended } } } else { enemy = newEnemy; } if (!CheckEnemyStatus(enemy, owner)) { owner->GetMind()->EndState(); return; // state has ended } // grayman #3520 - don't look toward new alerts if ( owner->m_lookAtAlertSpot ) { owner->m_lookAtAlertSpot = false; owner->m_lookAtPos = idVec3(idMath::INFINITY,idMath::INFINITY,idMath::INFINITY); } // angua: look at enemy owner->Event_LookAtPosition(enemy->GetEyePosition(), gameLocal.msec); Memory& memory = owner->GetMemory(); idVec3 vec2Enemy = enemy->GetPhysics()->GetOrigin() - owner->GetPhysics()->GetOrigin(); float dist2Enemy = vec2Enemy.LengthFast(); // grayman #3331 - need to take vertical separation into account. It's possible to have the origins // close enough to be w/in the melee zone, but still be unable to hit the enemy. bool inMeleeRange = ( dist2Enemy <= ( 3 * owner->GetMeleeRange() ) ); ECombatType newCombatType; if ( inMeleeRange && !_meleePossible ) // grayman #3355 - can't fight up close { owner->fleeingEvent = false; // grayman #3356 owner->emitFleeBarks = false; // grayman #3474 owner->GetMind()->SwitchState(STATE_FLEE); return; } if ( !inMeleeRange && _rangedPossible ) { newCombatType = COMBAT_RANGED; } else { newCombatType = COMBAT_MELEE; } // Check for situation where you're in the melee zone, yet you're unable to hit // the enemy. This can happen if the enemy is above or below you and you can't // reach them. switch(_combatSubState) { case EStateReaction: { if ( gameLocal.time < _reactionEndTime ) { return; // stay in this state until the reaction time expires } // Check to see if the enemy is still visible. // grayman #2816 - Visibility doesn't matter if you're in combat because // you bumped into your enemy. idEntity* tactEnt = owner->GetTactEnt(); if ( ( tactEnt == NULL ) || !tactEnt->IsType(idActor::Type) || ( tactEnt != enemy ) || !owner->AI_TACTALERT ) { if ( !owner->CanSee(enemy, true) ) { owner->ClearEnemy(); owner->SetAlertLevel(owner->thresh_5 - 0.1); // reset alert level just under Combat owner->GetMind()->EndState(); return; } } owner->m_ignorePlayer = false; // grayman #3063 - clear flag that prevents mission statistics on player sightings // The AI has processed his reaction, and needs to move into combat, or flee. _criticalHealth = owner->spawnArgs.GetInt("health_critical", "0"); // greebo: Check for weapons and flee if ... if ( ( !_meleePossible && !_rangedPossible ) || // ... I'm unarmed ( owner->spawnArgs.GetBool("is_civilian", "0")) || // ... I'm a civilian, and don't fight ( owner->health < _criticalHealth ) ) // grayman #3140 ... I'm very damaged and can't afford to engage in combat { owner->fleeingEvent = false; // grayman #3356 owner->emitFleeBarks = true; // grayman #3474 owner->GetMind()->SwitchState(STATE_FLEE); return; } memory.stopRelight = true; // grayman #2603 - abort a relight in progress memory.stopExaminingRope = true; // grayman #2872 - stop examining a rope memory.stopReactingToHit = true; // grayman #2816 _combatSubState = EStateDoOnce; break; } case EStateDoOnce: { // Check for sitting or sleeping moveType_t moveType = owner->GetMoveType(); if ( moveType == MOVETYPE_SIT || moveType == MOVETYPE_SLEEP || moveType == MOVETYPE_SIT_DOWN || moveType == MOVETYPE_LAY_DOWN ) { owner->GetUp(); // okay if called multiple times return; } // grayman #3009 - check for getting up from sitting or sleeping if ( moveType == MOVETYPE_GET_UP || moveType == MOVETYPE_GET_UP_FROM_LYING ) { return; } // not sitting or sleeping at this point // Stop what you're doing owner->StopMove(MOVE_STATUS_DONE); owner->movementSubsystem->ClearTasks(); owner->senseSubsystem->ClearTasks(); owner->actionSubsystem->ClearTasks(); // Bark // This will hold the message to be delivered with the bark, if appropriate CommMessagePtr message; // Only alert the bystanders if we didn't receive the alert by message ourselves if (!memory.alertedDueToCommunication) { message = CommMessagePtr(new CommMessage( CommMessage::DetectedEnemy_CommType, owner, NULL, // from this AI to anyone enemy, memory.lastEnemyPos, 0 )); } // grayman #3496 - This is an alert bark, but it's not subject to the delay // between alert barks because the previous bark will end before we get here. // It's also the culmination of a journey up from Idle State, and if the AI is // going to go after an enemy, this bark needs to be heard. // We'll still reset the last alert bark time, though. // The communication system plays starting bark // grayman #3343 - accommodate different barks for human and non-human enemies idPlayer* player(NULL); if (enemy->IsType(idPlayer::Type)) { player = static_cast<idPlayer*>(enemy); } idStr bark = ""; if (player && player->m_bShoulderingBody) { bark = "snd_spotted_player_with_body"; } else if ((MS2SEC(gameLocal.time - memory.lastTimeFriendlyAISeen)) <= MAX_FRIEND_SIGHTING_SECONDS_FOR_ACCOMPANIED_ALERT_BARK) { idStr enemyAiUse = enemy->spawnArgs.GetString("AIUse"); if ( ( enemyAiUse == AIUSE_MONSTER ) || ( enemyAiUse == AIUSE_UNDEAD ) ) { bark = "snd_to_combat_company_monster"; } else { bark = "snd_to_combat_company"; } } else { idStr enemyAiUse = enemy->spawnArgs.GetString("AIUse"); if ( ( enemyAiUse == AIUSE_MONSTER ) || ( enemyAiUse == AIUSE_UNDEAD ) ) { bark = "snd_to_combat_monster"; } else { bark = "snd_to_combat"; } } owner->commSubsystem->AddCommTask(CommunicationTaskPtr(new SingleBarkTask(bark, message))); owner->GetMemory().lastTimeAlertBark = gameLocal.time; // grayman #3496 if (cv_ai_debug_transition_barks.GetBool()) { gameLocal.Printf("%d: %s starts combat, barks '%s'\n",gameLocal.time,owner->GetName(),bark.c_str()); } _justDrewWeapon = false; _combatSubState = EStateCheckWeaponState; break; } case EStateCheckWeaponState: // check which combat type we should use { // Can you continue with your current combat type, and not have to switch weapons? // Check for case where melee combat has stalled. You're in the melee zone, but you're // unable to hit the enemy. Perhaps he's higher or lower than you and you can't reach him. if ( !owner->AI_FORWARD && // not moving ( _combatType == COMBAT_MELEE ) && // in melee combat _rangedPossible && // ranged combat is possible !owner->TestMelee() ) // I can't hit the enemy { float orgZ = owner->GetPhysics()->GetOrigin().z; float height = owner->GetPhysics()->GetBounds().GetSize().z; float enemyOrgZ = enemy->GetPhysics()->GetOrigin().z; float enemyHeight = enemy->GetPhysics()->GetBounds().GetSize().z; if ( ( (orgZ + height + owner->melee_range_vert) < enemyOrgZ ) || // enemy too high ( (enemyOrgZ + enemyHeight) < orgZ ) ) // enemy too low { newCombatType = COMBAT_RANGED; } } if ( newCombatType == _combatType ) { // yes - no need to run weapon-switching animations _combatSubState = EStateCombatAndChecks; return; } // Do you need to switch a melee or ranged weapon? You might already have one // drawn, or you might have none drawn, or you might have to change weapons, // or you might be using unarmed attacks, and you don't need a drawn weapon. // Check for unarmed combat. if ( _unarmedMelee && ( newCombatType == COMBAT_MELEE ) ) { // unarmed combat doesn't need attached weapons _combatType = COMBAT_NONE; // clear ranged combat tasks and start melee combat tasks _combatSubState = EStateCombatAndChecks; return; } if ( _unarmedRanged && ( newCombatType == COMBAT_RANGED ) ) { // unarmed combat doesn't need attached weapons _combatType = COMBAT_NONE; // clear melee combat tasks and start ranged combat tasks _combatSubState = EStateCombatAndChecks; return; } // Do you have a drawn weapon? if ( owner->GetAttackFlag(COMBAT_MELEE) && ( newCombatType == COMBAT_MELEE ) ) { // melee weapon is already drawn _combatSubState = EStateCombatAndChecks; return; } if ( owner->GetAttackFlag(COMBAT_RANGED) && ( newCombatType == COMBAT_RANGED ) ) { // ranged weapon is already drawn _combatSubState = EStateCombatAndChecks; return; } // At this point, we know we need to draw a weapon that's not already drawn. // See if you need to sheathe a drawn weapon. if ( ( ( newCombatType == COMBAT_RANGED ) && owner->GetAttackFlag(COMBAT_MELEE) ) || ( ( newCombatType == COMBAT_MELEE ) && owner->GetAttackFlag(COMBAT_RANGED) ) ) { // switch from one type of weapon to another owner->movementSubsystem->ClearTasks(); owner->actionSubsystem->ClearTasks(); _combatType = COMBAT_NONE; // sheathe current weapon so you can draw the other weapon owner->SheathWeapon(); _waitEndTime = gameLocal.time + 2000; // safety net _combatSubState = EStateSheathingWeapon; return; } // No need to sheathe a weapon _combatSubState = EStateDrawWeapon; break; } case EStateSheathingWeapon: { // if you're sheathing a weapon, stay in this state until it's done, or until the timer expires // grayman #3355 - check wait state if ( ( gameLocal.time < _waitEndTime ) && ( idStr(owner->WaitState()) == "sheath") ) { return; } _combatSubState = EStateDrawWeapon; break; } case EStateDrawWeapon: { // grayman #3331 - if you don't already have the correct weapon drawn, // draw a ranged weapon if you're far from the enemy, and you have a // ranged weapon, otherwise draw your melee weapon bool drawingWeapon = false; if ( !inMeleeRange ) { // beyond melee range if ( !owner->GetAttackFlag(COMBAT_RANGED) && _rangedPossible ) { owner->DrawWeapon(COMBAT_RANGED); drawingWeapon = true; } else // no ranged weapon { owner->DrawWeapon(COMBAT_MELEE); drawingWeapon = true; } } else // in melee range { if ( _meleePossible && !owner->GetAttackFlag(COMBAT_MELEE) ) { owner->DrawWeapon(COMBAT_MELEE); drawingWeapon = true; } } // grayman #3331 - if this is the first weapon draw, to make sure the weapon is drawn // before starting combat, delay some before starting to chase the enemy. // The farther away the enemy is, the better the chance that you'll start chasing before your // weapon is drawn. If he's close, this gives you time to completely draw your weapon before // engaging him. The interesting distance is how far you have to travel to get w/in melee range. if ( _needInitialDrawDelay ) // True if this is the first time through, and you don't already have a raised weapon { int delay = 0; if ( drawingWeapon ) { delay = (int)(2064.0f - 20.0f*(dist2Enemy - owner->GetMeleeRange())); if ( delay < 0 ) { delay = gameLocal.random.RandomInt(2064); } _waitEndTime = gameLocal.time + delay; _combatSubState = EStateDrawingWeapon; // grayman #3563 - safety net when drawing a weapon _drawEndTime = gameLocal.time + MAX_DRAW_DURATION; } else { _combatSubState = EStateCombatAndChecks; } _needInitialDrawDelay = false; // No need to do this again } else { if ( drawingWeapon ) { _waitEndTime = gameLocal.time; _combatSubState = EStateDrawingWeapon; // grayman #3563 - safety net when drawing a weapon _drawEndTime = gameLocal.time + MAX_DRAW_DURATION; } else { _combatSubState = EStateCombatAndChecks; } } break; } case EStateDrawingWeapon: { // grayman #3355 - check wait state if ( idStr(owner->WaitState()) == "draw" ) { if ( gameLocal.time < _drawEndTime ) // grayman #3563 - check safety net { return; // wait until weapon is drawn } } if ( gameLocal.time < _waitEndTime ) { return; // wait until timer expires } // Weapon is now drawn _justDrewWeapon = true; _combatSubState = EStateCombatAndChecks; break; } case EStateCombatAndChecks: { // Need to check if a weapon that was just drawn is correct for the zone you're now in, in case // you started drawing the correct weapon for one zone, and while it was drawing, you switched // to the other zone. if ( _justDrewWeapon ) { if ( newCombatType == COMBAT_RANGED ) { // beyond melee range if ( !owner->GetAttackFlag(COMBAT_RANGED) && _rangedPossible ) { // wrong weapon raised - go back and get the correct one _justDrewWeapon = false; _combatSubState = EStateCheckWeaponState; return; } } else // in melee combat { if ( !owner->GetAttackFlag(COMBAT_MELEE) && _meleePossible ) { // wrong weapon raised - go back and get the correct one _justDrewWeapon = false; _combatSubState = EStateCheckWeaponState; return; } } } _justDrewWeapon = false; if ( _combatType == COMBAT_NONE ) // Either combat hasn't been initially set up, or you're switching weapons { if ( newCombatType == COMBAT_RANGED ) { // Set up ranged combat owner->actionSubsystem->PushTask(RangedCombatTask::CreateInstance()); owner->movementSubsystem->PushTask(ChaseEnemyRangedTask::CreateInstance()); _combatType = COMBAT_RANGED; } else { // Set up melee combat ChaseEnemyTaskPtr chaseEnemy = ChaseEnemyTask::CreateInstance(); chaseEnemy->SetEnemy(enemy); owner->movementSubsystem->PushTask(chaseEnemy); owner->actionSubsystem->PushTask(MeleeCombatTask::CreateInstance()); _combatType = COMBAT_MELEE; } // Let the AI update their weapons (make them nonsolid) owner->UpdateAttachmentContents(false); } // Check the distance to the enemy, the subsystem tasks need it. memory.canHitEnemy = owner->CanHitEntity(enemy, _combatType); // grayman #3331 - willBeAbleToHitEnemy is only relevant if canHitEnemy is FALSE if ( owner->m_bMeleePredictProximity && !memory.canHitEnemy ) { memory.willBeAbleToHitEnemy = owner->WillBeAbleToHitEntity(enemy, _combatType); } // Check whether the enemy can hit us in the near future memory.canBeHitByEnemy = owner->CanBeHitByEntity(enemy, _combatType); if ( !owner->AI_ENEMY_VISIBLE && ( ( ( _combatType == COMBAT_MELEE ) && !memory.canHitEnemy ) || ( _combatType == COMBAT_RANGED) ) ) { // The enemy is not visible, let's keep track of him for a small amount of time if (gameLocal.time - memory.lastTimeEnemySeen < MAX_BLIND_CHASE_TIME) { // Cheat a bit and take the last reachable position as "visible & reachable" owner->lastVisibleReachableEnemyPos = owner->lastReachableEnemyPos; } else if (owner->ReachedPos(owner->lastVisibleReachableEnemyPos, MOVE_TO_POSITION) || ( ( gameLocal.time - memory.lastTimeEnemySeen ) > 2 * MAX_BLIND_CHASE_TIME) ) { // BLIND_CHASE_TIME has expired, we have lost the enemy! owner->GetMind()->SwitchState(STATE_LOST_TRACK_OF_ENEMY); return; } } // Flee if you're damaged and the current melee action is finished if ( ( owner->health < _criticalHealth ) && ( owner->m_MeleeStatus.m_ActionState == MELEEACTION_READY ) ) { DM_LOG(LC_AI, LT_INFO)LOGSTRING("I'm badly hurt, I'm afraid, and am fleeing!\r"); owner->fleeingEvent = false; // grayman #3182 owner->emitFleeBarks = true; // grayman #3474 owner->GetMind()->SwitchState(STATE_FLEE); return; } _combatSubState = EStateCheckWeaponState; break; } default: break; } }
/* ============== sdSnowPrecipitation::Update ============== */ void sdSnowPrecipitation::Update( void ) { renderEntity_t *re = GetRenderEntity(); if ( re->hModel == NULL ) { return; } idBounds modelbb = re->hModel->Bounds(); idVec3 extents = (modelbb.GetMaxs() - modelbb.GetMins()) * 0.5f; idPlayer *p = gameLocal.GetLocalViewPlayer(); idVec3 const &v = p->GetViewPos(); for (int i=0; i<MAX_GROUPS; i++) { groups[i].time -= 1.f / 30.f; if ( groups[i].time < 0.f ) { groups[i].axis = idVec3( idRandom::StaticRandom().RandomFloat()- 0.15f , idRandom::StaticRandom().RandomFloat() - 0.5f , idRandom::StaticRandom().RandomFloat() - 0.5f ); groups[i].axis.z *= 40.25f; groups[i].axis.Normalize(); groups[i].rotate = 0.f; groups[i].rotateSpeed = idRandom::StaticRandom().RandomFloat() * 50.f + 50.f; groups[i].rotationPoint = idVec3( idRandom::StaticRandom().RandomFloat() * extents.x, idRandom::StaticRandom().RandomFloat() * extents.y, idRandom::StaticRandom().RandomFloat() * extents.z ); groups[i].alpha = 0.f; groups[i].time = idRandom::StaticRandom().RandomFloat() * 1.f + 1.f; groups[i].worldPos = v + idVec3( (idRandom::StaticRandom().RandomFloat()-0.5f) * extents.x * 3, (idRandom::StaticRandom().RandomFloat()-0.5f) * extents.y * 3, (idRandom::StaticRandom().RandomFloat()-0.5f) * extents.z * 3 ); } else { if ( groups[i].time > 0.25f ) { groups[i].alpha += 1.f / 7.5f; if ( groups[i].alpha > 1.f ) { groups[i].alpha = 1.f; } } else { groups[i].alpha = groups[i].time * 4.f; if ( groups[i].alpha < 0.f ) { groups[i].alpha = 0.f; } } groups[i].worldPos += idVec3( 0.f, 0.f, -600.f ) * 1.f / 30.f; groups[i].rotate += groups[i].rotateSpeed * 1.f / 30.f; } } int gridx = idMath::Ftoi( idMath::Floor(v.x / extents.x) ); int gridy = idMath::Ftoi( idMath::Floor(v.y / extents.y) ); idBounds bounds; bounds.Clear(); sdInstInfo *inst = re->insts; for (int i=0; i<MAX_GROUPS; i++) { idRotation r( groups[i].rotationPoint, groups[i].axis, groups[i].rotate ); idBounds bb2; inst->inst.color[0] = 0xff; inst->inst.color[1] = 0xff; inst->inst.color[2] = 0xff; inst->inst.color[3] = 0xff; inst->fadeOrigin = inst->inst.origin = groups[i].worldPos; inst->inst.axis = r.ToMat3(); inst->maxVisDist = 0; inst->minVisDist = 0.f; bb2.FromTransformedBounds( modelbb, inst->inst.origin, inst->inst.axis ); bounds.AddBounds( bb2 ); inst++; } re->flags.overridenBounds = true; re->bounds = bounds; if ( renderEntityHandle == -1 ) { renderEntityHandle = gameRenderWorld->AddEntityDef( re ); } else { gameRenderWorld->UpdateEntityDef( renderEntityHandle, re ); } if ( !effect.GetRenderEffect().declEffect ) return; idVec3 viewOrg; renderView_t view; if ( sdDemoManager::GetInstance().CalculateRenderView( &view ) ) { viewOrg = view.vieworg; } else { // If we are inside don't run the bacground effect idPlayer* player = gameLocal.GetLocalViewPlayer(); if ( player == NULL ) { return; } viewOrg = player->GetRenderView()->vieworg; } int area = gameRenderWorld->PointInArea( viewOrg ); bool runEffect = false; if ( area >= 0 ) { if ( gameRenderWorld->GetAreaPortalFlags( area ) & ( 1 << PORTAL_OUTSIDE ) ) { runEffect = true && !g_skipLocalizedPrecipitation.GetBool(); } } // Update the background effect if ( runEffect ) { effect.GetRenderEffect().origin = viewOrg; if ( !effectRunning ) { effect.Start( gameLocal.time ); effectRunning = true; } else { effect.Update(); } } else { effect.StopDetach(); effectRunning = false; } }
/* ============== sdSnowEffect::Think ============== */ void sdSnowEffect::Think( void ) { renderEntity_t *re = GetRenderEntity(); if ( re->hModel == NULL ) { return; } idBounds modelbb = re->hModel->Bounds(); idVec3 extents = (modelbb.GetMaxs() - modelbb.GetMins()) * 0.5f; idPlayer *p = gameLocal.GetLocalViewPlayer(); idVec3 const &v = p->GetViewPos(); for (int i=0; i<MAX_GROUPS; i++) { groups[i].time -= 1.f / 30.f; if ( groups[i].time < 0.f ) { groups[i].axis = idVec3( idRandom::StaticRandom().RandomFloat()- 0.15f , idRandom::StaticRandom().RandomFloat() - 0.5f , idRandom::StaticRandom().RandomFloat() - 0.5f ); groups[i].axis.z *= 40.25f; groups[i].axis.Normalize(); groups[i].rotate = 0.f; groups[i].rotateSpeed = idRandom::StaticRandom().RandomFloat() * 50.f + 50.f; groups[i].rotationPoint = idVec3( idRandom::StaticRandom().RandomFloat() * extents.x, idRandom::StaticRandom().RandomFloat() * extents.y, idRandom::StaticRandom().RandomFloat() * extents.z ); groups[i].alpha = 0.f; groups[i].time = idRandom::StaticRandom().RandomFloat() * 1.f + 1.f; groups[i].worldPos = v + idVec3( (idRandom::StaticRandom().RandomFloat()-0.5f) * extents.x * 3, (idRandom::StaticRandom().RandomFloat()-0.5f) * extents.y * 3, (idRandom::StaticRandom().RandomFloat()-0.5f) * extents.z * 3 ); } else { if ( groups[i].time > 0.25f ) { groups[i].alpha += 1.f / 7.5f; if ( groups[i].alpha > 1.f ) { groups[i].alpha = 1.f; } } else { groups[i].alpha = groups[i].time * 4.f; if ( groups[i].alpha < 0.f ) { groups[i].alpha = 0.f; } } groups[i].worldPos += idVec3( 0.f, 0.f, -600.f ) * 1.f / 30.f; groups[i].rotate += groups[i].rotateSpeed * 1.f / 30.f; } } int gridx = idMath::Ftoi( idMath::Floor(v.x / extents.x) ); int gridy = idMath::Ftoi( idMath::Floor(v.y / extents.y) ); idBounds bounds; bounds.Clear(); sdInstInfo *inst = re->insts; for (int i=0; i<MAX_GROUPS; i++) { idRotation r( groups[i].rotationPoint, groups[i].axis, groups[i].rotate ); idBounds bb2; inst->inst.color[0] = 0xff; inst->inst.color[1] = 0xff; inst->inst.color[2] = 0xff; inst->inst.color[3] = 0xff; inst->fadeOrigin = inst->inst.origin = groups[i].worldPos; inst->inst.axis = r.ToMat3(); inst->maxVisDist = 0; inst->minVisDist = 0.f; bb2.FromTransformedBounds( modelbb, inst->inst.origin, inst->inst.axis ); bounds.AddBounds( bb2 ); inst++; } re->flags.overridenBounds = true; re->bounds = bounds; UpdateVisuals(); Present(); }
bool sdTransportPositionManager::EjectPlayer( sdVehiclePosition& position, bool force ) { idPlayer* player = position.GetPlayer(); if ( !player ) { return true; } // // Find a position to eject to // bool foundOrg = false; idVec3 selectedOrg = player->GetPhysics()->GetOrigin(); idMat3 selectedAxes = player->GetPhysics()->GetAxis(); if ( transport->UnbindOnEject() ) { if( !gameLocal.isClient ) { player->DisableClip( false ); sdTeleporter* teleportEnt = transport->GetTeleportEntity(); if ( teleportEnt != NULL ) { teleportEnt->GetTeleportEndPoint( transport, selectedOrg, selectedAxes ); selectedOrg.z += 64.f; foundOrg = true; } else { // prioritize exit joints by the nearest idStaticList< sdExitJointDistanceInfo, MAX_EXIT_JOINTS > sortedExitJoints; sortedExitJoints.SetNum( exitJoints.Num() ); idVec3 traceFromPoint; transport->GetWorldOrigin( position.GetAttachJoint(), traceFromPoint ); for( int i = 0; i < exitJoints.Num(); i++ ) { sortedExitJoints[ i ].joint = exitJoints[ i ]; transport->GetWorldOriginAxis( exitJoints[ i ], sortedExitJoints[ i ].origin, sortedExitJoints[ i ].axis ); sortedExitJoints[ i ].distanceSqr = ( traceFromPoint - sortedExitJoints[ i ].origin ).LengthSqr(); } sortedExitJoints.Sort( sdExitJointDistanceInfo::SortByDistance ); // choose a point to do the cast-to-exit-point from - if we just use the origin it could // potentially be in all sorts of wacky positions depending how the vehicle is built // this enures the the point casted from is inside the vehicle traceFromPoint = transport->GetPhysics()->GetAxis().TransposeMultiply( traceFromPoint - transport->GetPhysics()->GetOrigin() ); const idBounds& transportBounds = transport->GetPhysics()->GetBounds(); traceFromPoint.z = ( transportBounds[ 0 ].z + transportBounds[ 1 ].z ) * 0.5f; traceFromPoint = traceFromPoint * transport->GetPhysics()->GetAxis() + transport->GetPhysics()->GetOrigin(); // default position to get out is inside the vehicle selectedOrg = traceFromPoint; selectedAxes; const idClipModel* playerClip = player->GetPlayerPhysics().GetNormalClipModel(); for ( int i = 0; i < sortedExitJoints.Num(); i++ ) { idVec3 org = sortedExitJoints[ i ].origin; idMat3 axes = sortedExitJoints[ i ].axis; if ( gameRenderWorld->PointInArea( org ) == -1 ) { // outside the map, so no go continue; } // check that the point is clear int contents = gameLocal.clip.Contents( CLIP_DEBUG_PARMS org, playerClip, mat3_identity, MASK_PLAYERSOLID, NULL ); if( !contents ) { // check that theres nothing in between the vehicle and the exit point trace_t trace; if( !gameLocal.clip.TracePoint( CLIP_DEBUG_PARMS trace, traceFromPoint, org, MASK_PLAYERSOLID, transport ) ) { selectedOrg = org; selectedAxes = axes; foundOrg = true; break; } } } if( !foundOrg ) { // Search all 8 positions around every exit joint, should find at least one. for ( int i = 0; i < sortedExitJoints.Num(); i++ ) { idVec3 orgBase = sortedExitJoints[ i ].origin; idMat3 axes = sortedExitJoints[ i ].axis; const int size = playerClip->GetBounds().GetSize().x; const int spacing = 8; for ( int j = -1; j < 2 && !foundOrg; j++ ) { for ( int k = -1; k < 2 && !foundOrg; k++ ) { if ( j == 0 && k == 0 ) { continue; } idVec3 org = orgBase + idVec3( j * size + j * spacing, k * size + k * spacing, 0.0f ); if ( gameRenderWorld->PointInArea( org ) == -1 ) { // outside the map, so no go continue; } // check that the point is clear int contents = gameLocal.clip.Contents( CLIP_DEBUG_PARMS org, playerClip, mat3_identity, MASK_PLAYERSOLID, NULL ); if( !contents ) { // check that theres nothing in between the vehicle and the exit point trace_t trace; if( !gameLocal.clip.TracePoint( CLIP_DEBUG_PARMS trace, traceFromPoint, org, MASK_PLAYERSOLID, transport ) ) { selectedOrg = org; selectedAxes = axes; foundOrg = true; } } } } } } } } } if ( !gameLocal.isClient ) { if ( !foundOrg ) { if ( !force ) { return false; } else { gameLocal.Warning( "sdTransportPositionManager::EjectPlayer No Valid Eject Position Found" ); } } } // // Actually eject // player->SetSuppressPredictionReset( true ); RemovePlayer( position ); if ( transport->UnbindOnEject() ) { // copy the velocity over idVec3 v = transport->GetPhysics()->GetLinearVelocity(); for ( int i = 0; i < 3; i++ ) { if ( FLOAT_IS_NAN( v[ i ] ) ) { v[ i ] = 0.f; } } v.FixDenormals(); player->GetPhysics()->SetLinearVelocity( v ); // set the position if ( foundOrg ) { idAngles temp; temp = selectedAxes.ToAngles(); temp.roll = 0.0f; if ( temp.pitch < -10.0f ) { temp.pitch = -10.0f; } player->SetViewAngles( temp ); player->SetOrigin( selectedOrg ); } player->EnableClip(); } player->SetProxyEntity( NULL, 0 ); // this forces the reset message to be re-sent player->SetSuppressPredictionReset( false ); player->ResetPredictionErrorDecay(); return true; }
/* =============== idRenderModelSprite::InstantiateDynamicModel =============== */ idRenderModel *idRenderModelSprite::InstantiateDynamicModel(const struct renderEntity_s *renderEntity, const struct viewDef_s *viewDef, idRenderModel *cachedModel) { idRenderModelStatic *staticModel; srfTriangles_t *tri; modelSurface_t surf; if (cachedModel && !r_useCachedDynamicModels.GetBool()) { delete cachedModel; cachedModel = NULL; } if (renderEntity == NULL || viewDef == NULL) { delete cachedModel; return NULL; } if (cachedModel != NULL) { assert(dynamic_cast<idRenderModelStatic *>(cachedModel) != NULL); assert(idStr::Icmp(cachedModel->Name(), sprite_SnapshotName) == 0); staticModel = static_cast<idRenderModelStatic *>(cachedModel); surf = *staticModel->Surface(0); tri = surf.geometry; } else { staticModel = new idRenderModelStatic; staticModel->InitEmpty(sprite_SnapshotName); tri = R_AllocStaticTriSurf(); R_AllocStaticTriSurfVerts(tri, 4); R_AllocStaticTriSurfIndexes(tri, 6); tri->verts[ 0 ].Clear(); tri->verts[ 0 ].normal.Set(1.0f, 0.0f, 0.0f); tri->verts[ 0 ].tangents[0].Set(0.0f, 1.0f, 0.0f); tri->verts[ 0 ].tangents[1].Set(0.0f, 0.0f, 1.0f); tri->verts[ 0 ].st[ 0 ] = 0.0f; tri->verts[ 0 ].st[ 1 ] = 0.0f; tri->verts[ 1 ].Clear(); tri->verts[ 1 ].normal.Set(1.0f, 0.0f, 0.0f); tri->verts[ 1 ].tangents[0].Set(0.0f, 1.0f, 0.0f); tri->verts[ 1 ].tangents[1].Set(0.0f, 0.0f, 1.0f); tri->verts[ 1 ].st[ 0 ] = 1.0f; tri->verts[ 1 ].st[ 1 ] = 0.0f; tri->verts[ 2 ].Clear(); tri->verts[ 2 ].normal.Set(1.0f, 0.0f, 0.0f); tri->verts[ 2 ].tangents[0].Set(0.0f, 1.0f, 0.0f); tri->verts[ 2 ].tangents[1].Set(0.0f, 0.0f, 1.0f); tri->verts[ 2 ].st[ 0 ] = 1.0f; tri->verts[ 2 ].st[ 1 ] = 1.0f; tri->verts[ 3 ].Clear(); tri->verts[ 3 ].normal.Set(1.0f, 0.0f, 0.0f); tri->verts[ 3 ].tangents[0].Set(0.0f, 1.0f, 0.0f); tri->verts[ 3 ].tangents[1].Set(0.0f, 0.0f, 1.0f); tri->verts[ 3 ].st[ 0 ] = 0.0f; tri->verts[ 3 ].st[ 1 ] = 1.0f; tri->indexes[ 0 ] = 0; tri->indexes[ 1 ] = 1; tri->indexes[ 2 ] = 3; tri->indexes[ 3 ] = 1; tri->indexes[ 4 ] = 2; tri->indexes[ 5 ] = 3; tri->numVerts = 4; tri->numIndexes = 6; surf.geometry = tri; surf.id = 0; surf.shader = tr.defaultMaterial; staticModel->AddSurface(surf); } int red = idMath::FtoiFast(renderEntity->shaderParms[ SHADERPARM_RED ] * 255.0f); int green = idMath::FtoiFast(renderEntity->shaderParms[ SHADERPARM_GREEN ] * 255.0f); int blue = idMath::FtoiFast(renderEntity->shaderParms[ SHADERPARM_BLUE ] * 255.0f); int alpha = idMath::FtoiFast(renderEntity->shaderParms[ SHADERPARM_ALPHA ] * 255.0f); idVec3 right = idVec3(0.0f, renderEntity->shaderParms[ SHADERPARM_SPRITE_WIDTH ] * 0.5f, 0.0f); idVec3 up = idVec3(0.0f, 0.0f, renderEntity->shaderParms[ SHADERPARM_SPRITE_HEIGHT ] * 0.5f); tri->verts[ 0 ].xyz = up + right; tri->verts[ 0 ].color[ 0 ] = red; tri->verts[ 0 ].color[ 1 ] = green; tri->verts[ 0 ].color[ 2 ] = blue; tri->verts[ 0 ].color[ 3 ] = alpha; tri->verts[ 1 ].xyz = up - right; tri->verts[ 1 ].color[ 0 ] = red; tri->verts[ 1 ].color[ 1 ] = green; tri->verts[ 1 ].color[ 2 ] = blue; tri->verts[ 1 ].color[ 3 ] = alpha; tri->verts[ 2 ].xyz = - right - up; tri->verts[ 2 ].color[ 0 ] = red; tri->verts[ 2 ].color[ 1 ] = green; tri->verts[ 2 ].color[ 2 ] = blue; tri->verts[ 2 ].color[ 3 ] = alpha; tri->verts[ 3 ].xyz = right - up; tri->verts[ 3 ].color[ 0 ] = red; tri->verts[ 3 ].color[ 1 ] = green; tri->verts[ 3 ].color[ 2 ] = blue; tri->verts[ 3 ].color[ 3 ] = alpha; R_BoundTriSurf(tri); staticModel->bounds = tri->bounds; return staticModel; }
/* ======================== idSoundWorldLocal::Update ======================== */ void idSoundWorldLocal::Update() { if ( s_noSound.GetBool() ) { return; } // ------------------ // Update emitters // // Only loop through the list once to avoid extra cache misses // ------------------ // The naming convention is weird here because we reuse the name "channel" // An idSoundChannel is a channel on an emitter, which may have an explicit channel assignment or SND_CHANNEL_ANY // A hardware channel is a channel from the sound file itself (IE: left, right, LFE) // We only allow MAX_HARDWARE_CHANNELS channels, which may wind up being a smaller number of idSoundChannels idStaticList< idActiveChannel, MAX_HARDWARE_VOICES > activeEmitterChannels; int maxEmitterChannels = s_maxEmitterChannels.GetInteger() + 1; // +1 to leave room for insert-before-sort if ( maxEmitterChannels > MAX_HARDWARE_VOICES ) { maxEmitterChannels = MAX_HARDWARE_VOICES; } int activeHardwareChannels = 0; int totalHardwareChannels = 0; int totalEmitterChannels = 0; int currentTime = GetSoundTime(); for ( int e = emitters.Num() - 1; e >= 0; e-- ) { // check for freeing a one-shot emitter that is finished playing if ( emitters[e]->CheckForCompletion( currentTime ) ) { // do a fast list collapse by swapping the last element into // the slot we are deleting emitters[e]->Reset(); emitterAllocator.Free( emitters[e] ); int lastEmitter = emitters.Num() - 1; if ( e != lastEmitter ) { emitters[e] = emitters[lastEmitter]; emitters[e]->index = e; } emitters.SetNum( lastEmitter ); continue; } emitters[e]->Update( currentTime ); totalEmitterChannels += emitters[e]->channels.Num(); // sort the active channels into the hardware list for ( int i = 0; i < emitters[e]->channels.Num(); i++ ) { idSoundChannel * channel = emitters[e]->channels[i]; // check if this channel contributes at all const bool canMute = channel->CanMute(); if ( canMute && channel->volumeDB <= DB_SILENCE ) { channel->Mute(); continue; } // Calculate the sort key. // VO can't be stopped and restarted accurately, so always keep VO channels by adding a large value to the sort key. const int sortKey = idMath::Ftoi( channel->volumeDB * 100.0f + ( canMute ? 0.0f : 100000.0f ) ); // Keep track of the total number of hardware channels. // This is done after calculating the sort key to avoid a load-hit-store that // would occur when using the sort key in the loop below after the Ftoi above. const int sampleChannels = channel->leadinSample->NumChannels(); totalHardwareChannels += sampleChannels; // Find the location to insert this channel based on the sort key. int insertIndex = 0; for ( insertIndex = 0; insertIndex < activeEmitterChannels.Num(); insertIndex++ ) { if ( sortKey > activeEmitterChannels[insertIndex].sortKey ) { break; } } // Only insert at the end if there is room. if ( insertIndex == activeEmitterChannels.Num() ) { // Always leave one spot free in the 'activeEmitterChannels' so there is room to insert sort a potentially louder sound later. if ( activeEmitterChannels.Num() + 1 >= activeEmitterChannels.Max() || activeHardwareChannels + sampleChannels > MAX_HARDWARE_CHANNELS ) { // We don't have enough voices to play this, so mute it if it was playing. channel->Mute(); continue; } } // We want to insert the sound at this point. activeEmitterChannels.Insert( idActiveChannel( channel, sortKey ), insertIndex ); activeHardwareChannels += sampleChannels; // If we are over our voice limit or at our channel limit, mute sounds until it fits. // If activeEmitterChannels is full, always remove the last one so there is room to insert sort a potentially louder sound later. while ( activeEmitterChannels.Num() == maxEmitterChannels || activeHardwareChannels > MAX_HARDWARE_CHANNELS ) { const int indexToRemove = activeEmitterChannels.Num() - 1; idSoundChannel * const channelToMute = activeEmitterChannels[ indexToRemove ].channel; channelToMute->Mute(); activeHardwareChannels -= channelToMute->leadinSample->NumChannels(); activeEmitterChannels.RemoveIndex( indexToRemove ); } } } const float secondsPerFrame = 1.0f / com_engineHz_latched; // ------------------ // In the very common case of having more sounds that would contribute to the // mix than there are available hardware voices, it can be an audible discontinuity // when a channel initially gets a voice or loses a voice. // To avoid this, make sure that the last few hardware voices are mixed with a volume // of zero, so they won't make a difference as they come and go. // It isn't obvious what the exact best volume ramping method should be, just that // it smoothly change frame to frame. // ------------------ const int uncushionedChannels = maxEmitterChannels - s_cushionFadeChannels.GetInteger(); currentCushionDB = AdjustForCushionChannels( activeEmitterChannels, uncushionedChannels, currentCushionDB, s_cushionFadeRate.GetFloat() * secondsPerFrame ); // ------------------ // Update Hardware // ------------------ shakeAmp = 0.0f; idStr showVoiceTable; bool showVoices = s_showVoices.GetBool(); if ( showVoices ) { showVoiceTable.Format( "currentCushionDB: %5.1f freeVoices: %i zombieVoices: %i buffers:%i/%i\n", currentCushionDB, soundSystemLocal.hardware.GetNumFreeVoices(), soundSystemLocal.hardware.GetNumZombieVoices(), soundSystemLocal.activeStreamBufferContexts.Num(), soundSystemLocal.freeStreamBufferContexts.Num() ); } for ( int i = 0; i < activeEmitterChannels.Num(); i++ ) { idSoundChannel * chan = activeEmitterChannels[i].channel; chan->UpdateHardware( 0.0f, currentTime ); if ( showVoices ) { idStr voiceLine; voiceLine.Format( "%5.1f db [%3i:%2i] %s", chan->volumeDB, chan->emitter->index, chan->logicalChannel, chan->CanMute() ? "" : " <CANT MUTE>\n" ); idSoundSample * leadinSample = chan->leadinSample; idSoundSample * loopingSample = chan->loopingSample; if ( loopingSample == NULL ) { voiceLine.Append( va( "%ikhz*%i %s\n", leadinSample->SampleRate()/1000, leadinSample->NumChannels(), leadinSample->GetName() ) ); } else if ( loopingSample == leadinSample ) { voiceLine.Append( va( "%ikhz*%i <LOOPING> %s\n", leadinSample->SampleRate()/1000, leadinSample->NumChannels(), leadinSample->GetName() ) ); } else { voiceLine.Append( va( "%ikhz*%i %s | %ikhz*%i %s\n", leadinSample->SampleRate()/1000, leadinSample->NumChannels(), leadinSample->GetName(), loopingSample->SampleRate()/1000, loopingSample->NumChannels(), loopingSample->GetName() ) ); } showVoiceTable += voiceLine; } // Calculate shakes if ( chan->hardwareVoice == NULL ) { continue; } shakeAmp += chan->parms.shakes * chan->hardwareVoice->GetGain() * chan->currentAmplitude; } if ( showVoices ) { static idOverlayHandle handle; console->PrintOverlay( handle, JUSTIFY_LEFT, showVoiceTable.c_str() ); } if ( s_drawSounds.GetBool() && renderWorld != NULL ) { for ( int e = 0; e < emitters.Num(); e++ ) { idSoundEmitterLocal * emitter = emitters[e]; bool audible = false; float maxGain = 0.0f; for ( int c = 0; c < emitter->channels.Num(); c++ ) { if ( emitter->channels[c]->hardwareVoice != NULL ) { audible = true; maxGain = Max( maxGain, emitter->channels[c]->hardwareVoice->GetGain() ); } } if ( !audible ) { continue; } static const int lifetime = 20; idBounds ref; ref.Clear(); ref.AddPoint( idVec3( -10.0f ) ); ref.AddPoint( idVec3( 10.0f ) ); // draw a box renderWorld->DebugBounds( idVec4( maxGain, maxGain, 1.0f, 1.0f ), ref, emitter->origin, lifetime ); if ( emitter->origin != emitter->spatializedOrigin ) { renderWorld->DebugLine( idVec4( 1.0f, 0.0f, 0.0f, 1.0f ), emitter->origin, emitter->spatializedOrigin, lifetime ); } // draw the index idVec3 textPos = emitter->origin; textPos.z -= 8; renderWorld->DrawText( va("%i", e), textPos, 0.1f, idVec4(1,0,0,1), listener.axis, 1, lifetime ); textPos.z += 8; // run through all the channels for ( int k = 0; k < emitter->channels.Num(); k++ ) { idSoundChannel * chan = emitter->channels[k]; float min = chan->parms.minDistance; float max = chan->parms.maxDistance; const char * defaulted = chan->leadinSample->IsDefault() ? " *DEFAULTED*" : ""; idStr text; text.Format( "%s (%i %i/%i)%s", chan->soundShader->GetName(), idMath::Ftoi( emitter->spatializedDistance ), idMath::Ftoi( min ), idMath::Ftoi( max ), defaulted ); renderWorld->DrawText( text, textPos, 0.1f, idVec4(1,0,0,1), listener.axis, 1, lifetime ); textPos.z += 8; } } } }
/* ================ idAF::AddBindConstraints ================ */ void idAF::AddBindConstraints( void ) { const idKeyValue *kv; idStr name; idAFBody *body; idLexer lexer; idToken type, bodyName, jointName; idVec3 origin, renderOrigin; idMat3 axis, renderAxis; if ( !IsLoaded() ) { return; } const idDict &args = self->spawnArgs; // get the render position origin = physicsObj.GetOrigin( 0 ); axis = physicsObj.GetAxis( 0 ); renderAxis = baseAxis.Transpose() * axis; renderOrigin = origin - baseOrigin * renderAxis; // parse all the bind constraints for ( kv = args.MatchPrefix( "bindConstraint ", NULL ); kv; kv = args.MatchPrefix( "bindConstraint ", kv ) ) { name = kv->GetKey(); name.Strip( "bindConstraint " ); lexer.LoadMemory( kv->GetValue(), kv->GetValue().Length(), kv->GetKey() ); lexer.ReadToken( &type ); lexer.ReadToken( &bodyName ); body = physicsObj.GetBody( bodyName ); if ( !body ) { gameLocal.Warning( "idAF::AddBindConstraints: body '%s' not found on entity '%s'", bodyName.c_str(), self->name.c_str() ); lexer.FreeSource(); continue; } if ( type.Icmp( "fixed" ) == 0 ) { idAFConstraint_Fixed *c; c = new idAFConstraint_Fixed( name, body, NULL ); physicsObj.AddConstraint( c ); } else if ( type.Icmp( "ballAndSocket" ) == 0 ) { idAFConstraint_BallAndSocketJoint *c; c = new idAFConstraint_BallAndSocketJoint( name, body, NULL ); physicsObj.AddConstraint( c ); lexer.ReadToken( &jointName ); jointHandle_t joint = animator->GetJointHandle( jointName ); if ( joint == INVALID_JOINT ) { gameLocal.Warning( "idAF::AddBindConstraints: joint '%s' not found", jointName.c_str() ); } animator->GetJointTransform( joint, gameLocal.time, origin, axis ); c->SetAnchor( renderOrigin + origin * renderAxis ); } else if ( type.Icmp( "universal" ) == 0 ) { idAFConstraint_UniversalJoint *c; c = new idAFConstraint_UniversalJoint( name, body, NULL ); physicsObj.AddConstraint( c ); lexer.ReadToken( &jointName ); jointHandle_t joint = animator->GetJointHandle( jointName ); if ( joint == INVALID_JOINT ) { gameLocal.Warning( "idAF::AddBindConstraints: joint '%s' not found", jointName.c_str() ); } animator->GetJointTransform( joint, gameLocal.time, origin, axis ); c->SetAnchor( renderOrigin + origin * renderAxis ); c->SetShafts( idVec3( 0, 0, 1 ), idVec3( 0, 0, -1 ) ); } else { gameLocal.Warning( "idAF::AddBindConstraints: unknown constraint type '%s' on entity '%s'", type.c_str(), self->name.c_str() ); } lexer.FreeSource(); } hasBindConstraints = true; }
/* ================= idMapBrush::ParseQ3 ================= */ idMapBrush *idMapBrush::ParseQ3( idLexer &src, const idVec3 &origin ) { int i, shift[2], rotate; float scale[2]; idVec3 planepts[3]; idToken token; idList<idMapBrushSide*> sides; idMapBrushSide *side; idDict epairs; do { if ( src.CheckTokenString( "}" ) ) { break; } side = new idMapBrushSide(); sides.Append( side ); // read the three point plane definition if (!src.Parse1DMatrix( 3, planepts[0].ToFloatPtr() ) || !src.Parse1DMatrix( 3, planepts[1].ToFloatPtr() ) || !src.Parse1DMatrix( 3, planepts[2].ToFloatPtr() ) ) { src.Error( "idMapBrush::ParseQ3: unable to read brush side plane definition" ); sides.DeleteContents( true ); return NULL; } planepts[0] -= origin; planepts[1] -= origin; planepts[2] -= origin; side->plane.FromPoints( planepts[0], planepts[1], planepts[2] ); // read the material if ( !src.ReadTokenOnLine( &token ) ) { src.Error( "idMapBrush::ParseQ3: unable to read brush side material" ); sides.DeleteContents( true ); return NULL; } // we have an implicit 'textures/' in the old format side->material = "textures/" + token; // read the texture shift, rotate and scale shift[0] = src.ParseInt(); shift[1] = src.ParseInt(); rotate = src.ParseInt(); scale[0] = src.ParseFloat(); scale[1] = src.ParseFloat(); side->texMat[0] = idVec3( 0.03125f, 0.0f, 0.0f ); side->texMat[1] = idVec3( 0.0f, 0.03125f, 0.0f ); side->origin = origin; // Q2 allowed override of default flags and values, but we don't any more if ( src.ReadTokenOnLine( &token ) ) { if ( src.ReadTokenOnLine( &token ) ) { if ( src.ReadTokenOnLine( &token ) ) { } } } } while( 1 ); idMapBrush *brush = new idMapBrush(); for ( i = 0; i < sides.Num(); i++ ) { brush->AddSide( sides[i] ); } brush->epairs = epairs; return brush; }
/* ============ idBox::FromPoints Tight box for a collection of points. ============ */ void idBox::FromPoints( const idVec3 *points, const int numPoints ) { int i; float invNumPoints, sumXX, sumXY, sumXZ, sumYY, sumYZ, sumZZ; idVec3 dir; idBounds bounds; idMatX eigenVectors; idVecX eigenValues; // compute mean of points center = points[0]; for ( i = 1; i < numPoints; i++ ) { center += points[i]; } invNumPoints = 1.0f / numPoints; center *= invNumPoints; // compute covariances of points sumXX = 0.0f; sumXY = 0.0f; sumXZ = 0.0f; sumYY = 0.0f; sumYZ = 0.0f; sumZZ = 0.0f; for ( i = 0; i < numPoints; i++ ) { dir = points[i] - center; sumXX += dir.x * dir.x; sumXY += dir.x * dir.y; sumXZ += dir.x * dir.z; sumYY += dir.y * dir.y; sumYZ += dir.y * dir.z; sumZZ += dir.z * dir.z; } sumXX *= invNumPoints; sumXY *= invNumPoints; sumXZ *= invNumPoints; sumYY *= invNumPoints; sumYZ *= invNumPoints; sumZZ *= invNumPoints; // compute eigenvectors for covariance matrix eigenValues.SetData( 3, VECX_ALLOCA( 3 ) ); eigenVectors.SetData( 3, 3, MATX_ALLOCA( 3 * 3 ) ); eigenVectors[0][0] = sumXX; eigenVectors[0][1] = sumXY; eigenVectors[0][2] = sumXZ; eigenVectors[1][0] = sumXY; eigenVectors[1][1] = sumYY; eigenVectors[1][2] = sumYZ; eigenVectors[2][0] = sumXZ; eigenVectors[2][1] = sumYZ; eigenVectors[2][2] = sumZZ; eigenVectors.Eigen_SolveSymmetric( eigenValues ); eigenVectors.Eigen_SortIncreasing( eigenValues ); axis[0][0] = eigenVectors[0][0]; axis[0][1] = eigenVectors[0][1]; axis[0][2] = eigenVectors[0][2]; axis[1][0] = eigenVectors[1][0]; axis[1][1] = eigenVectors[1][1]; axis[1][2] = eigenVectors[1][2]; axis[2][0] = eigenVectors[2][0]; axis[2][1] = eigenVectors[2][1]; axis[2][2] = eigenVectors[2][2]; extents[0] = eigenValues[0]; extents[1] = eigenValues[0]; extents[2] = eigenValues[0]; // refine by calculating the bounds of the points projected onto the axis and adjusting the center and extents bounds.Clear(); for ( i = 0; i < numPoints; i++ ) { bounds.AddPoint( idVec3( points[i] * axis[0], points[i] * axis[1], points[i] * axis[2] ) ); } center = ( bounds[0] + bounds[1] ) * 0.5f; extents = bounds[1] - center; center *= axis; }
/* ============= idEditEntities::DisplayEntities ============= */ void idEditEntities::DisplayEntities( void ) { idEntity *ent; if ( !gameLocal.GetLocalPlayer() ) { return; } selectableEntityClasses.Clear(); selectedTypeInfo_t sit; switch( g_editEntityMode.GetInteger() ) { case 1: sit.typeInfo = &idLight::Type; sit.textKey = "texture"; selectableEntityClasses.Append( sit ); break; case 2: sit.typeInfo = &idSound::Type; sit.textKey = "s_shader"; selectableEntityClasses.Append( sit ); sit.typeInfo = &idLight::Type; sit.textKey = "texture"; selectableEntityClasses.Append( sit ); break; case 3: sit.typeInfo = &idAFEntity_Base::Type; sit.textKey = "articulatedFigure"; selectableEntityClasses.Append( sit ); break; case 4: sit.typeInfo = &idFuncEmitter::Type; sit.textKey = "model"; selectableEntityClasses.Append( sit ); break; case 5: sit.typeInfo = &idAI::Type; sit.textKey = "name"; selectableEntityClasses.Append( sit ); break; case 6: sit.typeInfo = &idEntity::Type; sit.textKey = "name"; selectableEntityClasses.Append( sit ); break; case 7: sit.typeInfo = &idEntity::Type; sit.textKey = "model"; selectableEntityClasses.Append( sit ); break; default: return; } idBounds viewBounds( gameLocal.GetLocalPlayer()->GetPhysics()->GetOrigin() ); idBounds viewTextBounds( gameLocal.GetLocalPlayer()->GetPhysics()->GetOrigin() ); idMat3 axis = gameLocal.GetLocalPlayer()->viewAngles.ToMat3(); viewBounds.ExpandSelf( 512 ); viewTextBounds.ExpandSelf( 128 ); idStr textKey; for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { idVec4 color; textKey = ""; if ( !EntityIsSelectable( ent, &color, &textKey ) ) { continue; } bool drawArrows = false; if ( ent->GetType() == &idAFEntity_Base::Type ) { if ( !static_cast<idAFEntity_Base *>(ent)->IsActiveAF() ) { continue; } } else if ( ent->GetType() == &idSound::Type ) { if ( ent->fl.selected ) { drawArrows = true; } const idSoundShader * ss = declManager->FindSound( ent->spawnArgs.GetString( textKey ) ); if ( ss->HasDefaultSound() || ss->base->GetState() == DS_DEFAULTED ) { color.Set( 1.0f, 0.0f, 1.0f, 1.0f ); } } else if ( ent->GetType() == &idFuncEmitter::Type ) { if ( ent->fl.selected ) { drawArrows = true; } } if ( !viewBounds.ContainsPoint( ent->GetPhysics()->GetOrigin() ) ) { continue; } gameRenderWorld->DebugBounds( color, idBounds( ent->GetPhysics()->GetOrigin() ).Expand( 8 ) ); if ( drawArrows ) { idVec3 start = ent->GetPhysics()->GetOrigin(); idVec3 end = start + idVec3( 1, 0, 0 ) * 20.0f; gameRenderWorld->DebugArrow( colorWhite, start, end, 2 ); gameRenderWorld->DrawText( "x+", end + idVec3( 4, 0, 0 ), 0.15f, colorWhite, axis ); end = start + idVec3( 1, 0, 0 ) * -20.0f; gameRenderWorld->DebugArrow( colorWhite, start, end, 2 ); gameRenderWorld->DrawText( "x-", end + idVec3( -4, 0, 0 ), 0.15f, colorWhite, axis ); end = start + idVec3( 0, 1, 0 ) * +20.0f; gameRenderWorld->DebugArrow( colorGreen, start, end, 2 ); gameRenderWorld->DrawText( "y+", end + idVec3( 0, 4, 0 ), 0.15f, colorWhite, axis ); end = start + idVec3( 0, 1, 0 ) * -20.0f; gameRenderWorld->DebugArrow( colorGreen, start, end, 2 ); gameRenderWorld->DrawText( "y-", end + idVec3( 0, -4, 0 ), 0.15f, colorWhite, axis ); end = start + idVec3( 0, 0, 1 ) * +20.0f; gameRenderWorld->DebugArrow( colorBlue, start, end, 2 ); gameRenderWorld->DrawText( "z+", end + idVec3( 0, 0, 4 ), 0.15f, colorWhite, axis ); end = start + idVec3( 0, 0, 1 ) * -20.0f; gameRenderWorld->DebugArrow( colorBlue, start, end, 2 ); gameRenderWorld->DrawText( "z-", end + idVec3( 0, 0, -4 ), 0.15f, colorWhite, axis ); } if ( textKey.Length() ) { const char *text = ent->spawnArgs.GetString( textKey ); if ( viewTextBounds.ContainsPoint( ent->GetPhysics()->GetOrigin() ) ) { gameRenderWorld->DrawText( text, ent->GetPhysics()->GetOrigin() + idVec3(0, 0, 12), 0.25, colorWhite, axis, 1 ); } } } }
void idKeypad::OnFrob( idEntity* activator ) { int index; if (!activator->spawnArgs.GetInt( "index", "", index)) { return; } //common->Printf("%d\n", index); if (index == 16) { this->StartSound( "snd_error" , SND_CHANNEL_ANY, 0, false, NULL ); return; } //PostEventSec( &EV_Turret_muzzleflashoff, MUZZLEFLASHTIME); if (index >= 0 && index <= 8) { jointHandle_t joint; joint = animator.GetJointHandle( GetJointViaIndex(index) ); animator.SetJointPos(joint, JOINTMOD_LOCAL, idVec3(-0.8f, 0, 0) ); transitions[index] = gameLocal.time + PRESSTIME; } StartSound( "snd_press", SND_CHANNEL_ANY, 0, false, NULL ); if (counter <= 0) { Event_PlayAnim("marker0", 4); } else if (counter == 1) { Event_PlayAnim("marker1", 4); } else if (counter == 2) { Event_PlayAnim("marker2", 4); } else if (counter == 3) { Event_PlayAnim("marker3", 4); } input[counter] = index; counter++; if (counter >= 4) { int i; for (i = 0; i < 9; i++) { this->frobcubes[i]->isFrobbable = false; } counter = 0; //TODO if successful. // gameLocal.GetLocalPlayer()->Event_useAmmo("ammo_hacktool", 1) if ( input[0] == keycode[0] && input[1] == keycode[1] && input[2] == keycode[2] && input[3] == keycode[3] ) { //done success. bluebox->Hide(); ActivateTargets( this ); state = CONFIRM_SUCCESS; int doneTime = Event_PlayAnim("success", 4); nextStateTime = doneTime; SetSkin(declManager->FindSkin("skins/keypad/green")); //add hud message. StartSound( "snd_deactivated", SND_CHANNEL_ANY, 0, false, NULL); return; } else { //fail. state = CONFIRM_FAIL; int doneTime = Event_PlayAnim("fail", 4); nextStateTime = doneTime; SetSkin(declManager->FindSkin("skins/keypad/red")); return; } } }
/* ==================== idRenderModelLiquid::InitFromFile ==================== */ void idRenderModelLiquid::InitFromFile( const char *fileName ) { int i, x, y; idToken token; idParser parser( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS ); idList<int> tris; float size_x, size_y; float rate; name = fileName; if ( !parser.LoadFile( fileName ) ) { MakeDefaultModel(); return; } size_x = scale_x * verts_x; size_y = scale_y * verts_y; while( parser.ReadToken( &token ) ) { if ( !token.Icmp( "seed" ) ) { seed = parser.ParseInt(); } else if ( !token.Icmp( "size_x" ) ) { size_x = parser.ParseFloat(); } else if ( !token.Icmp( "size_y" ) ) { size_y = parser.ParseFloat(); } else if ( !token.Icmp( "verts_x" ) ) { verts_x = parser.ParseFloat(); if ( verts_x < 2 ) { parser.Warning( "Invalid # of verts. Using default model." ); MakeDefaultModel(); return; } } else if ( !token.Icmp( "verts_y" ) ) { verts_y = parser.ParseFloat(); if ( verts_y < 2 ) { parser.Warning( "Invalid # of verts. Using default model." ); MakeDefaultModel(); return; } } else if ( !token.Icmp( "liquid_type" ) ) { liquid_type = parser.ParseInt() - 1; if ( ( liquid_type < 0 ) || ( liquid_type >= LIQUID_MAX_TYPES ) ) { parser.Warning( "Invalid liquid_type. Using default model." ); MakeDefaultModel(); return; } } else if ( !token.Icmp( "density" ) ) { density = parser.ParseFloat(); } else if ( !token.Icmp( "drop_height" ) ) { drop_height = parser.ParseFloat(); } else if ( !token.Icmp( "drop_radius" ) ) { drop_radius = parser.ParseInt(); } else if ( !token.Icmp( "drop_delay" ) ) { drop_delay = SEC2MS( parser.ParseFloat() ); } else if ( !token.Icmp( "shader" ) ) { parser.ReadToken( &token ); shader = declManager->FindMaterial( token ); } else if ( !token.Icmp( "seed" ) ) { seed = parser.ParseInt(); } else if ( !token.Icmp( "update_rate" ) ) { rate = parser.ParseFloat(); if ( ( rate <= 0.0f ) || ( rate > 60.0f ) ) { parser.Warning( "Invalid update_rate. Must be between 0 and 60. Using default model." ); MakeDefaultModel(); return; } update_tics = 1000 / rate; } else { parser.Warning( "Unknown parameter '%s'. Using default model.", token.c_str() ); MakeDefaultModel(); return; } } scale_x = size_x / ( verts_x - 1 ); scale_y = size_y / ( verts_y - 1 ); pages.SetNum( 2 * verts_x * verts_y ); page1 = pages.Ptr(); page2 = page1 + verts_x * verts_y; verts.SetNum( verts_x * verts_y ); for ( i = 0, y = 0; y < verts_y; y++ ) { for ( x = 0; x < verts_x; x++, i++ ) { page1[ i ] = 0.0f; page2[ i ] = 0.0f; verts[ i ].Clear(); verts[ i ].xyz.Set( x * scale_x, y * scale_y, 0.0f ); verts[ i ].st.Set( (float) x / (float)( verts_x - 1 ), (float) -y / (float)( verts_y - 1 ) ); } } tris.SetNum( ( verts_x - 1 ) * ( verts_y - 1 ) * 6 ); for( i = 0, y = 0; y < verts_y - 1; y++ ) { for( x = 1; x < verts_x; x++, i += 6 ) { tris[ i + 0 ] = y * verts_x + x; tris[ i + 1 ] = y * verts_x + x - 1; tris[ i + 2 ] = ( y + 1 ) * verts_x + x - 1; tris[ i + 3 ] = ( y + 1 ) * verts_x + x - 1; tris[ i + 4 ] = ( y + 1 ) * verts_x + x; tris[ i + 5 ] = y * verts_x + x; } } // build the information that will be common to all animations of this mesh: // sil edge connectivity and normal / tangent generation information deformInfo = R_BuildDeformInfo( verts.Num(), verts.Ptr(), tris.Num(), tris.Ptr(), true ); bounds.Clear(); bounds.AddPoint( idVec3( 0.0f, 0.0f, drop_height * -10.0f ) ); bounds.AddPoint( idVec3( ( verts_x - 1 ) * scale_x, ( verts_y - 1 ) * scale_y, drop_height * 10.0f ) ); // set the timestamp for reloadmodels fileSystem->ReadFile( name, NULL, &timeStamp ); Reset(); }
/* ===================== idCameraAnim::GetViewParms ===================== */ void idCameraAnim::GetViewParms( renderView_t *view ) { int realFrame; int frame; int frameTime; float lerp; float invlerp; cameraFrame_t *camFrame; int i; int cut; idQuat q1, q2, q3; assert( view ); if ( !view ) { return; } if ( camera.Num() == 0 ) { // we most likely are in the middle of a restore // FIXME: it would be better to fix it so this doesn't get called during a restore return; } #ifdef _D3XP SetTimeState ts( timeGroup ); #endif if ( frameRate == USERCMD_HZ ) { frameTime = gameLocal.time - starttime; frame = frameTime / gameLocal.msec; lerp = 0.0f; } else { frameTime = ( gameLocal.time - starttime ) * frameRate; frame = frameTime / 1000; lerp = ( frameTime % 1000 ) * 0.001f; } // skip any frames where camera cuts occur realFrame = frame; cut = 0; for( i = 0; i < cameraCuts.Num(); i++ ) { if ( frame < cameraCuts[ i ] ) { break; } frame++; cut++; } if ( g_debugCinematic.GetBool() ) { int prevFrameTime = ( gameLocal.time - starttime - gameLocal.msec ) * frameRate; int prevFrame = prevFrameTime / 1000; int prevCut; prevCut = 0; for( i = 0; i < cameraCuts.Num(); i++ ) { if ( prevFrame < cameraCuts[ i ] ) { break; } prevFrame++; prevCut++; } if ( prevCut != cut ) { gameLocal.Printf( "%d: '%s' cut %d\n", gameLocal.framenum, GetName(), cut ); } } // clamp to the first frame. also check if this is a one frame anim. one frame anims would end immediately, // but since they're mainly used for static cams anyway, just stay on it infinitely. if ( ( frame < 0 ) || ( camera.Num() < 2 ) ) { view->viewaxis = camera[ 0 ].q.ToQuat().ToMat3(); view->vieworg = camera[ 0 ].t + offset; view->fov_x = camera[ 0 ].fov; } else if ( frame > camera.Num() - 2 ) { if ( cycle > 0 ) { cycle--; } if ( cycle != 0 ) { // advance start time so that we loop starttime += ( ( camera.Num() - cameraCuts.Num() ) * 1000 ) / frameRate; GetViewParms( view ); return; } Stop(); if ( gameLocal.GetCamera() != NULL ) { // we activated another camera when we stopped, so get it's viewparms instead gameLocal.GetCamera()->GetViewParms( view ); return; } else { // just use our last frame camFrame = &camera[ camera.Num() - 1 ]; view->viewaxis = camFrame->q.ToQuat().ToMat3(); view->vieworg = camFrame->t + offset; view->fov_x = camFrame->fov; } } else if ( lerp == 0.0f ) { camFrame = &camera[ frame ]; view->viewaxis = camFrame[ 0 ].q.ToMat3(); view->vieworg = camFrame[ 0 ].t + offset; view->fov_x = camFrame[ 0 ].fov; } else { camFrame = &camera[ frame ]; invlerp = 1.0f - lerp; q1 = camFrame[ 0 ].q.ToQuat(); q2 = camFrame[ 1 ].q.ToQuat(); q3.Slerp( q1, q2, lerp ); view->viewaxis = q3.ToMat3(); view->vieworg = camFrame[ 0 ].t * invlerp + camFrame[ 1 ].t * lerp + offset; view->fov_x = camFrame[ 0 ].fov * invlerp + camFrame[ 1 ].fov * lerp; } gameLocal.CalcFov( view->fov_x, view->fov_x, view->fov_y ); // setup the pvs for this frame UpdatePVSAreas( view->vieworg ); #if 0 static int lastFrame = 0; static idVec3 lastFrameVec( 0.0f, 0.0f, 0.0f ); if ( gameLocal.time != lastFrame ) { gameRenderWorld->DebugBounds( colorCyan, idBounds( view->vieworg ).Expand( 16.0f ), vec3_origin, gameLocal.msec ); gameRenderWorld->DebugLine( colorRed, view->vieworg, view->vieworg + idVec3( 0.0f, 0.0f, 2.0f ), 10000, false ); gameRenderWorld->DebugLine( colorCyan, lastFrameVec, view->vieworg, 10000, false ); gameRenderWorld->DebugLine( colorYellow, view->vieworg + view->viewaxis[ 0 ] * 64.0f, view->vieworg + view->viewaxis[ 0 ] * 66.0f, 10000, false ); gameRenderWorld->DebugLine( colorOrange, view->vieworg + view->viewaxis[ 0 ] * 64.0f, view->vieworg + view->viewaxis[ 0 ] * 64.0f + idVec3( 0.0f, 0.0f, 2.0f ), 10000, false ); lastFrameVec = view->vieworg; lastFrame = gameLocal.time; } #endif if ( g_showcamerainfo.GetBool() ) { gameLocal.Printf( "^5Frame: ^7%d/%d\n\n\n", realFrame + 1, camera.Num() - cameraCuts.Num() ); } }
/* ============== sdDeployMaskEditSession::UpdateProjection ============== */ void sdDeployMaskEditSession::UpdateProjection( const idVec3& position ) { if ( !decalMaterial ) { return; } if ( decalHandle == -1 ) { decalHandle = gameLocal.RegisterLoggedDecal( decalMaterial ); } gameLocal.ResetLoggedDecal( decalHandle ); gameDecalInfo_t* decalInfo = gameLocal.GetLoggedDecal( decalHandle ); sdDeployMaskInstance* mask = GetMask( position ); const sdHeightMapInstance* heightMap = GetHeightMap( position ); if ( mask != NULL && mask->IsValid() && heightMap != NULL ) { sdDeployMask::extents_t extents; GetExtents( position, *mask, extents ); float depth = 512.0f; int maxX, maxY; mask->GetDimensions( maxX, maxY ); sdDeployMask::extents_t expandedExtents; expandedExtents.minx = Max( 0, extents.minx - 2 ); expandedExtents.miny = Max( 0, extents.miny - 2 ); expandedExtents.maxx = Min( maxX, extents.maxx + 2 ); expandedExtents.maxy = Min( maxY, extents.maxy + 2 ); idList< const idMaterial* > megaTextureMaterials; const idStrList& megaTextureMaterialNames = gameLocal.GetMapInfo().GetMegatextureMaterials(); for ( int i = 0; i < megaTextureMaterialNames.Num(); i++ ) { megaTextureMaterials.Append( declHolder.FindMaterial( megaTextureMaterialNames[ i ] ) ); } idFixedWinding winding; int spawnID = WORLD_SPAWN_ID; for ( int i = expandedExtents.minx; i <= expandedExtents.maxx; i++ ) { for ( int j = expandedExtents.miny; j <= expandedExtents.maxy; j++ ) { gameDecalInfo_t* info = decalInfo; if ( !info ) { continue; } sdDeployMask::extents_t localExtents; localExtents.minx = i; localExtents.maxx = i; localExtents.miny = j; localExtents.maxy = j; idBounds bounds; mask->GetBounds( localExtents, bounds, heightMap ); idVec3 top = bounds.GetCenter(); top[ 2 ] = bounds.GetMaxs()[ 2 ]; deployResult_t localResult = mask->IsValid( localExtents ); idVec4 localColor; switch ( localResult ) { case DR_CLEAR: localColor = colorGreen; break; default: case DR_FAILED: localColor = colorDkRed; break; } if ( !( ( i >= extents.minx ) && ( i <= extents.maxx ) && ( j >= extents.miny ) && ( j <= extents.maxy ) ) ) { localColor.x *= 0.3f; localColor.y *= 0.3f; localColor.z *= 0.3f; } winding.Clear(); winding += idVec5( idVec3( bounds.GetMins()[ 0 ], bounds.GetMins()[ 1 ], bounds.GetMins()[ 2 ] - depth ), idVec2( 0.0f, 0.0f ) ); winding += idVec5( idVec3( bounds.GetMins()[ 0 ], bounds.GetMaxs()[ 1 ], bounds.GetMins()[ 2 ] - depth ), idVec2( 0.0f, 1.0f ) ); winding += idVec5( idVec3( bounds.GetMaxs()[ 0 ], bounds.GetMaxs()[ 1 ], bounds.GetMins()[ 2 ] - depth ), idVec2( 1.0f, 1.0f ) ); winding += idVec5( idVec3( bounds.GetMaxs()[ 0 ], bounds.GetMins()[ 1 ], bounds.GetMins()[ 2 ] - depth ), idVec2( 1.0f, 0.0f ) ); gameRenderWorld->AddToProjectedDecal( winding, top + idVec3( 0, 0, 64.f + depth ), true, localColor, info->renderEntity.hModel, spawnID, megaTextureMaterials.Begin(), megaTextureMaterials.Num() ); } } } }
void idTrembler::Event_Touch( idEntity *other, trace_t *trace ) { if (state == 0) { idEntity *entityList[ MAX_GENTITIES ]; int i; int flyforce; state = 1; Event_PlayAnim("bounce", 1); StartSound( "snd_trigger", SND_CHANNEL_BODY, 0, false, NULL ); StartSound( "snd_trigger_idle", SND_CHANNEL_BODY2, 0, false, NULL ); ticksoundTime = gameLocal.time + (spawnArgs.GetInt("ticktime", "10") * 1000); if (g_skill.GetInteger() > 0) { ActivateTargets(this); //activate alarm. } int listedEntities = gameLocal.EntitiesWithinRadius( GetPhysics()->GetOrigin(), FLYRADIUS, entityList, MAX_GENTITIES ); flyforce = spawnArgs.GetInt("force", "256"); renderEntity.shaderParms[ SHADERPARM_RED ] = 1; renderEntity.shaderParms[ SHADERPARM_GREEN ] = 0; renderEntity.shaderParms[ SHADERPARM_BLUE ] = 0; UpdateVisuals(); //KA BOIIIIINNNGGGGGG for (i = 0; i < listedEntities; i++) { idEntity *ent = entityList[ i ]; if (!ent) { continue; } float ent_zpos = ent->GetPhysics()->GetOrigin().z; float trembler_zpos = this->GetPhysics()->GetOrigin().z; //only affect things above the trembler. if (ent_zpos < trembler_zpos) continue; //only affect things within X height of trembler. if (ent_zpos > trembler_zpos + HEIGHT_MAX) continue; if ( !ent->IsType( idMoveableItem::Type ) && !ent->IsType( idMoveable::Type ) ) continue; ent->GetPhysics()->SetLinearVelocity(idVec3(0,0,1) * flyforce); } } }
/* ================ sdScriptedEntityHelper_LegIk::Init ================ */ void sdScriptedEntityHelper_LegIk::Init( sdScriptEntity* owner, const sdDeclStringMap* map ) { sdScriptedEntityHelper::Init( owner, map ); const idDict& dict = map->GetDict(); idAnimator* animator = _owner->GetAnimator(); upperLegJoint = animator->GetJointHandle( dict.GetString( "joint_upper" ) ); if ( upperLegJoint == INVALID_JOINT ) { gameLocal.Warning( "sdScriptedEntityHelper_LegIk::Init Invalid Upper Leg Joint" ); } middleLegJoint = animator->GetJointHandle( dict.GetString( "joint_middle" ) ); if ( middleLegJoint == INVALID_JOINT ) { gameLocal.Warning( "sdScriptedEntityHelper_LegIk::Init Invalid Middle Leg Joint" ); } lowerLegJoint = animator->GetJointHandle( dict.GetString( "joint_lower" ) ); if ( lowerLegJoint == INVALID_JOINT ) { gameLocal.Warning( "sdScriptedEntityHelper_LegIk::Init Invalid Lower Leg Joint" ); } upDir = dict.GetVector( "direction", "0 0 1" ); groundOffset = dict.GetFloat( "ground_offset" ); blendRate = dict.GetFloat( "blend_rate", "0.95" ); maxUpTrace = dict.GetFloat( "max_up_trace", "64" ); maxDownTrace = dict.GetFloat( "max_down_trace", "64" ); lifetime = dict.GetInt( "lifetime", "5000" ); startTime = gameLocal.time; idMat3 axis; idMat3 upperJointAxis, middleJointAxis, lowerJointAxis; idVec3 upperJointOrg, middleJointOrg, lowerJointOrg; animator->GetJointTransform( upperLegJoint, gameLocal.time, upperJointOrg, upperJointAxis ); animator->GetJointTransform( middleLegJoint, gameLocal.time, middleJointOrg, middleJointAxis ); animator->GetJointTransform( lowerLegJoint, gameLocal.time, lowerJointOrg, lowerJointAxis ); upperLength = idIK::GetBoneAxis( upperJointOrg, middleJointOrg, upDir, axis ); midToUpperJoint = upperJointAxis * axis.Transpose(); lowerLength = idIK::GetBoneAxis( middleJointOrg, lowerJointOrg, upDir, axis ); lowerToMidJoint = middleJointAxis * axis.Transpose(); { idVec3 lowerJointOrg; animator->GetJointTransform( lowerLegJoint, gameLocal.time, lowerJointOrg ); idVec3 traceOrg = _owner->GetRenderEntity()->origin + ( lowerJointOrg * _owner->GetRenderEntity()->axis ); trace_t tr; gameLocal.clip.TracePoint( CLIP_DEBUG_PARMS tr, traceOrg + idVec3( 0, 0, maxUpTrace ), traceOrg - idVec3( 0, 0, maxDownTrace ), CONTENTS_SOLID, _owner ); currentGroundOffset = traceOrg.z - tr.endpos.z; } owner->IncForcedAnimUpdate(); }
/* ============ idFrustum::ProjectionBounds ============ */ bool idFrustum::ProjectionBounds( const idBox &box, idBounds &projectionBounds ) const { int i, p1, p2, pointCull[8], culled, outside; float scale1, scale2; idFrustum localFrustum; idVec3 points[8], localOrigin; idMat3 localAxis, localScaled; idBounds bounds( -box.GetExtents(), box.GetExtents() ); // if the frustum origin is inside the bounds if( bounds.ContainsPoint( ( origin - box.GetCenter() ) * box.GetAxis().Transpose() ) ) { // bounds that cover the whole frustum float boxMin, boxMax, base; base = origin * axis[0]; box.AxisProjection( axis[0], boxMin, boxMax ); projectionBounds[0].x = boxMin - base; projectionBounds[1].x = boxMax - base; projectionBounds[0].y = projectionBounds[0].z = -1.0f; projectionBounds[1].y = projectionBounds[1].z = 1.0f; return true; } projectionBounds.Clear(); // transform the bounds into the space of this frustum localOrigin = ( box.GetCenter() - origin ) * axis.Transpose(); localAxis = box.GetAxis() * axis.Transpose(); BoxToPoints( localOrigin, box.GetExtents(), localAxis, points ); // test outer four edges of the bounds culled = -1; outside = 0; for( i = 0; i < 4; i++ ) { p1 = i; p2 = 4 + i; AddLocalLineToProjectionBoundsSetCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds ); culled &= pointCull[p1] & pointCull[p2]; outside |= pointCull[p1] | pointCull[p2]; } // if the bounds are completely outside this frustum if( culled ) { return false; } // if the bounds are completely inside this frustum if( !outside ) { return true; } // test the remaining edges of the bounds for( i = 0; i < 4; i++ ) { p1 = i; p2 = ( i + 1 ) & 3; AddLocalLineToProjectionBoundsUseCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds ); } for( i = 0; i < 4; i++ ) { p1 = 4 + i; p2 = 4 + ( ( i + 1 ) & 3 ); AddLocalLineToProjectionBoundsUseCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds ); } // if the bounds extend beyond two or more boundaries of this frustum if( outside != 1 && outside != 2 && outside != 4 && outside != 8 ) { localOrigin = ( origin - box.GetCenter() ) * box.GetAxis().Transpose(); localScaled = axis * box.GetAxis().Transpose(); localScaled[0] *= dFar; localScaled[1] *= dLeft; localScaled[2] *= dUp; // test the outer edges of this frustum for intersection with the bounds if( ( outside & 2 ) && ( outside & 8 ) ) { BoundsRayIntersection( bounds, localOrigin, localScaled[0] - localScaled[1] - localScaled[2], scale1, scale2 ); if( scale1 <= scale2 && scale1 >= 0.0f ) { projectionBounds.AddPoint( idVec3( scale1 * dFar, -1.0f, -1.0f ) ); projectionBounds.AddPoint( idVec3( scale2 * dFar, -1.0f, -1.0f ) ); } } if( ( outside & 2 ) && ( outside & 4 ) ) { BoundsRayIntersection( bounds, localOrigin, localScaled[0] - localScaled[1] + localScaled[2], scale1, scale2 ); if( scale1 <= scale2 && scale1 >= 0.0f ) { projectionBounds.AddPoint( idVec3( scale1 * dFar, -1.0f, 1.0f ) ); projectionBounds.AddPoint( idVec3( scale2 * dFar, -1.0f, 1.0f ) ); } } if( ( outside & 1 ) && ( outside & 8 ) ) { BoundsRayIntersection( bounds, localOrigin, localScaled[0] + localScaled[1] - localScaled[2], scale1, scale2 ); if( scale1 <= scale2 && scale1 >= 0.0f ) { projectionBounds.AddPoint( idVec3( scale1 * dFar, 1.0f, -1.0f ) ); projectionBounds.AddPoint( idVec3( scale2 * dFar, 1.0f, -1.0f ) ); } } if( ( outside & 1 ) && ( outside & 2 ) ) { BoundsRayIntersection( bounds, localOrigin, localScaled[0] + localScaled[1] + localScaled[2], scale1, scale2 ); if( scale1 <= scale2 && scale1 >= 0.0f ) { projectionBounds.AddPoint( idVec3( scale1 * dFar, 1.0f, 1.0f ) ); projectionBounds.AddPoint( idVec3( scale2 * dFar, 1.0f, 1.0f ) ); } } } return true; }
/* ================ sdPlayerArmIK::Update ================ */ void sdPlayerArmIK::Update( idPlayer* player, idEntity* target ) { struct arm_t { idMat3 targetShoulderAxis; idMat3 targetElbowAxis; idMat3 targetHandAxis; }; arm_t arms[ NUM_ARMS ]; ClearJointMods( player ); idAnimator* animator = player->GetAnimator(); idMat3 tempAxis; idVec3 modelOrigin = player->GetRenderEntity()->origin; idMat3 modelAxis = player->GetRenderEntity()->axis; idMat3 transposeModelAxis = modelAxis.Transpose(); // get the arm bone lengths and rotation matrices for ( int i = 0; i < NUM_ARMS; i++ ) { if ( armTargets[ i ].joint == INVALID_JOINT ) { continue; } idMat3 handAxis; idVec3 handOrigin; animator->GetJointTransform( player->GetHandJoint( i ), gameLocal.time, handOrigin, handAxis ); idMat3 elbowAxis; idVec3 elbowOrigin; animator->GetJointTransform( player->GetElbowJoint( i ), gameLocal.time, elbowOrigin, elbowAxis ); idMat3 shoulderAxis; idVec3 shoulderOrigin; animator->GetJointTransform( player->GetShoulderJoint( i ), gameLocal.time, shoulderOrigin, shoulderAxis ); idVec3 t1 = ( elbowOrigin - shoulderOrigin ); // t1.Normalize(); idVec3 t2 = ( elbowOrigin - handOrigin ); // t2.Normalize(); idVec3 dir = t1 + t2; dir.Normalize(); // conversion from upper arm bone axis to should joint axis float upperArmLength = idIK::GetBoneAxis( shoulderOrigin, elbowOrigin, dir, tempAxis ); idMat3 upperArmToShoulderJoint = shoulderAxis * tempAxis.Transpose(); // conversion from lower arm bone axis to elbow joint axis float lowerArmLength = idIK::GetBoneAxis( elbowOrigin, handOrigin, dir, tempAxis ); idMat3 lowerArmToElbowJoint = elbowAxis * tempAxis.Transpose(); // get target idVec3 targetOrigin; target->GetWorldOriginAxis( armTargets[ i ].joint, targetOrigin, arms[ i ].targetHandAxis ); idVec3 targetOriginLocal = targetOrigin - modelOrigin; targetOriginLocal *= transposeModelAxis; arms[ i ].targetHandAxis *= transposeModelAxis; // solve IK and calculate elbow position idIK::SolveTwoBones( shoulderOrigin, targetOriginLocal, dir, upperArmLength, lowerArmLength, elbowOrigin ); if ( ik_debug.GetBool() ) { idVec3 shoulderWorld = ( shoulderOrigin * modelAxis ) + modelOrigin; idVec3 elbowWorld = ( elbowOrigin * modelAxis ) + modelOrigin; gameRenderWorld->DebugLine( colorCyan, shoulderWorld, elbowWorld ); gameRenderWorld->DebugLine( colorRed, elbowWorld, targetOrigin ); gameRenderWorld->DebugBox( colorYellow, idBox( targetOrigin, idVec3( 2, 2, 2 ), mat3_identity ) ); gameRenderWorld->DebugLine( colorGreen, elbowWorld, elbowWorld + ( ( dir * modelAxis ) * 8 ) ); } // get the axis for the shoulder joint idIK::GetBoneAxis( shoulderOrigin, elbowOrigin, dir, tempAxis ); arms[ i ].targetShoulderAxis = upperArmToShoulderJoint * tempAxis; // get the axis for the elbow joint idIK::GetBoneAxis( elbowOrigin, targetOriginLocal, dir, tempAxis ); arms[ i ].targetElbowAxis = lowerArmToElbowJoint * tempAxis; } for ( int i = 0; i < NUM_ARMS; i++ ) { if ( armTargets[ i ].joint == INVALID_JOINT ) { continue; } animator->SetJointAxis( player->GetShoulderJoint( i ), JOINTMOD_WORLD_OVERRIDE, arms[ i ].targetShoulderAxis ); animator->SetJointAxis( player->GetElbowJoint( i ), JOINTMOD_WORLD_OVERRIDE, arms[ i ].targetElbowAxis ); animator->SetJointAxis( player->GetHandJoint( i ), JOINTMOD_WORLD_OVERRIDE, arms[ i ].targetHandAxis ); } }
/* ============ idAASLocal::DrawEdge ============ */ void idAASLocal::DrawEdge( int edgeNum, bool arrow ) const { const aasEdge_t *edge; idVec4 *color; if ( !file ) { return; } edge = &file->GetEdge( edgeNum ); color = &colorRed; if ( arrow ) { gameRenderWorld->DebugArrow( *color, file->GetVertex( edge->vertexNum[0] ), file->GetVertex( edge->vertexNum[1] ), 1 ); } else { gameRenderWorld->DebugLine( *color, file->GetVertex( edge->vertexNum[0] ), file->GetVertex( edge->vertexNum[1] ) ); } if ( gameLocal.GetLocalPlayer() ) { gameRenderWorld->DrawText( va( "%d", edgeNum ), ( file->GetVertex( edge->vertexNum[0] ) + file->GetVertex( edge->vertexNum[1] ) ) * 0.5f + idVec3(0,0,4), 0.1f, colorRed, gameLocal.GetLocalPlayer()->viewAxis ); } }
/* ================ sdScriptedEntityHelper_LegIk::Update ================ */ void sdScriptedEntityHelper_LegIk::Update( bool postThink ) { if ( !gameLocal.isNewFrame || postThink ) { return; } if ( gameLocal.time - startTime > lifetime ) { delete this; return; } idMat3 upperJointAxis, middleJointAxis, lowerJointAxis; idVec3 upperJointOrg, middleJointOrg, lowerJointOrg; ClearJointMods(); idAnimator* animator = _owner->GetAnimator(); if ( !animator->GetJointTransform( upperLegJoint, gameLocal.time, upperJointOrg, upperJointAxis ) ) { delete this; return; } if ( !animator->GetJointTransform( middleLegJoint, gameLocal.time, middleJointOrg, middleJointAxis ) ) { delete this; return; } if ( animator->GetJointTransform( lowerLegJoint, gameLocal.time, lowerJointOrg, lowerJointAxis ) ) { delete this; return; } { idVec3 traceOrg = _owner->GetRenderEntity()->origin + ( lowerJointOrg * _owner->GetRenderEntity()->axis ); trace_t tr; gameLocal.clip.TracePoint( CLIP_DEBUG_PARMS tr, traceOrg + idVec3( 0, 0, maxUpTrace ), traceOrg - idVec3( 0, 0, maxDownTrace ), CONTENTS_SOLID, _owner ); /* gameRenderWorld->DebugCircle( colorGreen, tr.endpos, idVec3( 0, 0, 1 ), 8, 16 ); gameRenderWorld->DebugLine( colorRed, tr.endpos, tr.endpos + idVec3( 0.f, 0.f, currentGroundOffset ) ); gameRenderWorld->DebugLine( colorBlue, tr.endpos, tr.endpos + idVec3( 0.f, 0.f, lowerJointOrg.z ) );*/ currentGroundOffset = Lerp( currentGroundOffset, lowerJointOrg.z, blendRate * MS2SEC( gameLocal.msec ) ); idVec3 jointPos = ( ( tr.endpos + idVec3( 0.f, 0.f, currentGroundOffset ) ) - _owner->GetRenderEntity()->origin ) * _owner->GetRenderEntity()->axis.Transpose(); idVec3 endPos; idIK::SolveTwoBones( upperJointOrg, jointPos, -upDir, upperLength, lowerLength, endPos ); idMat3 axis; idIK::GetBoneAxis( upperJointOrg, endPos, upDir, axis ); idMat3 upperAxis = midToUpperJoint * axis; idIK::GetBoneAxis( endPos, jointPos, upDir, axis ); idMat3 middleAxis = lowerToMidJoint * axis; animator->SetJointAxis( upperLegJoint, JOINTMOD_WORLD_OVERRIDE, upperAxis ); animator->SetJointAxis( middleLegJoint, JOINTMOD_WORLD_OVERRIDE, middleAxis ); /* idVec3 worldOrg; _owner->GetWorldOrigin( upperLegJoint, worldOrg ); gameRenderWorld->DebugLine( colorGreen, worldOrg, worldOrg + ( ( side * _owner->GetRenderEntity()->axis ) * 16 ) ); gameRenderWorld->DebugLine( colorBlue, worldOrg, worldOrg + ( ( up * _owner->GetRenderEntity()->axis ) * 16 ) ); gameRenderWorld->DebugLine( colorRed, worldOrg, worldOrg + ( ( temp * _owner->GetRenderEntity()->axis ) * 16 ) );*/ } }
/* ================ idThread::Event_VecToAngles ================ */ void idThread::Event_VecToAngles( idVec3 &vec ) { idAngles ang = vec.ToAngles(); ReturnVector( idVec3( ang[0], ang[1], ang[2] ) ); }
void InvestigateSpotTask::Init(idAI* owner, Subsystem& subsystem) { // Just init the base class Task::Init(owner, subsystem); // Get a shortcut reference Memory& memory = owner->GetMemory(); // Stop previous moves //owner->StopMove(MOVE_STATUS_DONE); if (memory.currentSearchSpot != idVec3(idMath::INFINITY, idMath::INFINITY, idMath::INFINITY)) { // Set the goal position SetNewGoal(memory.currentSearchSpot); memory.hidingSpotInvestigationInProgress = true; // grayman #3857 memory.stopHidingSpotInvestigation = false; } else { // Invalid hiding spot, terminate task subsystem.FinishTask(); } // grayman #3857 - Only the first searcher in a search is allowed to investigate // the spot closely. Am I it? if (_investigateClosely) { Search* search = gameLocal.m_searchManager->GetSearch(owner->m_searchID); if (search) { if (search->_searcherCount > 0) { Assignment *assignment = &search->_assignments[0]; // first AI assigned to the search if (assignment->_searcher != owner) { _investigateClosely = false; } } } } if (owner->HasSeenEvidence()) { // Draw weapon, if we haven't already if (!owner->GetAttackFlag(COMBAT_MELEE) && !owner->GetAttackFlag(COMBAT_RANGED)) { if ( ( owner->GetNumRangedWeapons() > 0 ) && !owner->spawnArgs.GetBool("unarmed_ranged","0") ) { owner->DrawWeapon(COMBAT_RANGED); } else if ( ( owner->GetNumMeleeWeapons() > 0 ) && !owner->spawnArgs.GetBool("unarmed_melee","0") ) { owner->DrawWeapon(COMBAT_MELEE); } } } //_exitTime = 0; // grayman #3507 }