418 lines
11 KiB
C++
418 lines
11 KiB
C++
|
#include "../idlib/precompiled.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include "Game_local.h"
|
||
|
#include "ai/AI.h"
|
||
|
|
||
|
#define RECORD_STATE_TRACE_LEN 2048.0f
|
||
|
|
||
|
class rvGamePlayback
|
||
|
{
|
||
|
public:
|
||
|
rvGamePlayback( void );
|
||
|
~rvGamePlayback( void );
|
||
|
|
||
|
void RecordData( const usercmd_t &cmd, idEntity *source );
|
||
|
private:
|
||
|
int mStartTime;
|
||
|
byte mOldFlags;
|
||
|
idStr mName;
|
||
|
idClipModel *mClipModel;
|
||
|
rvDeclPlayback *mPlayback;
|
||
|
};
|
||
|
|
||
|
idCVar g_recordPlayback( "g_recordPlayback", "0", CVAR_GAME | CVAR_INTEGER | CVAR_NOCHEAT, "record the player movement in a playback" );
|
||
|
idCVar g_playPlayback( "g_playPlayback", "0", CVAR_GAME | CVAR_INTEGER | CVAR_NOCHEAT, "plays the current playback in a camera path" );
|
||
|
|
||
|
rvGamePlayback *gamePlayback = NULL;
|
||
|
rvCameraPlayback *playbackCamera = NULL;
|
||
|
|
||
|
rvGamePlayback::rvGamePlayback( void )
|
||
|
{
|
||
|
idStr newName;
|
||
|
const idVec3 trace_mins( -1.0f, -1.0f, -1.0f );
|
||
|
const idVec3 trace_maxs( 1.0f, 1.0f, 1.0f );
|
||
|
const idBounds trace_bounds( trace_mins, trace_maxs );
|
||
|
idTraceModel traceModel( trace_bounds );
|
||
|
|
||
|
mStartTime = gameLocal.time;
|
||
|
mOldFlags = 0;
|
||
|
mClipModel = new idClipModel( traceModel );
|
||
|
|
||
|
if( !g_currentPlayback.GetInteger() )
|
||
|
{
|
||
|
newName = declManager->GetNewName( DECL_PLAYBACK, "playbacks/untitled" );
|
||
|
mPlayback = ( rvDeclPlayback * )declManager->CreateNewDecl( DECL_PLAYBACK, newName, newName + ".playback" );
|
||
|
mPlayback->ReplaceSourceFileText();
|
||
|
mPlayback->Invalidate();
|
||
|
|
||
|
g_currentPlayback.SetInteger( mPlayback->Index() );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mPlayback = ( rvDeclPlayback * )declManager->PlaybackByIndex( g_currentPlayback.GetInteger() );
|
||
|
}
|
||
|
|
||
|
declManager->StartPlaybackRecord( mPlayback );
|
||
|
common->Printf( "Starting playback record to %s type %d\n", mPlayback->GetName(), g_recordPlayback.GetInteger() );
|
||
|
}
|
||
|
|
||
|
rvGamePlayback::~rvGamePlayback( void )
|
||
|
{
|
||
|
declManager->FinishPlayback( mPlayback );
|
||
|
delete mClipModel;
|
||
|
|
||
|
common->Printf( "Stopping playback play/record\n" );
|
||
|
}
|
||
|
|
||
|
void rvGamePlayback::RecordData( const usercmd_t &cmd, idEntity *source )
|
||
|
{
|
||
|
idPlayer *player;
|
||
|
trace_t trace;
|
||
|
idMat3 axis;
|
||
|
idVec3 start, end;
|
||
|
rvDeclPlaybackData info;
|
||
|
|
||
|
info.Init();
|
||
|
|
||
|
switch( g_recordPlayback.GetInteger() )
|
||
|
{
|
||
|
case 1:
|
||
|
info.SetPosition( source->GetPhysics()->GetOrigin() );
|
||
|
info.SetAngles( source->GetPhysics()->GetAxis().ToAngles() );
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
gameLocal.GetPlayerView( start, axis );
|
||
|
|
||
|
end = start + axis[0] * RECORD_STATE_TRACE_LEN;
|
||
|
|
||
|
gameLocal.Translation( gameLocal.GetLocalPlayer(), trace, start, end, mClipModel, mat3_identity, CONTENTS_SOLID | CONTENTS_RENDERMODEL, source );
|
||
|
|
||
|
if( trace.fraction != 1.0f )
|
||
|
{
|
||
|
info.SetPosition( trace.endpos );
|
||
|
info.SetAngles( trace.c.normal.ToAngles() );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
assert( source->IsType( idPlayer::GetClassType() ) );
|
||
|
if( source->IsType( idPlayer::GetClassType() ) )
|
||
|
{
|
||
|
player = static_cast<idPlayer *>( source );
|
||
|
info.SetPosition( player->GetEyePosition() );
|
||
|
info.SetAngles( source->GetPhysics()->GetAxis().ToAngles() );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Record buttons
|
||
|
info.SetButtons( cmd.buttons );
|
||
|
|
||
|
// Record impulses
|
||
|
if( ( cmd.flags & UCF_IMPULSE_SEQUENCE ) != ( mOldFlags & UCF_IMPULSE_SEQUENCE ) )
|
||
|
{
|
||
|
info.SetImpulse( cmd.impulse );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
info.SetImpulse( 0 );
|
||
|
}
|
||
|
mOldFlags = cmd.flags;
|
||
|
|
||
|
declManager->SetPlaybackData( mPlayback, gameLocal.time - mStartTime, -1, &info );
|
||
|
}
|
||
|
|
||
|
// ================================================================================================
|
||
|
|
||
|
void idGameEdit::DrawPlaybackDebugInfo( void )
|
||
|
{
|
||
|
int duration, time;
|
||
|
rvDeclPlaybackData pbd, pbdOld;
|
||
|
const rvDeclPlayback *pb;
|
||
|
|
||
|
pb = declManager->PlaybackByIndex( g_currentPlayback.GetInteger(), true );
|
||
|
if( pb )
|
||
|
{
|
||
|
duration = SEC2MS( pb->GetDuration() );
|
||
|
pbd.Init();
|
||
|
pbdOld.Init();
|
||
|
|
||
|
declManager->GetPlaybackData( pb, -1, 0, 0, &pbdOld );
|
||
|
for( time = gameLocal.GetMSec(); time < duration; time += gameLocal.GetMSec() * g_showPlayback.GetInteger() )
|
||
|
{
|
||
|
declManager->GetPlaybackData( pb, -1, time, time, &pbd );
|
||
|
gameRenderWorld->DebugArrow( colorGreen, pbdOld.GetPosition(), pbd.GetPosition(), 2 );
|
||
|
pbdOld = pbd;
|
||
|
}
|
||
|
|
||
|
gameRenderWorld->DebugBounds( colorRed, pb->GetBounds(), pb->GetOrigin() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void idGameEdit::RecordPlayback( const usercmd_t &cmd, idEntity *source )
|
||
|
{
|
||
|
// Not recording - so instantly exit
|
||
|
if( !g_recordPlayback.GetInteger() && !gamePlayback )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if( !gamePlayback )
|
||
|
{
|
||
|
gamePlayback = new rvGamePlayback();
|
||
|
}
|
||
|
|
||
|
if( g_recordPlayback.GetInteger() )
|
||
|
{
|
||
|
gamePlayback->RecordData( cmd, source );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
delete gamePlayback;
|
||
|
gamePlayback = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ================================================================================================
|
||
|
|
||
|
bool idGameEdit::PlayPlayback( void )
|
||
|
{
|
||
|
// Not playing - so instantly exit
|
||
|
if( !g_playPlayback.GetInteger() && !playbackCamera )
|
||
|
{
|
||
|
return( false );
|
||
|
}
|
||
|
|
||
|
if( !playbackCamera )
|
||
|
{
|
||
|
playbackCamera = static_cast<rvCameraPlayback *>( gameLocal.SpawnEntityType( rvCameraPlayback::GetClassType() ) );
|
||
|
SetCamera( playbackCamera );
|
||
|
|
||
|
common->Printf( "Starting playback play\n" );
|
||
|
}
|
||
|
|
||
|
if( g_currentPlayback.IsModified() )
|
||
|
{
|
||
|
// Spawn is a misnomer - it should be init with new data
|
||
|
playbackCamera->Spawn();
|
||
|
g_currentPlayback.ClearModified();
|
||
|
}
|
||
|
|
||
|
if( !g_playPlayback.GetInteger() )
|
||
|
{
|
||
|
playbackCamera->PostEventMS( &EV_Remove, 0 );
|
||
|
playbackCamera = NULL;
|
||
|
SetCamera( NULL );
|
||
|
}
|
||
|
|
||
|
return( true );
|
||
|
}
|
||
|
|
||
|
// ================================================================================================
|
||
|
|
||
|
void idGameEdit::ShutdownPlaybacks( void )
|
||
|
{
|
||
|
g_recordPlayback.SetInteger( 0 );
|
||
|
g_playPlayback.SetInteger( 0 );
|
||
|
|
||
|
if( gamePlayback )
|
||
|
{
|
||
|
delete gamePlayback;
|
||
|
gamePlayback = NULL;
|
||
|
}
|
||
|
|
||
|
if( playbackCamera )
|
||
|
{
|
||
|
playbackCamera->PostEventMS( &EV_Remove, 0 );
|
||
|
playbackCamera = NULL;
|
||
|
SetCamera( NULL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ================================================================================================
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
rvPlaybackDriver::Start
|
||
|
|
||
|
Start a new playback automatically blending with the old playback (if any) over numFrames
|
||
|
============
|
||
|
*/
|
||
|
bool rvPlaybackDriver::Start( const char *playback, idEntity *owner, int flags, int numFrames )
|
||
|
{
|
||
|
idVec3 startPos;
|
||
|
|
||
|
if( !idStr::Length( playback ) )
|
||
|
{
|
||
|
mPlaybackDecl = NULL;
|
||
|
mOldPlaybackDecl = NULL;
|
||
|
return( true );
|
||
|
}
|
||
|
|
||
|
const rvDeclPlayback *pb = declManager->FindPlayback( playback );
|
||
|
|
||
|
if( g_showPlayback.GetInteger() )
|
||
|
{
|
||
|
common->Printf( "Starting playback: %s\n", pb->GetName() );
|
||
|
}
|
||
|
|
||
|
mOldPlaybackDecl = mPlaybackDecl;
|
||
|
mOldFlags = mFlags;
|
||
|
mOldStartTime = mStartTime;
|
||
|
mOldOffset = mOffset;
|
||
|
|
||
|
mPlaybackDecl = pb;
|
||
|
mFlags = flags;
|
||
|
mStartTime = gameLocal.time;
|
||
|
mOffset.Zero();
|
||
|
|
||
|
if( flags & PBFL_RELATIVE_POSITION )
|
||
|
{
|
||
|
mOffset = owner->GetPhysics()->GetOrigin() - pb->GetOrigin();
|
||
|
}
|
||
|
|
||
|
mTransitionTime = numFrames * gameLocal.GetMSec();
|
||
|
return( true );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
PlaybackCallback
|
||
|
|
||
|
Called whenever a button up or down, or an impulse event is found while getting the playback data
|
||
|
============
|
||
|
*/
|
||
|
void PlaybackCallback( int type, float time, const void *data )
|
||
|
{
|
||
|
const rvDeclPlaybackData *pbd = ( const rvDeclPlaybackData * )data;
|
||
|
idEntity *ent = pbd->GetEntity();
|
||
|
|
||
|
ent->PostEventSec( &EV_PlaybackCallback, time, type, pbd->GetChanged(), pbd->GetImpulse() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
rvPlaybackDriver::UpdateFrame
|
||
|
|
||
|
Blend two playbacks together
|
||
|
============
|
||
|
*/
|
||
|
bool rvPlaybackDriver::UpdateFrame( idEntity *ent, rvDeclPlaybackData &out )
|
||
|
{
|
||
|
rvDeclPlaybackData pbd, oldPbd;
|
||
|
float blend, invBlend;
|
||
|
idStr ret;
|
||
|
bool expired, oldExpired;
|
||
|
|
||
|
// Get the current playback position
|
||
|
pbd.Init();
|
||
|
pbd.SetCallback( ent, PlaybackCallback );
|
||
|
expired = declManager->GetPlaybackData( mPlaybackDecl, mFlags, gameLocal.time - mStartTime, mLastTime - mStartTime, &pbd );
|
||
|
pbd.SetPosition( pbd.GetPosition() + mOffset );
|
||
|
|
||
|
// Get the playback data we are merging from
|
||
|
oldPbd.Init();
|
||
|
oldExpired = declManager->GetPlaybackData( mOldPlaybackDecl, mOldFlags, gameLocal.time - mOldStartTime, gameLocal.time - mOldStartTime, &oldPbd );
|
||
|
oldPbd.SetPosition( oldPbd.GetPosition() + mOldOffset );
|
||
|
|
||
|
mLastTime = gameLocal.time;
|
||
|
|
||
|
if( g_showPlayback.GetInteger() && mPlaybackDecl )
|
||
|
{
|
||
|
common->Printf( "Running playback: %s at %.1f\n", mPlaybackDecl->GetName(), MS2SEC( gameLocal.time - mStartTime ) );
|
||
|
}
|
||
|
|
||
|
// Fully merged - so delete the old one
|
||
|
if( gameLocal.time > mStartTime + mTransitionTime )
|
||
|
{
|
||
|
oldExpired = true;
|
||
|
}
|
||
|
|
||
|
// Interpolate the result
|
||
|
if( expired && oldExpired )
|
||
|
{
|
||
|
out.Init();
|
||
|
mPlaybackDecl = NULL;
|
||
|
mOldPlaybackDecl = NULL;
|
||
|
}
|
||
|
else if( !expired && oldExpired )
|
||
|
{
|
||
|
out = pbd;
|
||
|
mOldPlaybackDecl = NULL;
|
||
|
}
|
||
|
else if( expired && !oldExpired )
|
||
|
{
|
||
|
out = oldPbd;
|
||
|
mPlaybackDecl = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Linear zero to one
|
||
|
blend = idMath::ClampFloat( 0.0f, 1.0f, ( gameLocal.time - mStartTime ) / ( float )mTransitionTime );
|
||
|
|
||
|
// Sinusoidal
|
||
|
blend = idMath::Sin( blend * idMath::HALF_PI );
|
||
|
invBlend = 1.0f - blend;
|
||
|
|
||
|
out.SetPosition( blend * pbd.GetPosition() + invBlend * oldPbd.GetPosition() );
|
||
|
out.SetAngles( blend * pbd.GetAngles() + invBlend * oldPbd.GetAngles() );
|
||
|
out.SetButtons( pbd.GetButtons() );
|
||
|
out.SetImpulse( pbd.GetImpulse() );
|
||
|
}
|
||
|
|
||
|
return( expired );
|
||
|
}
|
||
|
|
||
|
// cnicholson: Begin Added save/restore functionality
|
||
|
/*
|
||
|
============
|
||
|
rvPlaybackDriver::Save
|
||
|
|
||
|
Save all member vars for save/load games
|
||
|
============
|
||
|
*/
|
||
|
void rvPlaybackDriver::Save( idSaveGame *savefile ) const {
|
||
|
|
||
|
savefile->WriteInt( mLastTime ); // cnicholson: Added unsaved var
|
||
|
savefile->WriteInt( mTransitionTime ); // cnicholson: Added unsaved var
|
||
|
|
||
|
savefile->WriteInt( mStartTime ); // cnicholson: Added unsaved var
|
||
|
savefile->WriteInt( mFlags ); // cnicholson: Added unsaved var
|
||
|
// TOSAVE: const rvDeclPlayback *mPlaybackDecl;
|
||
|
savefile->WriteVec3( mOffset ); // cnicholson: Added unsaved var
|
||
|
|
||
|
savefile->WriteInt( mOldStartTime ); // cnicholson: Added unsaved var
|
||
|
savefile->WriteInt( mOldFlags ); // cnicholson: Added unsaved var
|
||
|
// TOSAVE: const rvDeclPlayback *mOldPlaybackDecl;
|
||
|
savefile->WriteVec3( mOldOffset ); // cnicholson: Added unsaved var
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
rvPlaybackDriver::Restore
|
||
|
|
||
|
Restore all member vars for save/load games
|
||
|
============
|
||
|
*/
|
||
|
void rvPlaybackDriver::Restore( idRestoreGame *savefile ) {
|
||
|
|
||
|
savefile->ReadInt( mLastTime ); // cnicholson: Added unrestored var
|
||
|
savefile->ReadInt( mTransitionTime ); // cnicholson: Added unrestored var
|
||
|
|
||
|
savefile->ReadInt( mStartTime ); // cnicholson: Added unrestored var
|
||
|
savefile->ReadInt( mFlags ); // cnicholson: Added unrestored var
|
||
|
// TOSAVE: const rvDeclPlayback *mPlaybackDecl;
|
||
|
savefile->ReadVec3( mOffset ); // cnicholson: Added unrestored var
|
||
|
|
||
|
savefile->ReadInt( mOldStartTime ); // cnicholson: Added unrestored var
|
||
|
savefile->ReadInt( mOldFlags ); // cnicholson: Added unrestored var
|
||
|
// TOSAVE: const rvDeclPlayback *mOldPlaybackDecl;
|
||
|
savefile->ReadVec3( mOldOffset ); // cnicholson: Added unrestored var
|
||
|
}
|
||
|
// cnicholson: End Added save/restore functionality
|
||
|
|
||
|
// end
|
||
|
|