diff --git a/neo/d3xp/Entity.cpp b/neo/d3xp/Entity.cpp index b350826e..ba332cb6 100644 --- a/neo/d3xp/Entity.cpp +++ b/neo/d3xp/Entity.cpp @@ -1694,7 +1694,11 @@ idEntity::UpdateRenderEntity */ bool idEntity::UpdateRenderEntity( renderEntity_s* renderEntity, const renderView_t* renderView ) { - + if( gameLocal.inCinematic && gameLocal.skipCinematic ) + { + return false; + } + idAnimator* animator = GetAnimator(); if( animator != NULL ) { diff --git a/neo/d3xp/Game.h b/neo/d3xp/Game.h index b9b72658..d2f7fe8d 100644 --- a/neo/d3xp/Game.h +++ b/neo/d3xp/Game.h @@ -183,6 +183,8 @@ public: virtual void Shell_UpdateClientCountdown( int countdown ) = 0; virtual void Shell_UpdateLeaderboard( const idLeaderboardCallback* callback ) = 0; virtual void Shell_SetGameComplete() = 0; + virtual bool SkipCinematicScene() = 0; + virtual bool CheckInCinematic() = 0; }; extern idGame* game; diff --git a/neo/d3xp/Game_local.cpp b/neo/d3xp/Game_local.cpp index eb6c2583..3d777fff 100644 --- a/neo/d3xp/Game_local.cpp +++ b/neo/d3xp/Game_local.cpp @@ -234,6 +234,9 @@ void idGameLocal::Clear() smokeParticles = NULL; editEntities = NULL; entityHash.Clear( 1024, MAX_GENTITIES ); + cinematicSkipTime = 0; + cinematicStopTime = 0; + cinematicMaxSkipTime = 0; inCinematic = false; framenum = 0; previousTime = 0; @@ -253,6 +256,7 @@ void idGameLocal::Clear() playerPVS.h = ( unsigned int ) - 1; playerConnectedAreas.h = ( unsigned int ) - 1; gamestate = GAMESTATE_UNINITIALIZED; + skipCinematic = false; influenceActive = false; realClientTime = 0; @@ -587,7 +591,11 @@ void idGameLocal::SaveGame( idFile* f, idFile* strings ) // FIXME: save smoke particles + savegame.WriteInt( cinematicSkipTime ); + savegame.WriteInt( cinematicStopTime ); + savegame.WriteInt( cinematicMaxSkipTime ); savegame.WriteBool( inCinematic ); + savegame.WriteBool( skipCinematic ); savegame.WriteInt( gameType ); @@ -1034,6 +1042,10 @@ void idGameLocal::LoadMap( const char* mapName, int randseed ) spawnArgs.Clear(); inCinematic = false; + skipCinematic = false; + cinematicSkipTime = 0; + cinematicStopTime = 0; + cinematicMaxSkipTime = 0; clip.Init(); @@ -1442,7 +1454,11 @@ bool idGameLocal::InitFromSaveGame( const char* mapName, idRenderWorld* renderWo // FIXME: save smoke particles + savegame.ReadInt( cinematicSkipTime ); + savegame.ReadInt( cinematicStopTime ); + savegame.ReadInt( cinematicMaxSkipTime ); savegame.ReadBool( inCinematic ); + savegame.ReadBool( skipCinematic ); savegame.ReadInt( ( int& )gameType ); @@ -2548,185 +2564,210 @@ void idGameLocal::RunFrame( idUserCmdMgr& cmdMgr, gameReturn_t& ret ) player->Think(); } } - else - { - // update the game time - framenum++; - fast.previousTime = FRAME_TO_MSEC( framenum - 1 ); - fast.time = FRAME_TO_MSEC( framenum ); - fast.realClientTime = fast.time; - SetServerGameTimeMs( fast.time ); - - ComputeSlowScale(); - - slow.previousTime = slow.time; - slow.time += idMath::Ftoi( ( fast.time - fast.previousTime ) * slowmoScale ); - slow.realClientTime = slow.time; - - SelectTimeGroup( false ); - + else do + { + // update the game time + framenum++; + fast.previousTime = FRAME_TO_MSEC( framenum - 1 ); + fast.time = FRAME_TO_MSEC( framenum ); + fast.realClientTime = fast.time; + SetServerGameTimeMs( fast.time ); + + ComputeSlowScale(); + + slow.previousTime = slow.time; + slow.time += idMath::Ftoi( ( fast.time - fast.previousTime ) * slowmoScale ); + slow.realClientTime = slow.time; + + SelectTimeGroup( false ); + #ifdef GAME_DLL - // allow changing SIMD usage on the fly - if( com_forceGenericSIMD.IsModified() ) - { - idSIMD::InitProcessor( "game", com_forceGenericSIMD.GetBool() ); - } + // allow changing SIMD usage on the fly + if( com_forceGenericSIMD.IsModified() ) + { + idSIMD::InitProcessor( "game", com_forceGenericSIMD.GetBool() ); + } #endif - - // make sure the random number counter is used each frame so random events - // are influenced by the player's actions - random.RandomInt(); - - if( player ) - { - // update the renderview so that any gui videos play from the right frame - view = player->GetRenderView(); - if( view ) + + // make sure the random number counter is used each frame so random events + // are influenced by the player's actions + random.RandomInt(); + + if( player ) { - gameRenderWorld->SetRenderView( view ); - } - } - - // clear any debug lines from a previous frame - gameRenderWorld->DebugClearLines( time ); - - // clear any debug polygons from a previous frame - gameRenderWorld->DebugClearPolygons( time ); - - // free old smoke particles - smokeParticles->FreeSmokes(); - - // process events on the server - ServerProcessEntityNetworkEventQueue(); - - // update our gravity vector if needed. - UpdateGravity(); - - // create a merged pvs for all players - SetupPlayerPVS(); - - // sort the active entity list - SortActiveEntityList(); - - timer_think.Clear(); - timer_think.Start(); - - // let entities think - if( g_timeentities.GetFloat() ) - { - num = 0; - for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) - { - if( g_cinematic.GetBool() && inCinematic && !ent->cinematic ) + // update the renderview so that any gui videos play from the right frame + view = player->GetRenderView(); + if( view ) { - ent->GetPhysics()->UpdateTime( time ); - continue; + gameRenderWorld->SetRenderView( view ); } - timer_singlethink.Clear(); - timer_singlethink.Start(); - RunEntityThink( *ent, cmdMgr ); - timer_singlethink.Stop(); - ms = timer_singlethink.Milliseconds(); - if( ms >= g_timeentities.GetFloat() ) - { - Printf( "%d: entity '%s': %.1f ms\n", time, ent->name.c_str(), ms ); - } - num++; } - } - else - { - if( inCinematic ) + + // clear any debug lines from a previous frame + gameRenderWorld->DebugClearLines( time ); + + // clear any debug polygons from a previous frame + gameRenderWorld->DebugClearPolygons( time ); + + // free old smoke particles + smokeParticles->FreeSmokes(); + + // process events on the server + ServerProcessEntityNetworkEventQueue(); + + // update our gravity vector if needed. + UpdateGravity(); + + // create a merged pvs for all players + SetupPlayerPVS(); + + // sort the active entity list + SortActiveEntityList(); + + timer_think.Clear(); + timer_think.Start(); + + // let entities think + if( g_timeentities.GetFloat() ) { num = 0; for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) { - if( g_cinematic.GetBool() && !ent->cinematic ) + if( g_cinematic.GetBool() && inCinematic && !ent->cinematic ) { ent->GetPhysics()->UpdateTime( time ); continue; } + timer_singlethink.Clear(); + timer_singlethink.Start(); RunEntityThink( *ent, cmdMgr ); + timer_singlethink.Stop(); + ms = timer_singlethink.Milliseconds(); + if( ms >= g_timeentities.GetFloat() ) + { + Printf( "%d: entity '%s': %.1f ms\n", time, ent->name.c_str(), ms ); + } num++; } } else { - num = 0; - for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) + if( inCinematic ) { - if( ent->timeGroup != TIME_GROUP1 ) + num = 0; + for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) { - continue; + if( g_cinematic.GetBool() && !ent->cinematic ) + { + ent->GetPhysics()->UpdateTime( time ); + continue; + } + RunEntityThink( *ent, cmdMgr ); + num++; } - RunEntityThink( *ent, cmdMgr ); - num++; } - } - } - - RunTimeGroup2( cmdMgr ); - - // Run catch-up for any client projectiles. - // This is done after the main think so that all projectiles will be up-to-date - // when snapshots are created. - //if ( common->IsMultiplayer() ) { - //while ( SimulateProjectiles() ) { - //clientGame.gameLibEffects.Update( clientGame.GetGameMs(), clientGame.GetGameMsPerFrame(), clientGame.GetServerGameTime() ); - //} - //} - - - // remove any entities that have stopped thinking - if( numEntitiesToDeactivate ) - { - idEntity* next_ent; - int c = 0; - for( ent = activeEntities.Next(); ent != NULL; ent = next_ent ) - { - next_ent = ent->activeNode.Next(); - if( !ent->thinkFlags ) + else { - ent->activeNode.Remove(); - c++; + num = 0; + for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) + { + if( ent->timeGroup != TIME_GROUP1 ) + { + continue; + } + RunEntityThink( *ent, cmdMgr ); + num++; + } } } - //assert( numEntitiesToDeactivate == c ); - numEntitiesToDeactivate = 0; + + RunTimeGroup2( cmdMgr ); + + // Run catch-up for any client projectiles. + // This is done after the main think so that all projectiles will be up-to-date + // when snapshots are created. + //if ( common->IsMultiplayer() ) { + //while ( SimulateProjectiles() ) { + //clientGame.gameLibEffects.Update( clientGame.GetGameMs(), clientGame.GetGameMsPerFrame(), clientGame.GetServerGameTime() ); + //} + //} + + + // remove any entities that have stopped thinking + if( numEntitiesToDeactivate ) + { + idEntity* next_ent; + int c = 0; + for( ent = activeEntities.Next(); ent != NULL; ent = next_ent ) + { + next_ent = ent->activeNode.Next(); + if( !ent->thinkFlags ) + { + ent->activeNode.Remove(); + c++; + } + } + //assert( numEntitiesToDeactivate == c ); + numEntitiesToDeactivate = 0; + } + + timer_think.Stop(); + timer_events.Clear(); + timer_events.Start(); + + // service any pending events + idEvent::ServiceEvents(); + + // service pending fast events + SelectTimeGroup( true ); + idEvent::ServiceFastEvents(); + SelectTimeGroup( false ); + + timer_events.Stop(); + + // free the player pvs + FreePlayerPVS(); + + // do multiplayer related stuff + if( common->IsMultiplayer() ) + { + mpGame.Run(); + } + + // display how long it took to calculate the current game frame + if( g_frametime.GetBool() ) + { + Printf( "game %d: all:%.1f th:%.1f ev:%.1f %d ents \n", + time, timer_think.Milliseconds() + timer_events.Milliseconds(), + timer_think.Milliseconds(), timer_events.Milliseconds(), num ); + } + + BuildReturnValue( ret ); + + // see if a target_sessionCommand has forced a changelevel + if( sessionCommand.Length() ) + { + strncpy( ret.sessionCommand, sessionCommand, sizeof( ret.sessionCommand ) ); + break; + } + + // make sure we don't loop forever when skipping a cinematic + if( skipCinematic && ( time > cinematicMaxSkipTime ) ) + { + Warning( "Exceeded maximum cinematic skip length. Cinematic may be looping infinitely." ); + skipCinematic = false; + break; + } } - - timer_think.Stop(); - timer_events.Clear(); - timer_events.Start(); - - // service any pending events - idEvent::ServiceEvents(); - - // service pending fast events - SelectTimeGroup( true ); - idEvent::ServiceFastEvents(); - SelectTimeGroup( false ); - - timer_events.Stop(); - - // free the player pvs - FreePlayerPVS(); - - // do multiplayer related stuff - if( common->IsMultiplayer() ) - { - mpGame.Run(); - } - - // display how long it took to calculate the current game frame - if( g_frametime.GetBool() ) - { - Printf( "game %d: all:%.1f th:%.1f ev:%.1f %d ents \n", - time, timer_think.Milliseconds() + timer_events.Milliseconds(), - timer_think.Milliseconds(), timer_events.Milliseconds(), num ); - } - - BuildReturnValue( ret ); + while( ( inCinematic || ( time < cinematicStopTime ) ) && skipCinematic ); + // i think this loop always play all that content until we skip the cinematic, + // when then we keep until the end of the function. + + //ret.syncNextGameFrame = skipCinematic; // this is form dhewm3 but it seems it's no longer useful + if( skipCinematic ) + { + soundSystem->SetMute( false ); + skipCinematic = false; } // show any debug info for this frame @@ -4747,6 +4788,20 @@ void idGameLocal::SetCamera( idCamera* cam ) { inCinematic = true; + if( skipCinematic && camera->spawnArgs.GetBool( "disconnect" ) ) + { + camera->spawnArgs.SetBool( "disconnect", false ); + cvarSystem->SetCVarFloat( "r_znear", 3.0f ); + cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" ); + skipCinematic = false; + return; + } + + if( time > cinematicStopTime ) + { + cinematicSkipTime = time + CINEMATIC_SKIP_DELAY; + } + // set r_znear so that transitioning into/out of the player's head doesn't clip through the view cvarSystem->SetCVarFloat( "r_znear", 1.0f ); @@ -4804,6 +4859,7 @@ void idGameLocal::SetCamera( idCamera* cam ) else { inCinematic = false; + cinematicStopTime = time + msec; // restore r_znear cvarSystem->SetCVarFloat( "r_znear", 3.0f ); @@ -4830,6 +4886,58 @@ idCamera* idGameLocal::GetCamera() const return camera; } +/* +============= +idGameLocal::SkipCinematic +============= +*/ +bool idGameLocal::SkipCinematic( void ) +{ + if( camera ) + { + if( camera->spawnArgs.GetBool( "disconnect" ) ) + { + camera->spawnArgs.SetBool( "disconnect", false ); + cvarSystem->SetCVarFloat( "r_znear", 3.0f ); + cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" ); + skipCinematic = false; + return false; + } + + if( camera->spawnArgs.GetBool( "instantSkip" ) ) + { + camera->Stop(); + return false; + } + } + + soundSystem->SetMute( true ); + if( !skipCinematic ) + { + skipCinematic = true; + cinematicMaxSkipTime = gameLocal.time + SEC2MS( g_cinematicMaxSkipTime.GetFloat() ); + } + + return true; +} +/* +============= +idGameLocal::SkipCinematicScene +============= +*/ +bool idGameLocal::SkipCinematicScene() +{ + return SkipCinematic(); +} +/* +============= +idGameLocal::CheckInCinematic +============= +*/ +bool idGameLocal::CheckInCinematic() +{ + return inCinematic; +} /* ====================== idGameLocal::SpreadLocations diff --git a/neo/d3xp/Game_local.h b/neo/d3xp/Game_local.h index 1e551d29..65526491 100644 --- a/neo/d3xp/Game_local.h +++ b/neo/d3xp/Game_local.h @@ -329,11 +329,16 @@ public: idSmokeParticles* smokeParticles; // global smoke trails idEditEntities* editEntities; // in game editing + int cinematicSkipTime; // don't allow skipping cinemetics until this time has passed so player doesn't skip out accidently from a firefight + int cinematicStopTime; // cinematics have several camera changes, so keep track of when we stop them so that we don't reset cinematicSkipTime unnecessarily + int cinematicMaxSkipTime; // time to end cinematic when skipping. there's a possibility of an infinite loop if the map isn't set up right. bool inCinematic; // game is playing cinematic (player controls frozen) + bool skipCinematic; int framenum; int time; // in msec int previousTime; // time in msec of last frame + static const int msec = USERCMD_MSEC; // time since last update in milliseconds int vacuumAreaNum; // -1 if level doesn't have any outside areas @@ -479,6 +484,7 @@ public: void SetCamera( idCamera* cam ); idCamera* GetCamera() const; + bool SkipCinematic( void ); void CalcFov( float base_fov, float& fov_x, float& fov_y ) const; void AddEntityToHash( const char* name, idEntity* ent ); @@ -596,6 +602,8 @@ public: virtual void Shell_UpdateClientCountdown( int countdown ); virtual void Shell_UpdateLeaderboard( const idLeaderboardCallback* callback ); virtual void Shell_SetGameComplete(); + virtual bool SkipCinematicScene(); + virtual bool CheckInCinematic(); void Shell_ClearRepeater(); diff --git a/neo/d3xp/anim/Anim_Blend.cpp b/neo/d3xp/anim/Anim_Blend.cpp index 1a261da2..96898520 100644 --- a/neo/d3xp/anim/Anim_Blend.cpp +++ b/neo/d3xp/anim/Anim_Blend.cpp @@ -5169,6 +5169,11 @@ bool idAnimator::CreateFrame( int currentTime, bool force ) static idCVar r_showSkel( "r_showSkel", "0", CVAR_RENDERER | CVAR_INTEGER, "", 0, 2, idCmdSystem::ArgCompletion_Integer<0, 2> ); + if( gameLocal.inCinematic && gameLocal.skipCinematic ) + { + return false; + } + if( !modelDef || !modelDef->ModelHandle() ) { return false; diff --git a/neo/framework/Common.cpp b/neo/framework/Common.cpp index 42d218d5..9f420eda 100644 --- a/neo/framework/Common.cpp +++ b/neo/framework/Common.cpp @@ -1762,31 +1762,38 @@ bool idCommonLocal::ProcessEvent( const sysEvent_t* event ) { if( event->evType == SE_KEY && event->evValue2 == 1 && ( event->evValue == K_ESCAPE || event->evValue == K_JOY9 ) ) { - if( !game->Shell_IsActive() ) + if( game->CheckInCinematic() == true ) { - - // menus / etc - if( MenuEvent( event ) ) - { - return true; - } - - console->Close(); - - StartMenu(); - return true; + game->SkipCinematicScene(); } else { - console->Close(); - - // menus / etc - if( MenuEvent( event ) ) + if( !game->Shell_IsActive() ) { + + // menus / etc + if( MenuEvent( event ) ) + { + return true; + } + + console->Close(); + + StartMenu(); return true; } - - game->Shell_ClosePause(); + else + { + console->Close(); + + // menus / etc + if( MenuEvent( event ) ) + { + return true; + } + + game->Shell_ClosePause(); + } } } } diff --git a/neo/framework/UsercmdGen.h b/neo/framework/UsercmdGen.h index 23f32616..cd8447e0 100644 --- a/neo/framework/UsercmdGen.h +++ b/neo/framework/UsercmdGen.h @@ -31,6 +31,11 @@ If you have questions concerning this license or the applicable additional terms #include "../sys/sys_session.h" +// FIXME this might not be true if the game runs at 120 fps... +const int USERCMD_HZ = 60; // 60 frames per second +const int USERCMD_MSEC = 1000 / USERCMD_HZ; + + /* ===============================================================================