quake4-sdk/source/mpgame/SplineMover.cpp

3290 lines
75 KiB
C++

#include "../idlib/precompiled.h"
#pragma hdrstop
#include "Game_local.h"
#include "spawner.h"
const idEventDef EV_OnAcceleration( "<onAcceleration>" );
const idEventDef EV_OnDeceleration( "<onDeceleration>" );
const idEventDef EV_OnCruising( "<onCruising>" );
const idEventDef EV_OnStartMoving( "<onStartMoving>" );
const idEventDef EV_OnStopMoving( "<onStopMoving>" );
//=======================================================
//
// rvPhysics_Spline
//
//=======================================================
CLASS_DECLARATION( idPhysics_Base, rvPhysics_Spline )
EVENT( EV_PostRestore, rvPhysics_Spline::Event_PostRestore )
END_CLASS
void splinePState_t::ApplyAccelerationDelta( float timeStepSec ) {
speed = SignZero(idealSpeed) * Min( idMath::Fabs(idealSpeed), idMath::Fabs(speed) + acceleration * timeStepSec );
}
void splinePState_t::ApplyDecelerationDelta( float timeStepSec ) {
speed = SignZero(speed) * Max( idMath::Fabs(idealSpeed), idMath::Fabs(speed) - deceleration * timeStepSec );
}
void splinePState_t::UpdateDist( float timeStepSec ) {
dist += speed * timeStepSec;
}
bool splinePState_t::ShouldAccelerate() const {
if( Sign(idealSpeed) == Sign(speed) ) {
return idMath::Fabs(speed) < idMath::Fabs(idealSpeed);
} else if( !Sign(speed) ) {
return true;
}
return false;
}
bool splinePState_t::ShouldDecelerate() const {
if( Sign(speed) == Sign(idealSpeed) ) {
return idMath::Fabs(speed) > idMath::Fabs(idealSpeed);
} else if( !Sign(idealSpeed) ) {
return true;
}
return false;
}
void splinePState_t::Clear() {
origin.Zero();
localOrigin.Zero();
axis.Identity();
localAxis.Identity();
speed = 0.0f;
idealSpeed = 0.0f;
dist = 0.0f;
acceleration = 0.0f;
deceleration = 0.0f;
}
void splinePState_t::WriteToSnapshot( idBitMsgDelta &msg ) const {
msg.WriteDeltaVec3( vec3_zero, origin );
msg.WriteDeltaVec3( vec3_zero, localOrigin );
msg.WriteDeltaMat3( mat3_identity, axis );
msg.WriteDeltaMat3( mat3_identity, localAxis );
msg.WriteDeltaFloat( 0.0f, speed );
msg.WriteDeltaFloat( 0.0f, idealSpeed );
msg.WriteDeltaFloat( 0.0f, dist );
msg.WriteDeltaFloat( 0.0f, acceleration );
msg.WriteDeltaFloat( 0.0f, deceleration );
}
void splinePState_t::ReadFromSnapshot( const idBitMsgDelta &msg ) {
origin = msg.ReadDeltaVec3( vec3_zero );
localOrigin = msg.ReadDeltaVec3( vec3_zero );
axis = msg.ReadDeltaMat3( mat3_identity );
localAxis = msg.ReadDeltaMat3( mat3_identity );
speed = msg.ReadDeltaFloat( 0.0f );
idealSpeed = msg.ReadDeltaFloat( 0.0f );
dist = msg.ReadDeltaFloat( 0.0f );
acceleration = msg.ReadDeltaFloat( 0.0f );
deceleration = msg.ReadDeltaFloat( 0.0f );
}
void splinePState_t::Save( idSaveGame *savefile ) const {
savefile->WriteVec3( origin );
savefile->WriteVec3( localOrigin );
savefile->WriteMat3( axis );
savefile->WriteMat3( localAxis );
savefile->WriteFloat( speed );
savefile->WriteFloat( idealSpeed );
savefile->WriteFloat( dist );
savefile->WriteFloat( acceleration );
savefile->WriteFloat( deceleration );
}
void splinePState_t::Restore( idRestoreGame *savefile ) {
savefile->ReadVec3( origin );
savefile->ReadVec3( localOrigin );
savefile->ReadMat3( axis );
savefile->ReadMat3( localAxis );
savefile->ReadFloat( speed );
savefile->ReadFloat( idealSpeed );
savefile->ReadFloat( dist );
savefile->ReadFloat( acceleration );
savefile->ReadFloat( deceleration );
}
splinePState_t& splinePState_t::Assign( const splinePState_t* state ) {
SIMDProcessor->Memcpy( this, state, sizeof(splinePState_t) );
return *this;
}
splinePState_t& splinePState_t::operator=( const splinePState_t& state ) {
return Assign( &state );
}
splinePState_t& splinePState_t::operator=( const splinePState_t* state ) {
return Assign( state );
}
/*
================
rvPhysics_Spline::rvPhysics_Spline
================
*/
rvPhysics_Spline::rvPhysics_Spline( void ) {
accelDecelStateThread.SetName( "AccelDecel" );
accelDecelStateThread.SetOwner( this );
accelDecelStateThread.SetState( "Cruising" );
clipModel = NULL;
spline = NULL;
SetSplineEntity( NULL );
memset( &pushResults, 0, sizeof(trace_t) );
pushResults.fraction = 1.0f;
current.Clear();
SaveState();
}
/*
================
rvPhysics_Spline::~rvPhysics_Spline
================
*/
rvPhysics_Spline::~rvPhysics_Spline( void ) {
SAFE_DELETE_PTR( clipModel );
SAFE_DELETE_PTR( spline );
}
/*
================
rvPhysics_Spline::Save
================
*/
void rvPhysics_Spline::Save( idSaveGame *savefile ) const {
current.Save( savefile );
saved.Save( savefile );
savefile->WriteFloat( splineLength );
// This spline was retored as NULL, so there's no reason to save it.
//savefile->WriteInt(spline != NULL ? spline->GetTime( 0 ) : -1 ); // cnicholson: Added unsaved var
splineEntity.Save( savefile );
savefile->WriteTrace( pushResults );
savefile->WriteClipModel( clipModel );
accelDecelStateThread.Save( savefile );
}
/*
================
rvPhysics_Spline::Restore
================
*/
void rvPhysics_Spline::Event_PostRestore( void ) {
if( splineEntity.IsValid() ) {
spline = splineEntity->GetSpline();
}
}
void rvPhysics_Spline::Restore( idRestoreGame *savefile ) {
current.Restore( savefile );
saved.Restore( savefile );
savefile->ReadFloat( splineLength );
SAFE_DELETE_PTR( spline );
splineEntity.Restore( savefile );
savefile->ReadTrace( pushResults );
savefile->ReadClipModel( clipModel );
accelDecelStateThread.Restore( savefile, this );
}
/*
================
rvPhysics_Spline::SetClipModel
================
*/
void rvPhysics_Spline::SetClipModel( idClipModel *model, const float density, int id, bool freeOld ) {
assert( self );
assert( model ); // we need a clip model
if ( clipModel && clipModel != model && freeOld ) {
delete clipModel;
}
clipModel = model;
LinkClip();
}
/*
================
rvPhysics_Spline::GetClipModel
================
*/
idClipModel *rvPhysics_Spline::GetClipModel( int id ) const {
return clipModel;
}
/*
================
rvPhysics_Spline::SetContents
================
*/
void rvPhysics_Spline::SetContents( int contents, int id ) {
clipModel->SetContents( contents );
}
/*
================
rvPhysics_Spline::GetContents
================
*/
int rvPhysics_Spline::GetContents( int id ) const {
return clipModel->GetContents();
}
/*
================
rvPhysics_Spline::GetBounds
================
*/
const idBounds &rvPhysics_Spline::GetBounds( int id ) const {
return clipModel->GetBounds();
}
/*
================
rvPhysics_Spline::GetAbsBounds
================
*/
const idBounds &rvPhysics_Spline::GetAbsBounds( int id ) const {
return clipModel->GetAbsBounds();
}
/*
================
rvPhysics_Spline::SetSpline
================
*/
void rvPhysics_Spline::SetSpline( idCurve_Spline<idVec3>* spline ) {
SAFE_DELETE_PTR( this->spline );
//Keep any left over dist from last spline to minimize hitches
if( GetSpeed() >= 0.0f ) {
current.dist = Max( 0.0f, current.dist - splineLength );
}
if( !spline ) {
splineLength = 0.0f;
return;
}
this->spline = spline;
splineLength = spline->GetLengthForTime( spline->GetTime(spline->GetNumValues() - 1) );
if( GetSpeed() < 0.0f ) {
current.dist = splineLength - current.dist;
}
Activate();
}
/*
================
rvPhysics_Spline::SetSplineEntity
================
*/
void rvPhysics_Spline::SetSplineEntity( idSplinePath* spline ) {
splineEntity = spline;
SetSpline( (spline) ? spline->GetSpline() : NULL );
}
/*
================
rvPhysics_Spline::ComputeDecelFromSpline
================
*/
float rvPhysics_Spline::ComputeDecelFromSpline() const {
// FIXME: merge this in better. It seems very special case
float numerator = GetSpeed() * GetSpeed();
float denomonator = 2.0f * ((GetSpeed() >= 0.0f) ? (splineLength - current.dist) : current.dist);
assert( denomonator > VECTOR_EPSILON );
return numerator / denomonator;
}
/*
================
rvPhysics_Spline::SetLinearAcceleration
================
*/
void rvPhysics_Spline::SetLinearAcceleration( const float accel ) {
current.acceleration = accel;
}
/*
================
rvPhysics_Spline::SetLinearDeceleration
================
*/
void rvPhysics_Spline::SetLinearDeceleration( const float decel ) {
current.deceleration = decel;
}
/*
================
rvPhysics_Spline::SetSpeed
================
*/
void rvPhysics_Spline::SetSpeed( float speed ) {
if( IsAtRest() || StoppedMoving() ) {
current.dist = (speed < 0.0f) ? splineLength - current.dist : current.dist;
}
current.idealSpeed = speed;
Activate();
}
/*
================
rvPhysics_Spline::GetSpeed
================
*/
float rvPhysics_Spline::GetSpeed() const {
return current.speed;
}
/*
================
rvPhysics_Spline::Evaluate
================
*/
bool rvPhysics_Spline::Evaluate( int timeStepMSec, int endTimeMSec ) {
idVec3 masterOrigin;
idMat3 masterAxis;
splinePState_t previous = current;
if( HasValidSpline() ) {
if( StoppedMoving() ) {
Rest();
return false;
}
accelDecelStateThread.Execute();
// FIXME: clean this up
if( IsAtBeginningOfSpline() || IsAtEndOfSpline() ) {
current = previous;
Rest();
self->ProcessEvent( &EV_DoneMoving );
if( gameLocal.program.GetReturnedBool() ) {
current.speed = 0.0f;
return false;
} else {
return true;
}
}
float currentTime = splineEntity->GetSampledTime ( current.dist );
if ( currentTime == -1.0f ) {
currentTime = spline->GetTimeForLength( Min(current.dist, splineLength), 0.01f );
}
current.axis = spline->GetCurrentFirstDerivative(currentTime).ToAngles().Normalize360().ToMat3();
current.origin = spline->GetCurrentValue( currentTime );
current.localOrigin = current.origin;
current.localAxis = current.axis;
} else if( self->IsBound() ) {
self->GetMasterPosition( masterOrigin, masterAxis );
current.axis = current.localAxis * masterAxis;
current.origin = masterOrigin + current.localOrigin * masterAxis;
} else {
Rest();
return false;
}
gameLocal.push.ClipPush( pushResults, self, 0, previous.origin, previous.axis, current.origin, current.axis );
if( pushResults.fraction < 1.0f ) {
current = previous;
LinkClip();
current.speed = 0.0f;
return false;
}
LinkClip();
if( StoppedMoving() && !self->IsBound() ) {
Rest();
self->ProcessEvent( &EV_DoneMoving );
return !gameLocal.program.GetReturnedBool();
}
return true;
}
/*
================
rvPhysics_Spline::Activate
================
*/
void rvPhysics_Spline::Activate( void ) {
assert( self );
self->BecomeActive( TH_PHYSICS );
}
/*
================
rvPhysics_Spline::Rest
================
*/
void rvPhysics_Spline::Rest( void ) {
assert( self );
self->BecomeInactive( TH_PHYSICS );
}
/*
================
rvPhysics_Spline::IsAtRest
================
*/
bool rvPhysics_Spline::IsAtRest( void ) const {
assert( self );
return !self->IsActive( TH_PHYSICS );
}
/*
================
rvPhysics_Spline::IsAtEndOfSpline
================
*/
bool rvPhysics_Spline::IsAtEndOfSpline( void ) const {
return current.dist >= splineLength;
}
/*
================
rvPhysics_Spline::IsAtBeginningOfSpline
================
*/
bool rvPhysics_Spline::IsAtBeginningOfSpline( void ) const {
return current.dist <= 0.0f;
}
/*
================
rvPhysics_Spline::IsPushable
================
*/
bool rvPhysics_Spline::IsPushable( void ) const {
return !HasValidSpline() && idPhysics_Base::IsPushable();
}
/*
================
rvPhysics_Spline::StartingToMove
================
*/
bool rvPhysics_Spline::StartingToMove( void ) const {
float firstDeltaSpeed = current.acceleration * MS2SEC(gameLocal.GetMSec());
return idMath::Fabs(current.idealSpeed) > VECTOR_EPSILON && idMath::Fabs(current.speed) <= firstDeltaSpeed;
}
/*
================
rvPhysics_Spline::StoppedMoving
================
*/
bool rvPhysics_Spline::StoppedMoving( void ) const {
return idMath::Fabs(current.idealSpeed) < VECTOR_EPSILON && idMath::Fabs(current.speed) < VECTOR_EPSILON;
}
/*
================
rvPhysics_Spline::HasValidSpline
================
*/
bool rvPhysics_Spline::HasValidSpline() const {
return spline && splineLength > VECTOR_EPSILON;
}
/*
================
rvPhysics_Spline::SaveState
================
*/
void rvPhysics_Spline::SaveState( void ) {
saved = current;
}
/*
================
rvPhysics_Spline::RestoreState
================
*/
void rvPhysics_Spline::RestoreState( void ) {
current = saved;
LinkClip();
}
/*
================
idPhysics::SetOrigin
================
*/
void rvPhysics_Spline::SetOrigin( const idVec3 &newOrigin, int id ) {
idVec3 masterOrigin;
idMat3 masterAxis;
current.localOrigin = newOrigin;
if( self->IsBound() ) {
self->GetMasterPosition( masterOrigin, masterAxis );
current.origin = masterOrigin + current.localOrigin * masterAxis;
}
else {
current.origin = current.localOrigin;
}
LinkClip();
Activate();
}
/*
================
idPhysics::SetAxis
================
*/
void rvPhysics_Spline::SetAxis( const idMat3 &newAxis, int id ) {
idVec3 masterOrigin;
idMat3 masterAxis;
current.localAxis = newAxis;
if ( self->IsBound() ) {
self->GetMasterPosition( masterOrigin, masterAxis );
current.axis = newAxis * masterAxis;
}
else {
current.axis = newAxis;
}
LinkClip();
Activate();
}
/*
================
rvPhysics_Spline::Translate
================
*/
void rvPhysics_Spline::Translate( const idVec3 &translation, int id ) {
SetOrigin( GetLocalOrigin() + translation );
}
/*
================
rvPhysics_Spline::Rotate
================
*/
void rvPhysics_Spline::Rotate( const idRotation &rotation, int id ) {
SetAxis( GetLocalAxis() * rotation.ToMat3() );
SetOrigin( GetLocalOrigin() * rotation );
}
/*
================
rvPhysics_Spline::GetOrigin
================
*/
const idVec3 &rvPhysics_Spline::GetOrigin( int id ) const {
return current.origin;
}
/*
================
rvPhysics_Spline::GetAxis
================
*/
const idMat3 &rvPhysics_Spline::GetAxis( int id ) const {
return current.axis;
}
/*
================
rvPhysics_Spline::GetOrigin
================
*/
idVec3 &rvPhysics_Spline::GetOrigin( int id ) {
return current.origin;
}
/*
================
rvPhysics_Spline::GetAxis
================
*/
idMat3 &rvPhysics_Spline::GetAxis( int id ) {
return current.axis;
}
/*
================
rvPhysics_Spline::GetLocalOrigin
================
*/
const idVec3 &rvPhysics_Spline::GetLocalOrigin( int id ) const {
return current.localOrigin;
}
/*
================
rvPhysics_Spline::GetLocalAxis
================
*/
const idMat3 &rvPhysics_Spline::GetLocalAxis( int id ) const {
return current.localAxis;
}
/*
================
rvPhysics_Spline::GetLocalOrigin
================
*/
idVec3 &rvPhysics_Spline::GetLocalOrigin( int id ) {
return current.localOrigin;
}
/*
================
rvPhysics_Spline::GetLocalAxis
================
*/
idMat3 &rvPhysics_Spline::GetLocalAxis( int id ) {
return current.localAxis;
}
/*
================
rvPhysics_Spline::SetMaster
================
*/
void rvPhysics_Spline::SetMaster( idEntity *master, const bool orientated ) {
idVec3 masterOrigin;
idMat3 masterAxis;
if( master ) {
if( self->IsBound() ) {
// transform from world space to master space
self->GetMasterPosition( masterOrigin, masterAxis );
current.localOrigin = ( GetOrigin() - masterOrigin ) * masterAxis.Transpose();
current.localAxis = GetAxis() * masterAxis.Transpose();
}
}
}
/*
================
rvPhysics_Spline::ClipTranslation
================
*/
void rvPhysics_Spline::ClipTranslation( trace_t &results, const idVec3 &translation, const idClipModel *model ) const {
if ( model ) {
gameLocal.TranslationModel( self, results, GetOrigin(), GetOrigin() + translation,
clipModel, GetAxis(), clipMask,
model->GetCollisionModel(), model->GetOrigin(), model->GetAxis() );
}
else {
gameLocal.Translation( self, results, GetOrigin(), GetOrigin() + translation,
clipModel, GetAxis(), clipMask, self );
}
}
/*
================
rvPhysics_Spline::ClipRotation
================
*/
void rvPhysics_Spline::ClipRotation( trace_t &results, const idRotation &rotation, const idClipModel *model ) const {
if ( model ) {
gameLocal.RotationModel( self, results, GetOrigin(), rotation,
clipModel, GetAxis(), clipMask,
model->GetCollisionModel(), model->GetOrigin(), model->GetAxis() );
}
else {
gameLocal.Rotation( self, results, GetOrigin(), rotation,
clipModel, GetAxis(), clipMask, self );
}
}
/*
================
rvPhysics_Spline::ClipContents
================
*/
int rvPhysics_Spline::ClipContents( const idClipModel *model ) const {
if ( model ) {
// RAVEN BEGIN
// ddynerman: multiple clip worlds
return gameLocal.ContentsModel( self, GetOrigin(), clipModel, GetAxis(), -1,
model->GetCollisionModel(), model->GetOrigin(), model->GetAxis() );
}
else {
return gameLocal.Contents( self, GetOrigin(), clipModel, GetAxis(), -1, NULL );
// RAVEN END
}
}
/*
================
rvPhysics_Spline::DisableClip
================
*/
void rvPhysics_Spline::DisableClip( void ) {
if( clipModel ) {
clipModel->Disable();
}
}
/*
================
rvPhysics_Spline::EnableClip
================
*/
void rvPhysics_Spline::EnableClip( void ) {
if( clipModel ) {
clipModel->Enable();
}
}
/*
================
rvPhysics_Spline::UnlinkClip
================
*/
void rvPhysics_Spline::UnlinkClip( void ) {
if( clipModel ) {
clipModel->Unlink();
}
}
/*
================
rvPhysics_Spline::LinkClip
================
*/
void rvPhysics_Spline::LinkClip( void ) {
if( clipModel ) {
// RAVEN BEGIN
// ddynerman: multiple clip worlds
clipModel->Link( self, clipModel->GetId(), GetOrigin(), GetAxis() );
// RAVEN END
}
}
/*
================
rvPhysics_Spline::GetBlockingInfo
================
*/
const trace_t* rvPhysics_Spline::GetBlockingInfo( void ) const {
return (pushResults.fraction < 1.0f) ? &pushResults : NULL;
}
/*
================
rvPhysics_Spline::GetBlockingEntity
================
*/
idEntity* rvPhysics_Spline::GetBlockingEntity( void ) const {
return (pushResults.fraction < 1.0f) ? gameLocal.entities[ pushResults.c.entityNum ] : NULL;
}
/*
================
rvPhysics_Spline::WriteToSnapshot
================
*/
void rvPhysics_Spline::WriteToSnapshot( idBitMsgDelta &msg ) const {
current.WriteToSnapshot( msg );
}
/*
================
rvPhysics_Spline::ReadFromSnapshot
================
*/
void rvPhysics_Spline::ReadFromSnapshot( const idBitMsgDelta &msg ) {
current.ReadFromSnapshot( msg );
LinkClip();
}
CLASS_STATES_DECLARATION( rvPhysics_Spline )
STATE( "Accelerating", rvPhysics_Spline::State_Accelerating )
STATE( "Decelerating", rvPhysics_Spline::State_Decelerating )
STATE( "Cruising", rvPhysics_Spline::State_Cruising )
END_CLASS_STATES
/*
================
rvPhysics_Spline::State_Accelerating
================
*/
stateResult_t rvPhysics_Spline::State_Accelerating( const stateParms_t& parms ) {
stateResult_t returnResult = SRESULT_WAIT;
if( !current.ShouldAccelerate() ) {
accelDecelStateThread.SetState( current.ShouldDecelerate() ? "Decelerating" : "Cruising" );
return SRESULT_DONE;
}
if( !parms.stage ) {
if( StartingToMove() ) {
self->ProcessEvent( &EV_OnStartMoving );
}
self->ProcessEvent( &EV_OnAcceleration );
returnResult = SRESULT_STAGE( parms.stage + 1 );
}
float timeStepSec = MS2SEC( gameLocal.GetMSec() );
current.ApplyAccelerationDelta( timeStepSec );
current.UpdateDist( timeStepSec );
return returnResult;
}
/*
================
rvPhysics_Spline::State_Decelerating
================
*/
stateResult_t rvPhysics_Spline::State_Decelerating( const stateParms_t& parms ) {
if( !current.ShouldDecelerate() ) {
accelDecelStateThread.SetState( current.ShouldAccelerate() ? "Accelerating" : "Cruising" );
return SRESULT_DONE;
}
float timeStepSec = MS2SEC( gameLocal.GetMSec() );
current.ApplyDecelerationDelta( timeStepSec );
current.UpdateDist( timeStepSec );
if( !parms.stage ) {
self->ProcessEvent( &EV_OnDeceleration );
return SRESULT_STAGE( parms.stage + 1 );
}
if( StoppedMoving() ) {
self->ProcessEvent( &EV_OnStopMoving );
}
return SRESULT_WAIT;
}
/*
================
rvPhysics_Spline::State_Cruising
================
*/
stateResult_t rvPhysics_Spline::State_Cruising( const stateParms_t& parms ) {
if( current.ShouldAccelerate() ) {
accelDecelStateThread.SetState( "Accelerating" );
return SRESULT_DONE;
} else if( current.ShouldDecelerate() ) {
accelDecelStateThread.SetState( "Decelerating" );
return SRESULT_DONE;
}
current.UpdateDist( MS2SEC(gameLocal.GetMSec()) );
if( !parms.stage ) {
self->ProcessEvent( &EV_OnCruising );
return SRESULT_STAGE( parms.stage + 1 );
}
return SRESULT_WAIT;
}
const idEventDef EV_SetSpline( "setSpline", "E" );
const idEventDef EV_SetAccel( "setAccel", "f" );
const idEventDef EV_SetDecel( "setDecel", "f" );
const idEventDef EV_SetSpeed( "setSpeed", "f" );
const idEventDef EV_GetSpeed( "getSpeed", "", 'f' );
const idEventDef EV_TramCar_SetIdealSpeed( "setIdealSpeed", "f" );
const idEventDef EV_TramCar_GetIdealSpeed( "getIdealSpeed", "", 'f' );
const idEventDef EV_TramCar_ApplySpeedScale( "applySpeedScale", "f" );
const idEventDef EV_GetCurrentTrackInfo( "getCurrentTrackInfo", "", 's' );
const idEventDef EV_GetTrackInfo( "getTrackInfo", "e", 's' );
const idEventDef EV_DoneMoving( "<doneMoving>", "", 'd' );
const idEventDef EV_StartSoundPeriodic( "<startSoundPeriodic>", "sddd" );
idLinkList<rvSplineMover> rvSplineMover::splineMovers;
//=======================================================
//
// rvSplineMover
//
//=======================================================
CLASS_DECLARATION( idAnimatedEntity, rvSplineMover )
EVENT( EV_PostSpawn, rvSplineMover::Event_PostSpawn )
EVENT( EV_Activate, rvSplineMover::Event_Activate )
EVENT( EV_SetSpline, rvSplineMover::Event_SetSpline )
EVENT( EV_SetAccel, rvSplineMover::Event_SetAcceleration )
EVENT( EV_SetDecel, rvSplineMover::Event_SetDeceleration )
EVENT( EV_SetSpeed, rvSplineMover::Event_SetSpeed )
EVENT( EV_GetSpeed, rvSplineMover::Event_GetSpeed )
EVENT( EV_Thread_SetCallback, rvSplineMover::Event_SetCallBack )
EVENT( EV_DoneMoving, rvSplineMover::Event_DoneMoving )
EVENT( EV_GetSplineEntity, rvSplineMover::Event_GetSpline )
EVENT( EV_GetCurrentTrackInfo, rvSplineMover::Event_GetCurrentTrackInfo )
EVENT( EV_GetTrackInfo, rvSplineMover::Event_GetTrackInfo )
EVENT( EV_TramCar_SetIdealSpeed, rvSplineMover::Event_SetIdealSpeed )
EVENT( EV_TramCar_GetIdealSpeed, rvSplineMover::Event_GetIdealSpeed )
EVENT( EV_TramCar_ApplySpeedScale, rvSplineMover::Event_ApplySpeedScale )
EVENT( EV_OnAcceleration, rvSplineMover::Event_OnAcceleration )
EVENT( EV_OnDeceleration, rvSplineMover::Event_OnDeceleration )
EVENT( EV_OnCruising, rvSplineMover::Event_OnCruising )
EVENT( EV_OnStartMoving, rvSplineMover::Event_OnStartMoving )
EVENT( EV_OnStopMoving, rvSplineMover::Event_OnStopMoving )
EVENT( EV_StartSoundPeriodic, rvSplineMover::Event_StartSoundPeriodic )
EVENT( EV_PartBlocked, rvSplineMover::Event_PartBlocked )
END_CLASS
/*
================
rvSplineMover::Spawn
================
*/
void rvSplineMover::Spawn() {
waitThreadId = -1;
physicsObj.SetSelf( this );
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_PUSH_HEAP_MEM(this);
// RAVEN END
physicsObj.SetClipModel( new idClipModel(GetPhysics()->GetClipModel()), 1.0f );
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_POP_HEAP();
// RAVEN END
physicsObj.SetContents( spawnArgs.GetBool("solid", "1") ? CONTENTS_SOLID : 0 );
physicsObj.SetClipMask( spawnArgs.GetBool("solidClip") ? CONTENTS_SOLID : 0 );
physicsObj.SetLinearVelocity( GetPhysics()->GetLinearVelocity() );
physicsObj.SetLinearAcceleration( spawnArgs.GetFloat("accel", "50") );
physicsObj.SetLinearDeceleration( spawnArgs.GetFloat("decel", "50") );
physicsObj.SetAxis( GetPhysics()->GetAxis() );
physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
SetPhysics( &physicsObj );
AddSelfToGlobalList();
// This is needed so we get sorted correctly
BecomeInactive( TH_PHYSICS );
BecomeActive( TH_PHYSICS );
PlayAnim( ANIMCHANNEL_ALL, "idle", 0 );
PostEventMS( &EV_PostSpawn, 0 );
}
/*
================
rvSplineMover::~rvSplineMover
================
*/
rvSplineMover::~rvSplineMover() {
RemoveSelfFromGlobalList();
SetPhysics( NULL );
}
/*
================
rvSplineMover::SetSpeed
================
*/
void rvSplineMover::SetSpeed( float newSpeed ) {
physicsObj.SetSpeed( newSpeed );
}
/*
================
rvSplineMover::GetSpeed
================
*/
float rvSplineMover::GetSpeed() const {
return physicsObj.GetSpeed();
}
/*
================
rvSplineMover::SetIdealSpeed
================
*/
void rvSplineMover::SetIdealSpeed( float newIdealSpeed ) {
idealSpeed = newIdealSpeed;
SetSpeed( newIdealSpeed );
}
/*
================
rvSplineMover::GetIdealSpeed
================
*/
float rvSplineMover::GetIdealSpeed() const {
return idealSpeed;
}
/*
================
rvSplineMover::SetSpline
================
*/
void rvSplineMover::SetSpline( idSplinePath* spline ) {
physicsObj.SetSplineEntity( spline );
CheckSplineForOverrides( physicsObj.GetSpline(), &spline->spawnArgs );
}
/*
================
rvSplineMover::GetSpline
================
*/
const idSplinePath* rvSplineMover::GetSpline() const {
return physicsObj.GetSplineEntity();
}
/*
================
rvSplineMover::GetSpline
================
*/
idSplinePath* rvSplineMover::GetSpline() {
return physicsObj.GetSplineEntity();
}
/*
================
rvSplineMover::SetAcceleration
================
*/
void rvSplineMover::SetAcceleration( float accel ) {
physicsObj.SetLinearAcceleration( accel );
}
/*
================
rvSplineMover::SetDeceleration
================
*/
void rvSplineMover::SetDeceleration( float decel ) {
physicsObj.SetLinearDeceleration( decel );
}
/*
================
rvSplineMover::CheckSplineForOverrides
================
*/
void rvSplineMover::CheckSplineForOverrides( const idCurve_Spline<idVec3>* spline, const idDict* args ) {
if( !spline || !args ) {
return;
}
int endSpline = args->GetInt( "end_spline" );
if( endSpline && Sign(endSpline) == Sign(GetSpeed()) ) {
physicsObj.SetLinearDeceleration( physicsObj.ComputeDecelFromSpline() );
SetIdealSpeed( 0.0f );
}
}
/*
================
rvSplineMover::RestoreFromOverrides
================
*/
void rvSplineMover::RestoreFromOverrides( const idDict* args ) {
if( !args ) {
return;
}
physicsObj.SetLinearDeceleration( args->GetFloat("decel") );
}
/*
================
rvSplineMover::PlayAnim
================
*/
int rvSplineMover::PlayAnim( int channel, const char* animName, int blendFrames ) {
int animIndex = GetAnimator()->GetAnim( animName );
if( !animIndex ) {
return 0;
}
GetAnimator()->PlayAnim( channel, animIndex, gameLocal.GetTime(), FRAME2MS(blendFrames) );
return GetAnimator()->CurrentAnim( channel )->Length();
}
/*
================
rvSplineMover::CycleAnim
================
*/
void rvSplineMover::CycleAnim( int channel, const char* animName, int blendFrames ) {
int animIndex = GetAnimator()->GetAnim( animName );
if( !animIndex ) {
return;
}
GetAnimator()->CycleAnim( channel, animIndex, gameLocal.GetTime(), FRAME2MS(blendFrames) );
}
/*
================
rvSplineMover::ClearChannel
================
*/
void rvSplineMover::ClearChannel( int channel, int clearFrame ) {
GetAnimator()->Clear( channel, gameLocal.GetTime(), FRAME2MS(clearFrame) );
}
/*
================
rvSplineMover::PreBind
================
*/
void rvSplineMover::PreBind() {
idAnimatedEntity::PreBind();
SetSpline( NULL );
}
/*
================
rvSplineMover::Save
================
*/
void rvSplineMover::Save( idSaveGame *savefile ) const {
savefile->WriteStaticObject( physicsObj );
savefile->WriteFloat( idealSpeed );
savefile->WriteInt( waitThreadId );
}
/*
================
rvSplineMover::Restore
================
*/
void rvSplineMover::Restore( idRestoreGame *savefile ) {
savefile->ReadStaticObject( physicsObj );
RestorePhysics( &physicsObj );
savefile->ReadFloat( idealSpeed );
savefile->ReadInt( waitThreadId );
AddSelfToGlobalList();
}
/*
================
rvSplineMover::WriteToSnapshot
================
*/
void rvSplineMover::WriteToSnapshot( idBitMsgDelta &msg ) const {
physicsObj.WriteToSnapshot( msg );
}
/*
================
rvSplineMover::ReadFromSnapshot
================
*/
void rvSplineMover::ReadFromSnapshot( const idBitMsgDelta &msg ) {
physicsObj.ReadFromSnapshot( msg );
}
/*
================
rvSplineMover::AddSelfToGlobalList
================
*/
void rvSplineMover::AddSelfToGlobalList() {
splineMoverNode.SetOwner( this );
if( !InGlobalList() ) {
splineMoverNode.AddToEnd( splineMovers );
}
}
/*
================
rvSplineMover::RemoveSelfFromGlobalList
================
*/
void rvSplineMover::RemoveSelfFromGlobalList() {
splineMoverNode.Remove();
}
/*
================
rvSplineMover::InGlobalList
================
*/
bool rvSplineMover::InGlobalList() const {
return splineMoverNode.InList();
}
/*
================
rvSplineMover::WhosVisible
================
*/
bool rvSplineMover::WhosVisible( const idFrustum& frustum, idList<rvSplineMover*>& list ) const {
list.Clear();
if( !frustum.IsValid() ) {
return false;
}
for( rvSplineMover* node = splineMovers.Next(); node; node = node->splineMoverNode.Next() ) {
if( node == this ) {
continue;
}
if( frustum.IntersectsBounds(node->GetPhysics()->GetAbsBounds()) ) {
list.AddUnique( node );
}
}
return list.Num() > 0;
}
/*
================
rvSplineMover::GetTrackInfo
================
*/
idStr rvSplineMover::GetTrackInfo( const idSplinePath* track ) const {
if( !track ) {
return idStr( "" );
}
idStr info( track->GetName() );
return info.Mid( info.Last('_') - 1, 1 );
}
/*
================
rvSplineMover::ConvertToMover
================
*/
rvSplineMover* rvSplineMover::ConvertToMover( idEntity* mover ) const {
return mover && mover->IsType(rvSplineMover::Type) ? static_cast<rvSplineMover*>(mover) : NULL;
}
/*
================
rvSplineMover::ConvertToSplinePath
================
*/
idSplinePath* rvSplineMover::ConvertToSplinePath( idEntity* spline ) const {
return (spline && spline->IsType(idSplinePath::GetClassType())) ? static_cast<idSplinePath*>(spline) : NULL;
}
/*
================
rvSplineMover::PreDoneMoving
================
*/
void rvSplineMover::PreDoneMoving() {
if( waitThreadId >= 0 ) {
idThread::ObjectMoveDone( waitThreadId, this );
waitThreadId = -1;
}
RestoreFromOverrides( &spawnArgs );
}
/*
================
rvSplineMover::PostDoneMoving
================
*/
void rvSplineMover::PostDoneMoving() {
CallScriptEvents( physicsObj.GetSplineEntity(), "call_doneMoving", this );
}
/*
==============
rvSplineMover::CallScriptEvents
==============
*/
// FIXME: very similier code is in the spawner...if possible try and make one function for both to call
void rvSplineMover::CallScriptEvents( const idSplinePath* spline, const char* prefixKey, idEntity* parm ) {
if( !spline || !prefixKey || !prefixKey[0] ) {
return;
}
rvScriptFuncUtility func;
for( const idKeyValue* kv = spline->spawnArgs.MatchPrefix(prefixKey); kv; kv = spline->spawnArgs.MatchPrefix(prefixKey, kv) ) {
if( !kv->GetValue().Length() ) {
continue;
}
if( func.Init(kv->GetValue()) <= SFU_ERROR ) {
continue;
}
func.InsertEntity( spline, 0 );
func.InsertEntity( parm, 1 );
func.CallFunc( &spawnArgs );
}
}
/*
================
rvSplineMover::Event_PostSpawn
================
*/
void rvSplineMover::Event_PostSpawn() {
idEntityPtr<idEntity> target;
for( int ix = targets.Num() - 1; ix >= 0; --ix ) {
target = targets[ix];
if( target.IsValid() && target->IsType(idSplinePath::GetClassType()) ) {
SetSpline( static_cast<idSplinePath*>(target.GetEntity()) );
break;
}
}
SetIdealSpeed( spawnArgs.GetBool("waitForTrigger") ? 0.0f : spawnArgs.GetFloat("speed", "50") );
}
/*
===============
rvSplineMover::Event_PartBlocked
===============
*/
void rvSplineMover::Event_PartBlocked( idEntity *blockingEntity ) {
assert( blockingEntity );
float damage = spawnArgs.GetFloat( "damage" );
if( damage > 0.0f ) {
blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
}
if( g_debugMover.GetBool() ) {
gameLocal.Printf( "%d: '%s' blocked by '%s'\n", gameLocal.GetTime(), GetName(), blockingEntity->GetName() );
}
}
/*
================
rvSplineMover::Event_SetSpline
================
*/
void rvSplineMover::Event_SetSpline( idEntity* spline ) {
SetSpline( ConvertToSplinePath(spline) );
}
/*
================
rvSplineMover::Event_GetSpline
================
*/
void rvSplineMover::Event_GetSpline() {
idThread::ReturnEntity( GetSpline() );
}
/*
================
rvSplineMover::Event_SetAcceleration
================
*/
void rvSplineMover::Event_SetAcceleration( float accel ) {
SetAcceleration( accel );
}
/*
================
rvSplineMover::Event_SetDeceleration
================
*/
void rvSplineMover::Event_SetDeceleration( float decel ) {
SetDeceleration( decel );
}
/*
================
rvSplineMover::Event_SetSpeed
================
*/
void rvSplineMover::Event_SetSpeed( float speed ) {
SetIdealSpeed( speed );
SetSpeed( speed );
}
/*
================
rvSplineMover::Event_GetSpeed
================
*/
void rvSplineMover::Event_GetSpeed() {
idThread::ReturnFloat( GetSpeed() );
}
/*
================
rvSplineMover::Event_SetIdealSpeed
================
*/
void rvSplineMover::Event_SetIdealSpeed( float speed ) {
SetIdealSpeed( speed );
}
/*
================
rvSplineMover::Event_GetIdealSpeed
================
*/
void rvSplineMover::Event_GetIdealSpeed() {
idThread::ReturnFloat( GetIdealSpeed() );
}
/*
================
rvSplineMover::Event_ApplySpeedScale
================
*/
void rvSplineMover::Event_ApplySpeedScale( float scale ) {
SetIdealSpeed( spawnArgs.GetFloat("speed", "50") * scale );
}
/*
================
rvSplineMover::Event_SetCallBack
================
*/
void rvSplineMover::Event_SetCallBack() {
if( waitThreadId >= 0 ) {
idThread::ReturnInt( false );
}
waitThreadId = idThread::CurrentThreadNum();
idThread::ReturnInt( true );
}
/*
================
rvSplineMover::Event_DoneMoving
================
*/
void rvSplineMover::Event_DoneMoving() {
PreDoneMoving();
PostDoneMoving();
idThread::ReturnInt( !physicsObj.HasValidSpline() );
}
/*
================
rvSplineMover::Event_GetCurrentTrackInfo
================
*/
void rvSplineMover::Event_GetCurrentTrackInfo() {
Event_GetTrackInfo( physicsObj.GetSplineEntity() );
}
/*
================
rvSplineMover::Event_GetTrackInfo
================
*/
void rvSplineMover::Event_GetTrackInfo( idEntity* track ) {
idThread::ReturnString( GetTrackInfo(ConvertToSplinePath(track)) );
}
/*
================
rvSplineMover::Event_Activate
================
*/
void rvSplineMover::Event_Activate( idEntity* activator ) {
// This is for my special case in tram1b
//if( physicsObj.StoppedMoving() ) {
// SetIdealSpeed( spawnArgs.GetFloat("speed", "50") );
//}
}
/*
================
rvSplineMover::Event_OnAcceleration
================
*/
void rvSplineMover::Event_OnAcceleration() {
StartSound( "snd_accel", SND_CHANNEL_ANY, 0, false, NULL );
}
/*
================
rvSplineMover::Event_OnDeceleration
================
*/
void rvSplineMover::Event_OnDeceleration() {
StartSound( "snd_decel", SND_CHANNEL_ANY, 0, false, NULL );
}
/*
================
rvSplineMover::Event_OnCruising
================
*/
void rvSplineMover::Event_OnCruising() {
idVec2 range( spawnArgs.GetVec2("noisePeriodRange") * idMath::M_SEC2MS );
if( !EventIsPosted(&EV_StartSoundPeriodic) && range.Length() > VECTOR_EPSILON ) {
ProcessEvent( &EV_StartSoundPeriodic, "snd_noise", (int)SND_CHANNEL_ANY, (int)range[0], (int)range[1] );
}
}
/*
================
rvSplineMover::Event_OnStopMoving
================
*/
void rvSplineMover::Event_OnStopMoving() {
StopSound( SND_CHANNEL_ANY, false );
CancelEvents( &EV_StartSoundPeriodic );
}
/*
================
rvSplineMover::Event_OnStartMoving
================
*/
void rvSplineMover::Event_OnStartMoving() {
}
/*
================
rvSplineMover::Event_StartSoundPeriodic
================
*/
void rvSplineMover::Event_StartSoundPeriodic( const char* sndKey, const s_channelType channel, int minDelay, int maxDelay ) {
CancelEvents( &EV_StartSoundPeriodic );
if( physicsObj.StoppedMoving() ) {
return;
}
int length;
StartSound( sndKey, channel, 0, false, &length );
PostEventMS( &EV_StartSoundPeriodic, Max(rvRandom::irand(minDelay, maxDelay), length), sndKey, (int)channel, minDelay, maxDelay );
}
const idEventDef EV_TramCar_RadiusDamage( "<tramCar_radiusDamage>", "vs" );
const idEventDef EV_TramCar_SetIdealTrack( "setIdealTrack", "s" );
const idEventDef EV_TramCar_DriverSpeak( "driverSpeak", "s", 'e' );
const idEventDef EV_TramCar_GetDriver( "getDriver", "", 'E' );
const idEventDef EV_TramCar_OpenDoors( "openDoors" );
const idEventDef EV_TramCar_CloseDoors( "closeDoors" );
//=======================================================
//
// rvTramCar
//
//=======================================================
CLASS_DECLARATION( rvSplineMover, rvTramCar )
EVENT( EV_TramCar_DriverSpeak, rvTramCar::Event_DriverSpeak )
EVENT( EV_TramCar_GetDriver, rvTramCar::Event_GetDriver )
EVENT( EV_Activate, rvTramCar::Event_Activate )
EVENT( EV_TramCar_RadiusDamage, rvTramCar::Event_RadiusDamage )
EVENT( EV_TramCar_SetIdealTrack, rvTramCar::Event_SetIdealTrack )
EVENT( EV_OnStartMoving, rvTramCar::Event_OnStartMoving )
EVENT( EV_OnStopMoving, rvTramCar::Event_OnStopMoving )
EVENT( EV_TramCar_OpenDoors, rvTramCar::Event_OpenDoors )
EVENT( EV_TramCar_CloseDoors, rvTramCar::Event_CloseDoors )
EVENT( AI_SetHealth, rvTramCar::Event_SetHealth )
END_CLASS
/*
================
rvTramCar::Spawn
================
*/
void rvTramCar::Spawn() {
RegisterStateThread( idealTrackStateThread, "IdealTrack" );
RegisterStateThread( speedSoundEffectsStateThread, "SpeedSoundEffects" );
numTracksOnMap = gameLocal.world->spawnArgs.GetInt( "numTramCarTracks", "1" );
idStr track;
if ( numTracksOnMap == 1 ) {
track += ConvertToTrackLetter( numTracksOnMap - 1 );
} else {
track = spawnArgs.RandomPrefix( "idealTrack", gameLocal.random, "0" );
}
Event_SetIdealTrack( track.c_str() );
idealTrackStateThread.SetState( GetIdealTrack() < 0 ? "RandomTrack" : "AssignedTrack" );
SpawnDriver( "def_driver" );
SpawnWeapons( "def_weapon" );
SpawnOccupants( "def_occupant" );
SpawnDoors();
float dNear = 0.0f, dFar = 0.0f, dLeft = 0.0f, dUp = 0.0f;
const char* frustumKey = spawnArgs.GetString( "collisionFov" );
if( frustumKey[0] ) {
sscanf( frustumKey, "%f %f %f %f", &dNear, &dFar, &dLeft, &dUp );
collisionFov.SetSize( dNear, dFar, dLeft, dUp );
}
fl.takedamage = health > 0;
BecomeActive( TH_THINK );
SetSpline( NULL );
}
/*
================
rvTramCar::SpawnDriver
================
*/
rvTramCar::~rvTramCar() {
SAFE_REMOVE( driver );
occupants.RemoveContents( true );
weapons.RemoveContents( true );
SAFE_REMOVE( leftDoor );
SAFE_REMOVE( rightDoor );
}
/*
================
rvTramCar::SpawnDriver
================
*/
void rvTramCar::SpawnDriver( const char* driverKey ) {
driver = (idAI*)SpawnPart( spawnArgs.GetString(driverKey), "def_occupant" );
}
/*
================
rvTramCar::SpawnWeapons
================
*/
void rvTramCar::SpawnWeapons( const char* partKey ) {
idEntityPtr<rvVehicle> weapon;
for( const idKeyValue* kv = spawnArgs.MatchPrefix(partKey); kv; kv = spawnArgs.MatchPrefix(partKey, kv) ) {
if( !kv->GetValue().Length() ) {
continue;
}
weapon = (rvVehicle*)SpawnPart( kv->GetValue().c_str(), partKey );
weapon->IdleAnim( ANIMCHANNEL_LEGS, "idle", 0 );
//if( weapon->GetAnimator()->GetAnim("toFire") && weapon->GetAnimator()->GetAnim("toIdle") ) {
weapons.AddUnique( weapon );
//}
}
}
/*
================
rvTramCar::SpawnOccupants
================
*/
void rvTramCar::SpawnOccupants( const char* partKey ) {
idEntityPtr<idAI> occupant;
for( const idKeyValue* kv = spawnArgs.MatchPrefix(partKey); kv; kv = spawnArgs.MatchPrefix(partKey, kv) ) {
if( !kv->GetValue().Length() ) {
continue;
}
occupant = (idAI*)SpawnPart( kv->GetValue().c_str(), partKey );
occupants.AddUnique( occupant );
}
}
/*
================
rvTramCar::SpawnPart
================
*/
idEntity* rvTramCar::SpawnPart( const char* partDefName, const char* subPartDefName ) {
idEntity* part = NULL;
idDict entityDef;
const idDict* info = gameLocal.FindEntityDefDict( partDefName, false );
if( !info ) {
return NULL;
}
const idDict* def = gameLocal.FindEntityDefDict( info->GetString(subPartDefName), false );
if( !def ) {
return NULL;
}
entityDef.Copy( *def );
entityDef.SetBool( "trigger", spawnArgs.GetBool("waitForTrigger") );
entityDef.SetVector( "origin", GetPhysics()->GetOrigin() + info->GetVector("spawn_offset") * GetPhysics()->GetAxis() );
entityDef.SetFloat( "angle", info->GetInt("spawn_facing_offset") + spawnArgs.GetFloat("angle") );
entityDef.Set( "bind", GetName() );
gameLocal.SpawnEntityDef( entityDef, &part );
return part;
}
/*
================
rvTramCar::SpawnDoors
================
*/
void rvTramCar::SpawnDoors() {
leftDoor = SpawnDoor( "clipModel_doorLeft" );
rightDoor = SpawnDoor( "clipModel_doorRight" );
}
/*
================
rvTramCar::SpawnDoors
================
*/
idMover* rvTramCar::SpawnDoor( const char* key ) {
idDict args;
const idDict* dict = NULL;
idMover* outer = NULL;
idMover* inner = NULL;
dict = gameLocal.FindEntityDefDict( spawnArgs.GetString(key), false );
if( !dict ) {
return NULL;
}
args.Set( "open_rotation", dict->GetString("open_rotation") );
args.Set( "close_rotation", dict->GetString("close_rotation") );
args.Set( "open_dir", dict->GetString("open_dir") );
args.Set( "close_dir", dict->GetString("close_dir") );
args.Set( "distExt", dict->GetString("distExt") );
args.SetVector( "origin", GetPhysics()->GetOrigin() + dict->GetVector("offset") * GetPhysics()->GetAxis() );
args.SetMatrix( "rotation", GetPhysics()->GetAxis() );
args.Set( "clipModel", dict->GetString("clipModel") );
args.Set( "bind", GetName() );
outer = gameLocal.SpawnSafeEntityDef<idMover>( "func_mover", &args );
assert( outer );
args.Set( "clipModel", dict->GetString("clipModelExt") );
args.Set( "bind", outer->GetName() );
inner = gameLocal.SpawnSafeEntityDef<idMover>( "func_mover", &args );
return inner;
}
/*
================
rvTramCar::Think
================
*/
void rvTramCar::Think() {
RunPhysics();
MidThink();
Present();
}
/*
================
rvTramCar::MidThink
================
*/
void rvTramCar::MidThink() {
if( !physicsObj.StoppedMoving() ) {
LookAround();
TouchTriggers();
speedSoundEffectsStateThread.Execute();
}
}
/*
================
rvTramCar::RegisterStateThread
================
*/
void rvTramCar::RegisterStateThread( rvStateThread& stateThread, const char* name ) {
stateThread.SetName( name );
stateThread.SetOwner( this );
}
/*
================
rvTramCar::SetIdealTrack
================
*/
void rvTramCar::SetIdealTrack( int track ) {
idealTrack = track;
}
/*
================
rvTramCar::GetIdealTrack
================
*/
int rvTramCar::GetIdealTrack() const {
return idealTrack;
}
/*
================
rvTramCar::AddDamageEffect
================
*/
void rvTramCar::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName, idEntity* inflictor ) {
rvSplineMover::AddDamageEffect( collision, velocity, damageDefName, inflictor );
if( GetDamageScale() >= 0.5f && rvRandom::flrand() <= spawnArgs.GetFloat("damageEffectFreq", "0.25") ) {
idVec3 center = GetPhysics()->GetAbsBounds().GetCenter();
idVec3 v = (center - collision.endpos).ToNormal();
float dot = v * collision.c.normal;
PlayEffect( (dot > 0.0f) ? "fx_damage_internal" : "fx_damage_external", collision.endpos, collision.c.normal.ToMat3(2) );
}
}
/*
================
rvTramCar::Damage
================
*/
void rvTramCar::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) {
if( attacker && GetPhysics()->GetAbsBounds().Contains(attacker->GetPhysics()->GetAbsBounds()) ) {
return;
}
if( attacker && g_debugVehicle.GetInteger() == 3 ) {
gameLocal.Printf( "Was damaged by %s. Current damage scale: %f\n", attacker->GetName(), GetDamageScale() );
}
rvSplineMover::Damage( inflictor, attacker, dir, damageDefName, damageScale, location );
}
/*
================
rvTramCar::Killed
================
*/
void rvTramCar::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
idVec3 center = GetPhysics()->GetAbsBounds().GetCenter();
fl.takedamage = false;
StopSound( SND_CHANNEL_ANY, false );
//Calling gameLocal's version because we don't want to get bound and hidden
gameLocal.PlayEffect( gameLocal.GetEffect( spawnArgs, "fx_explode" ), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() );
PostEventMS( &EV_TramCar_RadiusDamage, 0, center, spawnArgs.GetString("def_damage_explode") );
//TODO: think about giving rigid body physics and have it fall from track
Hide();
PostEventMS( &EV_Remove, 0 );
}
/*
================
rvTramCar::GetDamageScale
================
*/
float rvTramCar::GetDamageScale() const {
return 1.0f - GetHealthScale();
}
/*
================
rvTramCar::GetDamageScale
================
*/
float rvTramCar::GetHealthScale() const {
float spawnHealth = spawnArgs.GetFloat( "health" );
return ( idMath::Fabs(spawnHealth) <= VECTOR_EPSILON ) ? 1.0f : (health / spawnHealth);
}
/*
================
rvTramCar::Save
================
*/
void rvTramCar::Save( idSaveGame *savefile ) const {
savefile->WriteInt( idealTrack );
// savefile->WriteString( idealTrackTag.c_str() ); // cnicholson: FIXME: This has a comment of HACK in the .h file... Not sure how to write a idStr yet
savefile->WriteFrustum( collisionFov );
idealTrackStateThread.Save( savefile );
speedSoundEffectsStateThread.Save( savefile );
driver.Save( savefile );
savefile->WriteInt( occupants.Num() );
for( int ix = occupants.Num() - 1; ix >= 0; --ix ) {
occupants[ix].Save( savefile );
}
savefile->WriteInt( weapons.Num() );
for( int ix = weapons.Num() - 1; ix >= 0; --ix ) {
weapons[ix].Save( savefile );
}
savefile->WriteInt( numTracksOnMap );
leftDoor.Save ( savefile );
rightDoor.Save ( savefile );
}
/*
================
rvTramCar::Restore
================
*/
void rvTramCar::Restore( idRestoreGame *savefile ) {
savefile->ReadInt( idealTrack );
savefile->ReadFrustum( collisionFov );
idealTrackStateThread.Restore( savefile, this );
speedSoundEffectsStateThread.Restore( savefile, this );
driver.Restore( savefile );
int num = 0;
savefile->ReadInt( num );
occupants.SetNum( num );
for( int ix = occupants.Num() - 1; ix >= 0; --ix ) {
occupants[ix].Restore( savefile );
}
savefile->ReadInt( num );
weapons.SetNum( num );
for( int ix = weapons.Num() - 1; ix >= 0; --ix ) {
weapons[ix].Restore( savefile );
}
savefile->ReadInt( numTracksOnMap );
leftDoor.Restore ( savefile );
rightDoor.Restore ( savefile );
}
/*
================
rvTramCar::WriteToSnapshot
================
*/
void rvTramCar::WriteToSnapshot( idBitMsgDelta &msg ) const {
}
/*
================
rvTramCar::ReadFromSnapshot
================
*/
void rvTramCar::ReadFromSnapshot( const idBitMsgDelta &msg ) {
}
/*
================
rvTramCar::HeadTowardsIdealTrack
================
*/
void rvTramCar::HeadTowardsIdealTrack() {
idSplinePath* ideal = FindSplineToIdealTrack( GetSpline() );
if( !ideal ) {
ideal = GetRandomSpline(GetSpline());
}
SetSpline( ideal );
}
/*
================
rvTramCar::FindSplineToTrack
================
*/
enum {
LOOK_LEFT = -1,
LOOK_RIGHT = 1
};
idSplinePath* rvTramCar::FindSplineToTrack( idSplinePath* spline, const idStr& track ) const {
idSplinePath* target = NULL;
idEntity* ent = NULL;
idStr trackInfo;
idList<rvSplineMover*> list;
if( !spline ) {
return NULL;
}
for( int ix = SortSplineTargets(spline) - 1; ix >= 0; --ix ) {
ent = GetSplineTarget( spline, ix );
target = static_cast<idSplinePath*>( ent );
assert( target->IsActive() );
trackInfo = GetTrackInfo( target );
if( -1 >= trackInfo.Find(track) ) {
continue;
}
// HACK: I hate switch statements
switch( ConvertToTrackNumber(trackInfo) - GetCurrentTrack() ) {
case LOOK_LEFT: {
if( !LookLeft(list) ) {
return target;
}
break;
}
case LOOK_RIGHT: {
if( !LookRight(list) ) {
return target;
}
break;
}
default: {
return target;
}
}
// HACK
}
return NULL;
}
/*
================
rvTramCar::SortSplineTargets
================
*/
int rvTramCar::SortSplineTargets( idSplinePath* spline ) const {
assert( spline );
return (SignZero(GetSpeed()) >= 0) ? spline->SortTargets() : spline->SortBackwardsTargets();
}
/*
================
rvTramCar::GetSplineTarget
================
*/
idEntity* rvTramCar::GetSplineTarget( idSplinePath* spline, int index ) const {
assert( spline );
return (SignZero(GetSpeed()) >= 0) ? spline->targets[index].GetEntity() : spline->backwardPathTargets[index].GetEntity();
}
/*
================
rvTramCar::FindSplineToIdealTrack
================
*/
idSplinePath* rvTramCar::FindSplineToIdealTrack( idSplinePath* spline ) const {
// HACK
int trackDelta = idMath::ClampInt( -1, 1, GetIdealTrack() - GetCurrentTrack() );
idStr trackLetter( ConvertToTrackLetter(GetCurrentTrack() + trackDelta) );
idSplinePath* s = NULL;
if( idealTrackTag.Length() && !trackDelta ) {// On ideal track
s = FindSplineToTrack( spline, idealTrackTag + trackLetter );
}
if( !s ) {
s = FindSplineToTrack( spline, trackLetter );
}
return s;
}
/*
================
rvTramCar::GetRandomSpline
================
*/
idSplinePath* rvTramCar::GetRandomSpline( idSplinePath* spline ) const {
if( !spline ) {
return NULL;
}
int numActiveTargets = SortSplineTargets( spline );
if( !numActiveTargets ) {
return NULL;
}
idEntity* target = GetSplineTarget( spline, rvRandom::irand(0, numActiveTargets - 1) );
return (target && target->IsType(idSplinePath::GetClassType())) ? static_cast<idSplinePath*>(target) : NULL;
}
/*
================
rvTramCar::GetCurrentTrack
================
*/
int rvTramCar::GetCurrentTrack() const {
return ConvertToTrackNumber( GetTrackInfo(GetSpline()) );
}
/*
================
rvTramCar::UpdateChannel
================
*/
void rvTramCar::UpdateChannel( const s_channelType channel, const soundShaderParms_t& parms ) {
idSoundEmitter *emitter = soundSystem->EmitterForIndex( SOUNDWORLD_GAME, refSound.referenceSoundHandle );
if( emitter ) {
emitter->ModifySound( channel, &parms );
}
}
/*
================
rvTramCar::AttenuateTrackChannel
================
*/
void rvTramCar::AttenuateTrackChannel( float attenuation ) {
soundShaderParms_t parms = refSound.parms;
parms.frequencyShift = attenuation;//idMath::MidPointLerp( 0.0f, 1.0f, 1.1f, attenuation );
UpdateChannel( SND_CHANNEL_BODY, parms );
}
/*
================
rvTramCar::AttenuateTramCarChannel
================
*/
void rvTramCar::AttenuateTramCarChannel( float attenuation ) {
soundShaderParms_t parms = refSound.parms;
parms.volume = (attenuation + 1.0f) * 0.5f;
parms.frequencyShift = Max( 0.4f, attenuation );
UpdateChannel( SND_CHANNEL_BODY2, parms );
}
/*
================
rvTramCar::LookForward
================
*/
bool rvTramCar::LookForward( idList<rvSplineMover*>& list ) const {
collisionFov.SetOrigin( GetPhysics()->GetOrigin() );
collisionFov.SetAxis( GetPhysics()->GetAxis() );
Look( collisionFov, list );
for( int ix = list.Num() - 1; ix >= 0; --ix ) {
if( !OnSameTrackAs(list[ix]) ) {
list.Remove( list[ix] );
}
}
return list.Num() > 0;
}
/*
================
rvTramCar::LookLeft
================
*/
bool rvTramCar::LookLeft( idList<rvSplineMover*>& list ) const {
collisionFov.SetOrigin( GetPhysics()->GetOrigin() );
collisionFov.SetAxis( idAngles(0.0f, 60.0f, 0.0f).ToMat3() * GetPhysics()->GetAxis() );
return Look( collisionFov, list );
}
/*
================
rvTramCar::LookRight
================
*/
bool rvTramCar::LookRight( idList<rvSplineMover*>& list ) const {
collisionFov.SetOrigin( GetPhysics()->GetOrigin() );
collisionFov.SetAxis( idAngles(0.0f, -60.0f, 0.0f).ToMat3() * GetPhysics()->GetAxis() );
return Look( collisionFov, list );
}
/*
================
rvTramCar::LookLeftForTrackChange
================
*/
bool rvTramCar::LookLeftForTrackChange( idList<rvSplineMover*>& list ) const {
collisionFov.SetOrigin( GetPhysics()->GetOrigin() );
collisionFov.SetAxis( idAngles(0.0f, 45.0f, 0.0f).ToMat3() * GetPhysics()->GetAxis() );
return Look( collisionFov, list );
}
/*
================
rvTramCar::LookRightForTrackChange
================
*/
bool rvTramCar::LookRightForTrackChange( idList<rvSplineMover*>& list ) const {
collisionFov.SetOrigin( GetPhysics()->GetOrigin() );
collisionFov.SetAxis( idAngles(0.0f, -45.0f, 0.0f).ToMat3() * GetPhysics()->GetAxis() );
return Look( collisionFov, list );
}
/*
================
rvTramCar::Look
================
*/
int rvSortByDist( const void* left, const void* right ) {
rvSplineMover* leftMover = *(rvSplineMover**)left;
rvSplineMover* rightMover = *(rvSplineMover**)right;
return rightMover->spawnArgs.GetFloat("distAway") - leftMover->spawnArgs.GetFloat("distAway");
}
bool rvTramCar::Look( const idFrustum& fov, idList<rvSplineMover*>& list ) const {
bool result = WhosVisible( fov, list );
if( g_debugVehicle.GetInteger() == 3 ) {
gameRenderWorld->DebugFrustum( colorRed, fov );
}
for( int ix = list.Num() - 1; ix >= 0; --ix ) {
list[ix]->spawnArgs.SetFloat( "distAway", (list[ix]->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin()).Length() );
}
qsort( list.Ptr(), list.Num(), list.TypeSize(), rvSortByDist );
// Do we need to get rid of these keyvalues?
return result;
}
/*
================
rvTramCar::OnSameTrackAs
================
*/
bool rvTramCar::OnSameTrackAs( const rvSplineMover* tram ) const {
return tram && !GetTrackInfo(GetSpline()).Icmp( GetTrackInfo(tram->GetSpline()) );
}
/*
================
rvTramCar::SameIdealTrackAs
================
*/
bool rvTramCar::SameIdealTrackAs( const rvSplineMover* tram ) const {
if( !tram ) {
return false;
}
return GetIdealTrack() == static_cast<const rvTramCar*>(tram)->GetIdealTrack();
}
/*
================
rvTramCar::LookAround
================
*/
void rvTramCar::LookAround() {
idList<rvSplineMover*> moverList;
if( !AdjustSpeed(moverList) ) {
SetSpeed( GetIdealSpeed() );
}
}
/*
================
rvTramCar::AdjustSpeed
================
*/
bool rvTramCar::AdjustSpeed( idList<rvSplineMover*>& moverList ) {
if( LookForward(moverList) ) {
if( g_debugVehicle.GetInteger() ) {
gameRenderWorld->DebugLine( colorRed, GetPhysics()->GetOrigin(), moverList[0]->GetPhysics()->GetOrigin() );
}
//Slow down and try to get onto other track if allowed
SetSpeed( moverList[0]->GetSpeed() * rvRandom::flrand(0.5f, 0.75f) );
// Is this safe if we are on a specified track
SetIdealTrack( (GetIdealTrack() + rvRandom::irand(1, 2)) % numTracksOnMap );
return true;
}
return false;
}
/*
================
rvTramCar::DriverSpeak
================
*/
idEntity* rvTramCar::DriverSpeak( const char* speechDecl, bool random ) {
if( !driver.IsValid() || driver->IsSpeaking() ) {
return NULL;
}
driver->Speak( speechDecl, random );
return driver;
}
/*
================
rvTramCar::OccupantSpeak
================
*/
idEntity* rvTramCar::OccupantSpeak( const char *speechDecl, bool random ) {
idEntityPtr<idAI> occupant;
if( !occupants.Num() ) {
return NULL;
}
occupant = occupants[ rvRandom::irand(0, occupants.Num() - 1) ];
if( !occupant.IsValid() || occupant->IsSpeaking() ) {
return NULL;
}
occupant->Speak( speechDecl, random );
return occupant;
}
/*
================
rvTramCar::PostDoneMoving
================
*/
void rvTramCar::PostDoneMoving() {
rvSplineMover::PostDoneMoving();
HeadTowardsIdealTrack();
}
/*
================
rvTramCar::DeployRamp
================
*/
void rvTramCar::DeployRamp() {
OperateRamp( "open" );
}
/*
================
rvTramCar::RetractRamp
================
*/
void rvTramCar::RetractRamp() {
OperateRamp( "close" );
}
/*
================
rvTramCar::OperateRamp
================
*/
void rvTramCar::OperateRamp( const char* operation ) {
if( !operation || !operation[0] ) {
return;
}
PlayAnim( ANIMCHANNEL_ALL, operation, 0 );
OperateRamp( operation, leftDoor );
OperateRamp( operation, rightDoor );
}
/*
================
rvTramCar::OperateRamp
================
*/
void rvTramCar::OperateRamp( const char* operation, idMover* door ) {
if( !operation || !operation[0] ) {
return;
}
idAngles ang;
idVec3 vec;
if( !door ) {
return;
}
if( !door->IsBound() ) {
return;
}
ang = door->spawnArgs.GetAngles( va("%s%s", operation, "_rotation") );
vec.Set( ang[0], ang[1], ang[2] );
door->GetBindMaster()->ProcessEvent( &EV_RotateOnce, vec );
vec = door->spawnArgs.GetVector(va("%s%s", operation, "_dir")).ToNormal() * door->spawnArgs.GetFloat("distExt");
door->PostEventSec( &EV_MoveAlongVector, 0.5f, vec );
}
/*
================
rvTramCar::Event_OnStopMoving
================
*/
void rvTramCar::Event_OnStopMoving() {
rvSplineMover::Event_OnStopMoving();
speedSoundEffectsStateThread.SetState( "IdleSpeed" );
}
/*
================
rvTramCar::Event_OnStartMoving
================
*/
void rvTramCar::Event_OnStartMoving() {
rvSplineMover::Event_OnStartMoving();
speedSoundEffectsStateThread.SetState( "NormalSpeed" );
}
/*
================
rvTramCar::Event_DriverSpeak
================
*/
void rvTramCar::Event_DriverSpeak( const char* voKey ) {
idThread::ReturnEntity( DriverSpeak(voKey) );
}
/*
================
rvTramCar::Event_GetDriver
================
*/
void rvTramCar::Event_GetDriver() {
idThread::ReturnEntity( driver );
}
/*
================
rvTramCar::Event_Activate
================
*/
void rvTramCar::Event_Activate( idEntity* activator ) {
rvSplineMover::Event_Activate( activator );
// If being activated by a spawner we need to attach to it
if( activator->IsType(rvSpawner::GetClassType()) ) {
static_cast<rvSpawner*>(activator)->Attach( this );
} else {
if( driver.IsValid() ) {
driver->ProcessEvent( &EV_Activate, activator );
}
occupants.RemoveNull();
for( int ix = occupants.Num() - 1; ix >= 0; --ix ) {
occupants[ix]->ProcessEvent( &EV_Activate, activator );
}
}
}
/*
================
rvTramCar::Event_RadiusDamage
================
*/
void rvTramCar::Event_RadiusDamage( const idVec3& origin, const char* damageDefName ) {
gameLocal.RadiusDamage( origin, this, this, this, this, damageDefName );
}
/*
================
rvTramCar::Event_SetIdealTrack
================
*/
void rvTramCar::Event_SetIdealTrack( const char* track ) {
idStr ideal = track;
if( ideal.Length() > 1 ) {
idealTrackTag = ideal.Left( ideal.Length() - 1 );
}
idealTrackStateThread.SetState( "AssignedTrack" );
SetIdealTrack( ConvertToTrackNumber(ideal.Right(1)) );
}
/*
================
rvTramCar::Event_OpenDoors
================
*/
void rvTramCar::Event_OpenDoors() {
DeployRamp();
}
/*
================
rvTramCar::Event_CloseDoors
================
*/
void rvTramCar::Event_CloseDoors() {
RetractRamp();
}
/*
================
rvTramCar::Event_SetHealth
================
*/
void rvTramCar::Event_SetHealth ( float health ) {
this->health = health;
}
CLASS_STATES_DECLARATION( rvTramCar )
STATE( "IdleSpeed", rvTramCar::State_Idle )
STATE( "NormalSpeed", rvTramCar::State_NormalSpeed )
STATE( "ExcessiveSpeed", rvTramCar::State_ExcessiveSpeed )
STATE( "RandomTrack", rvTramCar::State_RandomTrack )
STATE( "AssignedTrack", rvTramCar::State_AssignedTrack )
END_CLASS_STATES
/*
================
rvTramCar::State_Idle
================
*/
stateResult_t rvTramCar::State_Idle( const stateParms_t& parms ) {
if( !parms.stage ) {
StartSound( "snd_speed_idle_track", SND_CHANNEL_BODY, 0, false, NULL );
StartSound( "snd_speed_idle_tram", SND_CHANNEL_BODY2, 0, false, NULL );
return SRESULT_STAGE( parms.stage + 1 );
}
return SRESULT_WAIT;
}
/*
================
rvTramCar::State_NormalSpeed
================
*/
stateResult_t rvTramCar::State_NormalSpeed( const stateParms_t& parms ) {
if( !parms.stage ) {
StartSound( "snd_speed_normal_track", SND_CHANNEL_BODY, 0, false, NULL );
StartSound( "snd_speed_normal_tram", SND_CHANNEL_BODY2, 0, false, NULL );
return SRESULT_STAGE( parms.stage + 1 );
}
float speedScale = 0.8f + 0.2f * ( GetSpeed() / GetNormalSpeed() );
if( speedScale >= 1.0f ) {
speedSoundEffectsStateThread.SetState( "ExcessiveSpeed" );
return SRESULT_DONE;
}
AttenuateTrackChannel( speedScale );
AttenuateTramCarChannel( speedScale );
return SRESULT_WAIT;
}
/*
================
rvTramCar::State_ExcessiveSpeed
================
*/
stateResult_t rvTramCar::State_ExcessiveSpeed( const stateParms_t& parms ) {
if( !parms.stage ) {
StartSound( "snd_speed_excessive_track", SND_CHANNEL_BODY, 0, false, NULL );
StartSound( "snd_speed_excessive_tram", SND_CHANNEL_BODY2, 0, false, NULL );
return SRESULT_STAGE( parms.stage + 1 );
}
float speedScale = GetSpeed() / GetNormalSpeed();
if( speedScale < 1.0f ) {
speedSoundEffectsStateThread.SetState( "NormalSpeed" );
return SRESULT_DONE;
}
AttenuateTrackChannel( speedScale );
AttenuateTramCarChannel( speedScale );
return SRESULT_WAIT;
}
/*
================
rvTramCar::State_RandomTrack
================
*/
stateResult_t rvTramCar::State_RandomTrack( const stateParms_t& parms ) {
SetIdealTrack( rvRandom::irand(0, numTracksOnMap - 1) );
return SRESULT_WAIT;
}
/*
================
rvTramCar::State_AssignedTrack
================
*/
stateResult_t rvTramCar::State_AssignedTrack( const stateParms_t& parms ) {
return SRESULT_WAIT;
}
//=======================================================
//
// rvTramCar_Marine
//
//=======================================================
const idEventDef EV_TramCar_UseMountedGun( "useMountedGun", "e" );
const idEventDef EV_TramCar_SetPlayerDamageEnt( "setPlayerDamageEnt", "f" );
CLASS_DECLARATION( rvTramCar, rvTramCar_Marine )
EVENT( EV_TramCar_UseMountedGun, rvTramCar_Marine::Event_UseMountedGun )
EVENT( EV_TramCar_SetPlayerDamageEnt, rvTramCar_Marine::Event_SetPlayerDamageEntity )
END_CLASS
/*
================
rvTramCar_Marine::Spawn
================
*/
void rvTramCar_Marine::Spawn() {
RegisterStateThread( playerOccupationStateThread, "PlayerOccupation" );
playerOccupationStateThread.SetState( "NotOccupied" );
RegisterStateThread( playerUsingMountedGunStateThread, "MountedGunInUse" );
playerUsingMountedGunStateThread.SetState( "NotUsingMountedGun" );
maxHealth = spawnArgs.GetInt( "health", "0" );
lastHeal = 0;
healDelay = spawnArgs.GetInt( "heal_delay", "15" );
healAmount = spawnArgs.GetInt( "heal_amount", "2" );
}
/*
================
rvTramCar_Marine::MidThink
================
*/
void rvTramCar_Marine::MidThink() {
rvTramCar::MidThink();
if ( healDelay > 0 && lastHeal + healDelay < gameLocal.time && health < maxHealth ) {
health += healAmount;
if ( health > maxHealth ) {
health = maxHealth;
}
}
playerOccupationStateThread.Execute();
}
/*
================
rvTramCar_Marine::ActivateTramHud
================
*/
void rvTramCar_Marine::ActivateTramHud( idPlayer* player ) {
if( !player ) {
return;
}
idUserInterface* hud = player->GetHud();
if( !hud ) {
return;
}
hud->HandleNamedEvent( "enterTram" );
}
/*
================
rvTramCar_Marine::DeactivateTramHud
================
*/
void rvTramCar_Marine::DeactivateTramHud( idPlayer* player ) {
if( !player ) {
return;
}
idUserInterface* hud = player->GetHud();
if( !hud ) {
return;
}
hud->HandleNamedEvent( "leaveTram" );
}
/*
================
rvTramCar_Marine::UpdateTramHud
================
*/
void rvTramCar_Marine::UpdateTramHud( idPlayer* player ) {
if( !player ) {
return;
}
idUserInterface* hud = player->GetHud();
if( !hud ) {
return;
}
hud->SetStateFloat( "tram_healthpct", GetHealthScale() );
}
/*
============
rvTramCar_Marine::Damage
============
*/
void rvTramCar_Marine::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) {
rvTramCar::Damage( inflictor, attacker, dir, damageDefName, damageScale, location );
if( attacker == gameLocal.GetLocalPlayer() ) {
return;
}
if( fl.takedamage && GetDamageScale() < 0.35f ) {
return;
}
if( rvRandom::flrand() > spawnArgs.GetFloat("damageWarningFreq", "0.2") ) {
return;
}
DriverSpeak( "lipsync_damageWarning", true );
}
/*
================
rvTramCar_Marine::Killed
================
*/
void rvTramCar_Marine::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
playerOccupationStateThread.Execute();
idPlayer* player = gameLocal.GetLocalPlayer();
if( player && EntityIsInside(player) ) {
player->SetDamageEntity( NULL );// This should fix the damage from tram car propogation issue
player->ExitVehicle( true );// This should fix the issue that was causing the player to get removed
}
rvTramCar::Killed( inflictor, attacker, damage, dir, location );
}
/*
================
rvTramCar_Marine::Save
================
*/
void rvTramCar_Marine::Save( idSaveGame *savefile ) const {
savefile->WriteInt( visibleEnemies.Num() );
for( int ix = visibleEnemies.Num() - 1; ix >= 0; --ix ) {
visibleEnemies[ix].Save( savefile );
}
playerOccupationStateThread.Save( savefile );
playerUsingMountedGunStateThread.Save( savefile );
// no need to save this since it's set in the restore
//savefile->WriteInt( maxHealth ); // cnicholson: added unsaved var
savefile->WriteInt( lastHeal );
savefile->WriteInt( healDelay );
savefile->WriteInt( healAmount );
}
/*
================
rvTramCar_Marine::Restore
================
*/
void rvTramCar_Marine::Restore( idRestoreGame *savefile ) {
int num = 0;
savefile->ReadInt( num );
visibleEnemies.SetNum( num );
for( int ix = visibleEnemies.Num() - 1; ix >= 0; --ix ) {
visibleEnemies[ix].Restore( savefile );
}
playerOccupationStateThread.Restore( savefile, this );
playerUsingMountedGunStateThread.Restore( savefile, this );
savefile->ReadInt( lastHeal );
savefile->ReadInt( healDelay );
savefile->ReadInt( healAmount );
maxHealth = spawnArgs.GetInt( "health", "0" );
}
/*
================
rvTramCar_Marine::LookAround
================
*/
void rvTramCar_Marine::LookAround() {
idList<rvSplineMover*> moverList;
rvTramCar::LookAround();
if( !driver.IsValid() || driver->IsSpeaking() ) {
return;
}
if( LookOverLeftShoulder(moverList)) {
DriverSpeak( "lipsync_warningLeft", true );
driver->ScriptedAnim( "point_left", 0, false, true );
} else if( LookOverRightShoulder(moverList)) {
DriverSpeak( "lipsync_warningRight", true );
driver->ScriptedAnim( "point_right", 0, false, true );
}
}
/*
================
rvTramCar_Marine::LookOverLeftShoulder
================
*/
bool rvTramCar_Marine::LookOverLeftShoulder( idList<rvSplineMover*>& list ) {
collisionFov.SetOrigin( GetPhysics()->GetOrigin() );
collisionFov.SetAxis( idAngles(0.0, -120.0f, 0.0f).ToMat3() * GetPhysics()->GetAxis() );
bool newOneFound = false;
Look( collisionFov, list );
// now determine if we've spotted a new enemy
for( int ix = list.Num() - 1; ix >= 0; --ix ) {
if ( !visibleEnemies.Find( list[ix] )) {
newOneFound = true;
visibleEnemies.AddUnique( list[ix] );
}
}
return newOneFound;
}
/*
================
rvTramCar_Marine::LookOverRightShoulder
================
*/
bool rvTramCar_Marine::LookOverRightShoulder( idList<rvSplineMover*>& list ) {
collisionFov.SetOrigin( GetPhysics()->GetOrigin() );
collisionFov.SetAxis( idAngles(0.0, 120.0f, 0.0f).ToMat3() * GetPhysics()->GetAxis() );
bool newOneFound = false;
Look( collisionFov, list );
// now determine if we've spotted a new enemy
for( int ix = list.Num() - 1; ix >= 0; --ix ) {
if ( !visibleEnemies.Find( list[ix] )) {
newOneFound = true;
visibleEnemies.AddUnique( list[ix] );
}
}
return newOneFound;
}
/*
================
rvTramCar_Marine::EntityIsInside
================
*/
bool rvTramCar_Marine::EntityIsInside( const idEntity* entity ) const {
assert( entity );
return GetPhysics()->GetAbsBounds().ContainsPoint( entity->GetPhysics()->GetAbsBounds().GetCenter() );
}
/*
================
rvTramCar_Marine::DeployRamp
================
*/
void rvTramCar_Marine::DeployRamp() {
rvTramCar::DeployRamp();
if( weapons.Num() ) {
weapons[0]->Unlock();
weapons[0]->EjectAllDrivers( true );
}
}
/*
================
rvTramCar_Marine::RetractRamp
================
*/
void rvTramCar_Marine::RetractRamp() {
rvTramCar::RetractRamp();
UseMountedGun( gameLocal.GetLocalPlayer() );
}
/*
================
rvTramCar_Marine::UseMountedGun
================
*/
void rvTramCar_Marine::UseMountedGun( idPlayer* player ) {
if( playerOccupationStateThread.CurrentStateIs("Occupied") && EntityIsInside(player) && weapons.Num() ) {
player->EnterVehicle( weapons[0] );
weapons[0]->Lock();
}
}
/*
================
rvTramCar_Marine::Event_UseMountedGun
================
*/
void rvTramCar_Marine::Event_UseMountedGun( idEntity* ent ) {
if( !ent || !ent->IsType(idPlayer::GetClassType()) ) {
return;
}
UseMountedGun( static_cast<idPlayer*>(ent) );
}
/*
================
rvTramCar_Marine::Event_SetPlayerDamageEntity
================
*/
void rvTramCar_Marine::Event_SetPlayerDamageEntity(float f) {
gameLocal.GetLocalPlayer()->SetDamageEntity( (f? this : NULL) );
}
CLASS_STATES_DECLARATION( rvTramCar_Marine )
STATE( "Occupied", rvTramCar_Marine::State_Occupied )
STATE( "NotOccupied", rvTramCar_Marine::State_NotOccupied )
STATE( "UsingMountedGun", rvTramCar_Marine::State_UsingMountedGun )
STATE( "NotUsingMountedGun",rvTramCar_Marine::State_NotUsingMountedGun )
END_CLASS_STATES
/*
================
rvTramCar_Marine::State_Occupied
================
*/
stateResult_t rvTramCar_Marine::State_Occupied( const stateParms_t& parms ) {
idPlayer* player = gameLocal.GetLocalPlayer();
if( !parms.stage ) {
//player->ProcessEvent( &EV_Player_DisableWeapon );
ActivateTramHud( player );
return SRESULT_STAGE( parms.stage + 1 );
}
if( !PlayerIsInside() ) {
playerOccupationStateThread.SetState( "NotOccupied" );
//gameLocal.GetLocalPlayer()->SetDamageEntity( NULL );
fl.takedamage = false;
return SRESULT_DONE_WAIT;
}
playerUsingMountedGunStateThread.Execute();
UpdateTramHud( player );
return SRESULT_WAIT;
}
/*
================
rvTramCar_Marine::State_NotOccupied
================
*/
stateResult_t rvTramCar_Marine::State_NotOccupied( const stateParms_t& parms ) {
if( !parms.stage ) {
//player->ProcessEvent( &EV_Player_EnableWeapon );
DeactivateTramHud( gameLocal.GetLocalPlayer() );
return SRESULT_STAGE( parms.stage + 1 );
}
if( PlayerIsInside() ) {
playerOccupationStateThread.SetState( "Occupied" );
//gameLocal.GetLocalPlayer()->SetDamageEntity( this );
fl.takedamage = true;
return SRESULT_DONE_WAIT;
}
return SRESULT_WAIT;
}
/*
================
rvTramCar_Marine::State_UsingMountedGun
================
*/
stateResult_t rvTramCar_Marine::State_UsingMountedGun( const stateParms_t& parms ) {
if( !parms.stage ) {
ActivateTramHud( gameLocal.GetLocalPlayer() );
return SRESULT_STAGE( parms.stage + 1 );
}
idPlayer* player = gameLocal.GetLocalPlayer();
if( player && !player->IsInVehicle() ) {
playerUsingMountedGunStateThread.SetState( "NotUsingMountedGun" );
}
return SRESULT_WAIT;
}
/*
================
rvTramCar_Marine::State_NotUsingMountedGun
================
*/
stateResult_t rvTramCar_Marine::State_NotUsingMountedGun( const stateParms_t& parms ) {
idPlayer* player = gameLocal.GetLocalPlayer();
if( player && player->IsInVehicle() ) {
playerUsingMountedGunStateThread.SetState( "UsingMountedGun" );
}
return SRESULT_WAIT;
}
//=======================================================
//
// rvTramCar_Strogg
//
//=======================================================
CLASS_DECLARATION( rvTramCar, rvTramCar_Strogg )
EVENT( EV_PostSpawn, rvTramCar_Strogg::Event_PostSpawn )
END_CLASS
/*
================
rvTramCar_Strogg::Spawn
================
*/
void rvTramCar_Strogg::Spawn() {
SetTarget( NULL );
RegisterStateThread( targetSearchStateThread, "targetSearch" );
targetSearchStateThread.SetState( "LookingForTarget" );
}
/*
================
rvTramCar_Strogg::SetTarget
================
*/
void rvTramCar_Strogg::SetTarget( idEntity* newTarget ) {
target = ConvertToMover( newTarget );
}
/*
================
rvTramCar_Strogg::GetTarget
================
*/
const rvSplineMover* rvTramCar_Strogg::GetTarget() const {
return target.GetEntity();
}
/*
================
rvTramCar_Strogg::Save
================
*/
void rvTramCar_Strogg::Save( idSaveGame *savefile ) const {
target.Save( savefile );
targetSearchStateThread.Save( savefile );
}
/*
================
rvTramCar_Strogg::Restore
================
*/
void rvTramCar_Strogg::Restore( idRestoreGame *savefile ) {
target.Restore( savefile );
targetSearchStateThread.Restore( savefile, this );
}
/*
================
rvTramCar_Strogg::WriteToSnapshot
================
*/
void rvTramCar_Strogg::WriteToSnapshot( idBitMsgDelta &msg ) const {
}
/*
================
rvTramCar_Strogg::ReadFromSnapshot
================
*/
void rvTramCar_Strogg::ReadFromSnapshot( const idBitMsgDelta &msg ) {
}
/*
================
rvTramCar_Strogg::Event_PostSpawn
================
*/
void rvTramCar_Strogg::Event_PostSpawn() {
idEntity* enemy = gameLocal.FindEntity( spawnArgs.GetString("enemy") );
SetTarget( enemy );
occupants.RemoveNull();
for( int ix = occupants.Num() - 1; ix >= 0; --ix ) {
occupants[ix]->SetEnemy( enemy );
}
rvTramCar::Event_PostSpawn();
}
/*
================
rvTramCar_Strogg::TargetIsToLeft
================
*/
bool rvTramCar_Strogg::TargetIsToLeft() {
idList<rvSplineMover*> list;
if( LookLeft(list) ) {
for( int ix = list.Num() - 1; ix >= 0; --ix ) {
if( list[ix] == target ) {
return true;
}
}
}
return false;
}
/*
================
rvTramCar_Strogg::TargetIsToRight
================
*/
bool rvTramCar_Strogg::TargetIsToRight() {
idList<rvSplineMover*> list;
if( LookRight(list) ) {
for( int ix = list.Num() - 1; ix >= 0; --ix ) {
if( list[ix] == target ) {
return true;
}
}
}
return false;
}
/*
================
rvTramCar_Strogg::LookAround
================
*/
void rvTramCar_Strogg::LookAround() {
targetSearchStateThread.Execute();
}
CLASS_STATES_DECLARATION( rvTramCar_Strogg )
STATE( "LookingForTarget", rvTramCar_Strogg::State_LookingForTarget )
STATE( "TargetInSight", rvTramCar_Strogg::State_TargetInSight )
END_CLASS_STATES
/*
================
rvTramCar_Strogg::State_LookingForTarget
================
*/
stateResult_t rvTramCar_Strogg::State_LookingForTarget( const stateParms_t& parms ) {
static const int MSEC_DELAY = 100;
idList<rvSplineMover*> list;
if( !parms.stage ) {// Go back to random if we are supposed to be random
//idealTrackStateThread.SetState( "RandomTrack" );
return SRESULT_STAGE( parms.stage + 1 );
}
if( AdjustSpeed(list) ) {
return SRESULT_DELAY( MSEC_DELAY );
}
// Could optimise based on what track
if( TargetIsToLeft() || TargetIsToRight() ) {
targetSearchStateThread.SetState( "TargetInSight" );
return SRESULT_DONE;
}
SetSpeed( GetIdealSpeed() );
return SRESULT_DELAY( MSEC_DELAY );
}
/*
================
rvTramCar_Strogg::State_TargetInSight
================
*/
stateResult_t rvTramCar_Strogg::State_TargetInSight( const stateParms_t& parms ) {
static const int MSEC_DELAY = 3000;
if( !target.IsValid() || (!TargetIsToLeft() && !TargetIsToRight()) ) {
targetSearchStateThread.SetState( "LookingForTarget" );
return SRESULT_DONE;
}
if( !parms.stage ) {
SetIdealTrack( GetCurrentTrack() );
SetSpeed( target->GetSpeed() * 0.5f );
idealTrackStateThread.SetState( "AssignedTrack" );
StartSound( "snd_horn", SND_CHANNEL_ANY, 0, false, NULL );
return SRESULT_STAGE( parms.stage + 1 );
}
idList<rvSplineMover*> list;
if( AdjustSpeed(list) ) {
return SRESULT_DELAY( MSEC_DELAY );
}
float dot = GetPhysics()->GetAxis()[0] * (target->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin()).ToNormal();
float deltaSpeed = target->GetIdealSpeed() * rvRandom::flrand(0.0f, 0.1f) * SignZero(dot);
SetSpeed( target->GetIdealSpeed() + deltaSpeed );
return SRESULT_DELAY( MSEC_DELAY );
}