etqw-sdk/source/game/misc/Parachute.cpp

278 lines
7.7 KiB
C++
Raw Normal View History

2008-05-29 00:00:00 +00:00
// Copyright (C) 2007 Id Software, Inc.
//
#include "../precompiled.h"
#pragma hdrstop
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#include "Parachute.h"
#include "../PredictionErrorDecay.h"
/*
===============================================================================
sdParachute
===============================================================================
*/
extern const idEventDef EV_SetOwner;
const idEventDef EV_SetDeployStart( "setDeployStart", '\0', DOC_TEXT( "Sets the game time at which the parachute should start applying force." ), 1, NULL, "f", "time", "Game time in seconds." );
const idEventDef EV_IsMovingTooSlow( "isMovingTooSlow", 'b', DOC_TEXT( "Returns whether the owner is moving too slow to be considered parachuting any more." ), 0, NULL );
CLASS_DECLARATION( sdScriptEntity, sdParachute )
EVENT( EV_SetOwner, sdParachute::Event_SetOwner )
EVENT( EV_SetDeployStart, sdParachute::Event_SetDeployStart )
EVENT( EV_IsMovingTooSlow, sdParachute::Event_IsMovingTooSlow )
END_CLASS
/*
================
sdParachute::Spawn
================
*/
void sdParachute::Spawn( void ) {
maxSpeed = spawnArgs.GetFloat( "min_speed" );
deployStartTime = -1;
deployTime = SEC2MS( spawnArgs.GetFloat( "deploy_time", "1" ) );
tooSlowTime = 0;
radius = InchesToMetres( spawnArgs.GetFloat( "chute_diameter" ) / 2 );
height = InchesToMetres( spawnArgs.GetFloat( "chute_height" ) / 2 );
forceHeight = spawnArgs.GetFloat( "force_height" );
Cd_up = spawnArgs.GetFloat( "drag_coefficient_up" );
Cd_side = spawnArgs.GetFloat( "drag_coefficient_side" );
Cl_side = spawnArgs.GetFloat( "lift_coefficient_side" );
rho = spawnArgs.GetFloat( "air_density" );
maxSideDrag = spawnArgs.GetFloat( "drag_side_max", "600" );
scale = 1.0f;
ownerOffset.Zero();
}
/*
================
sdParachute::Think
================
*/
void sdParachute::Think( void ) {
if ( owner.IsValid() ) {
// convert velocity to metric
idVec3 velocity = InchesToMetres( owner->GetPhysics()->GetLinearVelocity() );
float speedSquared = velocity.LengthSqr();
if ( velocity.z > -maxSpeed ) {
if ( !gameLocal.isClient ) {
if ( !tooSlowTime ) {
tooSlowTime = gameLocal.time;
}
}
} else if ( speedSquared > 0.01f ) {
tooSlowTime = 0;
if ( deployStartTime > 0 ) {
float canopyScale = 1.0f;
float time = gameLocal.time - deployStartTime;
if ( time < deployTime ) {
canopyScale = time / deployTime;
}
ApplyParachute( owner, canopyScale );
}
}
}
sdScriptEntity::Think();
}
/*
================
sdParachute::ApplyParachute
================
*/
void sdParachute::ApplyParachute( idEntity* owner, float canopyScale ) {
// calculate how much to scale the parachute size by based on the owner's mass
if ( owner->GetPhysics()->GetMass() > 0.0f ) {
scale = idMath::Sqrt( owner->GetPhysics()->GetMass() / 100.0f );
} else {
scale = 1.0f;
}
canopyScale *= scale;
const idMat3& axis = GetPhysics()->GetAxis();
const idVec3& trueUp = axis[ 2 ];
idVec3 forceUp( 0, 0, 1.0f );
idVec3 velocity = InchesToMetres( owner->GetPhysics()->GetLinearVelocity() );
// take angular velocity into account
idVec3 angVel = owner->GetPhysics()->GetAngularVelocity();
angVel.z = 0.0f; // ignore yaw
// calculate axis
idVec3 angVelAxis = angVel;
float angVelMagnitude = -angVelAxis.Normalize();
// calculate direction along circle of rotation
idVec3 tangent = trueUp.Cross( angVelAxis );
idVec3 angVelResult = angVelMagnitude * InchesToMetres( forceHeight ) * canopyScale * tangent * 2.0f;
angVelResult = angVelResult * axis;
velocity += angVelResult;
float upSpeed = velocity * forceUp;
idVec3 sideVel = velocity - upSpeed * forceUp;
float sideSpeedSq = sideVel.LengthSqr();
idVec3 upDragDirection = forceUp;
idVec3 sideDragDirection = velocity; //velocity.Normalize();
sideDragDirection.Normalize();
float upComponent = -( upDragDirection * sideDragDirection );
sideDragDirection += upComponent * upDragDirection;
//
// Up axis
//
// don't react if in the opposite direction
if ( upComponent < 0 ) {
upComponent = 0;
}
float currentRadius = radius * canopyScale;
float canopyArea = idMath::PI * currentRadius * currentRadius * upComponent;
// now do drag on this axis
float upDragMagnitude = 0.5 * Cd_up * canopyArea * rho * upSpeed * upSpeed;
idVec3 dragForce = upDragMagnitude * upDragDirection;
//
// Side axis
//
// calculate the cross-sectional area of the side profile of the canopy
float sideArea = 2 * currentRadius * height * canopyScale * canopyScale;
// now do drag on this axis
float sideDragMagnitude = 0.5 * Cd_side * sideArea * rho * sideSpeedSq;
if ( sideDragMagnitude > maxSideDrag ) {
sideDragMagnitude = maxSideDrag;
} else if ( sideDragMagnitude < -maxSideDrag ) {
sideDragMagnitude = -maxSideDrag;
}
dragForce = dragForce + sideDragMagnitude * sideDragDirection;
//
// Apply the forces
//
// convert this to an impulse
idVec3 dragImpulse = dragForce * MS2SEC( gameLocal.msec );
// this impulse is in kg.m/s - convert to game units (kg.in/s)
dragImpulse = MetresToInches( dragImpulse );
// apply it to the object
owner->GetPhysics()->ApplyImpulse( 0, GetPhysics()->GetOrigin() + trueUp * forceHeight, dragImpulse );
}
/*
================
sdParachute::UpdateModelTransform
================
*/
void sdParachute::UpdateModelTransform( void ) {
if ( IsBound() ) {
// HACK: get the offset & angles just like the script does
idVec3 origin;
idMat3 axis;
idEntity* master = GetBindMaster();
idPlayer* playerMaster = master->Cast< idPlayer >();
if ( playerMaster != NULL ) {
origin = GetBindMaster()->GetLastPushedOrigin();
origin.z += 80.0f;
idAngles angles( 0.0f, 0.0f, 0.0f );
angles.yaw = GetBindMaster()->GetLastPushedAxis().ToAngles().yaw;
axis = angles.ToMat3();
} else {
origin = master->GetPhysics()->GetCenterOfMass();
origin = origin * GetBindMaster()->GetLastPushedAxis() + GetBindMaster()->GetLastPushedOrigin();
axis = GetBindMaster()->GetLastPushedAxis();
}
origin += axis * ownerOffset;
// get the offset from the bind master
renderEntity.origin = origin;
renderEntity.axis = axis;
} else {
renderEntity.origin = GetPhysics()->GetOrigin();
renderEntity.axis = GetPhysics()->GetAxis();
}
DoPredictionErrorDecay();
}
/*
================
sdParachute::Present
================
*/
void sdParachute::Present( void ) {
// HACK: ensure its in the right position relative to the owner before pushing to renderer
UpdateModelTransform();
sdScriptEntity::Present();
// make sure this entity is being interpolated after the entity it is bound to
if ( !gameLocal.unlock.unlockedDraw && IsBound() ) {
idEntity* master = GetBindMaster();
if ( master->interpolateNode.InList() ) {
interpolateNode.InsertAfter( master->interpolateNode );
}
}
}
/*
================
sdParachute::Event_SetOwner
================
*/
void sdParachute::Event_SetOwner( idEntity* _owner ) {
owner = _owner;
if( owner == NULL ) {
ownerOffset.Zero();
} else {
ownerOffset = owner->spawnArgs.GetVector( "parachute_offset" );
}
}
/*
================
sdParachute::Event_SetDeployStart
================
*/
void sdParachute::Event_SetDeployStart( float time ) {
deployStartTime = SEC2MS( time );
}
/*
================
sdParachute::Event_IsMovingTooSlow
================
*/
void sdParachute::Event_IsMovingTooSlow( void ) {
sdProgram::ReturnBoolean( tooSlowTime != 0 && ( gameLocal.time - tooSlowTime > SEC2MS( 0.1f ) ) );
}