mirror of
https://github.com/ptitSeb/Serious-Engine
synced 2024-11-29 21:25:54 +01:00
3002 lines
99 KiB
C++
3002 lines
99 KiB
C++
|
/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */
|
||
|
|
||
|
// Game.cpp : Defines the initialization routines for the DLL.
|
||
|
//
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
#include "Game.h"
|
||
|
#include <direct.h> // for _mkdir()
|
||
|
#include <sys/timeb.h>
|
||
|
#include <time.h>
|
||
|
#include <locale.h>
|
||
|
#include <io.h>
|
||
|
#include <Engine/Base/Profiling.h>
|
||
|
#include <Engine/Base/Statistics.h>
|
||
|
#include <Engine/CurrentVersion.h>
|
||
|
#include "Camera.h"
|
||
|
#include "LCDDrawing.h"
|
||
|
|
||
|
extern FLOAT con_fHeightFactor = 0.5f;
|
||
|
extern FLOAT con_tmLastLines = 5.0f;
|
||
|
extern INDEX con_bTalk = 0;
|
||
|
CTimerValue _tvMenuQuickSave(0I64);
|
||
|
|
||
|
// used filenames
|
||
|
CTFileName fnmPersistentSymbols = CTString("Scripts\\PersistentSymbols.ini");
|
||
|
CTFileName fnmStartupScript = CTString("Scripts\\Game_startup.ini");
|
||
|
CTFileName fnmConsoleHistory = CTString("Temp\\ConsoleHistory.txt");
|
||
|
CTFileName fnmCommonControls = CTString("Controls\\System\\Common.ctl");
|
||
|
|
||
|
// force dependency for player class
|
||
|
DECLARE_CTFILENAME( fnmPlayerClass, "Classes\\Player.ecl");
|
||
|
|
||
|
#define MAX_HIGHSCORENAME 16
|
||
|
#define MAX_HIGHSCORETABLESIZE ((MAX_HIGHSCORENAME+1+sizeof(INDEX)*4)*HIGHSCORE_COUNT)*2
|
||
|
UBYTE _aubHighScoreBuffer[MAX_HIGHSCORETABLESIZE];
|
||
|
UBYTE _aubHighScorePacked[MAX_HIGHSCORETABLESIZE];
|
||
|
|
||
|
// controls used for all commands not belonging to any particular player
|
||
|
static CControls _ctrlCommonControls;
|
||
|
|
||
|
// array for keeping all frames' times
|
||
|
static CStaticStackArray<TIME> _atmFrameTimes;
|
||
|
static CStaticStackArray<INDEX> _actTriangles; // world, model, particle, total
|
||
|
|
||
|
|
||
|
// one and only Game object
|
||
|
extern CGame *_pGame = NULL;
|
||
|
|
||
|
extern "C" __declspec (dllexport) CGame *GAME_Create(void)
|
||
|
{
|
||
|
_pGame = new CGame;
|
||
|
|
||
|
return _pGame;
|
||
|
}
|
||
|
|
||
|
// recorded profiling stats
|
||
|
static CTimerValue _tvDemoStarted;
|
||
|
static CTimerValue _tvLastFrame;
|
||
|
static CTString _strProfile;
|
||
|
static BOOL _bDumpNextTime = FALSE;
|
||
|
static BOOL _bStartProfilingNextTime = FALSE;
|
||
|
static BOOL _bProfiling = FALSE;
|
||
|
static INDEX _ctProfileRecording = 0;
|
||
|
static FLOAT gam_iRecordHighScore = -1.0f;
|
||
|
|
||
|
|
||
|
extern FLOAT gam_afAmmoQuantity[5] = {2.0f, 2.0f, 1.0f, 1.0f , 2.0f };
|
||
|
extern FLOAT gam_afDamageStrength[5] = {0.25f, 0.5f, 1.0f, 1.5f , 2.0f };
|
||
|
extern FLOAT gam_afEnemyAttackSpeed[5] = {0.75f, 0.75f, 1.0f, 2.0f , 2.0f };
|
||
|
extern FLOAT gam_afEnemyMovementSpeed[5] = {1.0f , 1.0f , 1.0f, 1.25f, 1.25f};
|
||
|
extern FLOAT gam_fManaTransferFactor = 0.5f;
|
||
|
extern FLOAT gam_fExtraEnemyStrength = 0;
|
||
|
extern FLOAT gam_fExtraEnemyStrengthPerPlayer = 0;
|
||
|
extern INDEX gam_iCredits = -1; // number of credits for respawning
|
||
|
extern FLOAT gam_tmSpawnInvulnerability = 3;
|
||
|
extern INDEX gam_iScoreLimit = 100000;
|
||
|
extern INDEX gam_iFragLimit = 20;
|
||
|
extern INDEX gam_iTimeLimit = 0;
|
||
|
extern INDEX gam_bWeaponsStay = TRUE;
|
||
|
extern INDEX gam_bAmmoStays = TRUE;
|
||
|
extern INDEX gam_bHealthArmorStays = TRUE;
|
||
|
extern INDEX gam_bAllowHealth = TRUE;
|
||
|
extern INDEX gam_bAllowArmor = TRUE;
|
||
|
extern INDEX gam_bInfiniteAmmo = FALSE;
|
||
|
extern INDEX gam_bRespawnInPlace = TRUE;
|
||
|
extern INDEX gam_bPlayEntireGame = TRUE;
|
||
|
extern INDEX gam_bFriendlyFire = FALSE;
|
||
|
extern INDEX gam_ctMaxPlayers = 8;
|
||
|
extern INDEX gam_bWaitAllPlayers = FALSE;
|
||
|
extern INDEX gam_iInitialMana = 100;
|
||
|
extern INDEX gam_bQuickLoad = FALSE;
|
||
|
extern INDEX gam_bQuickSave = FALSE;
|
||
|
extern INDEX gam_iQuickSaveSlots = 8;
|
||
|
|
||
|
extern INDEX gam_iQuickStartDifficulty = 1;
|
||
|
extern INDEX gam_iQuickStartMode = 0;
|
||
|
extern INDEX gam_bQuickStartMP = 0;
|
||
|
|
||
|
extern INDEX gam_bEnableAdvancedObserving = 0;
|
||
|
extern INDEX gam_iObserverConfig = 0;
|
||
|
extern INDEX gam_iObserverOffset = 0;
|
||
|
|
||
|
extern INDEX gam_iStartDifficulty = 1;
|
||
|
extern INDEX gam_iStartMode = 0;
|
||
|
extern CTString gam_strGameAgentExtras = "";
|
||
|
|
||
|
extern INDEX gam_iBlood = 2; // 0=none, 1=green, 2=red, 3=hippie
|
||
|
extern INDEX gam_bGibs = TRUE;
|
||
|
|
||
|
extern INDEX gam_bUseExtraEnemies = TRUE;
|
||
|
|
||
|
static INDEX hud_iEnableStats = 1;
|
||
|
static FLOAT hud_fEnableFPS = 1;
|
||
|
static INDEX hud_iStats = 0;
|
||
|
static INDEX hud_bShowTime = FALSE;
|
||
|
static INDEX hud_bShowClock = FALSE;
|
||
|
static INDEX hud_bShowNetGraph = FALSE;
|
||
|
static INDEX hud_bShowResolution = FALSE;
|
||
|
static INDEX dem_bOSD = FALSE;
|
||
|
static INDEX dem_bPlay = FALSE;
|
||
|
static INDEX dem_bPlayByName = FALSE;
|
||
|
static INDEX dem_bProfile = FALSE;
|
||
|
static INDEX dem_iProfileRate = 5;
|
||
|
static CTString dem_strPostExec = "";
|
||
|
|
||
|
static INDEX ctl_iCurrentPlayerLocal = -1;
|
||
|
static INDEX ctl_iCurrentPlayer = -1;
|
||
|
static FLOAT gam_fChatSoundVolume = 0.25f;
|
||
|
|
||
|
extern BOOL _bUserBreakEnabled = FALSE;
|
||
|
extern BOOL map_bIsFirstEncounter = FALSE;
|
||
|
|
||
|
// make sure that console doesn't show last lines if not playing in network
|
||
|
void MaybeDiscardLastLines(void)
|
||
|
{
|
||
|
// if not in network
|
||
|
if (!_pNetwork->IsNetworkEnabled()) {
|
||
|
// don't show last lines on screen after exiting console
|
||
|
CON_DiscardLastLineTimes();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
class CEnableUserBreak {
|
||
|
public:
|
||
|
BOOL bOld;
|
||
|
CEnableUserBreak();
|
||
|
~CEnableUserBreak();
|
||
|
};
|
||
|
|
||
|
CEnableUserBreak::CEnableUserBreak() {
|
||
|
bOld = _bUserBreakEnabled;
|
||
|
_bUserBreakEnabled = TRUE;
|
||
|
}
|
||
|
CEnableUserBreak::~CEnableUserBreak() {
|
||
|
_bUserBreakEnabled = bOld;
|
||
|
}
|
||
|
|
||
|
|
||
|
// wrapper function for dump and printout of extensive demo profile report
|
||
|
static void DumpDemoProfile(void)
|
||
|
{
|
||
|
CTString strFragment, strAnalyzed;
|
||
|
dem_iProfileRate = Clamp( dem_iProfileRate, 0L, 60L);
|
||
|
strFragment = _pGame->DemoReportFragmentsProfile( dem_iProfileRate);
|
||
|
strAnalyzed = _pGame->DemoReportAnalyzedProfile();
|
||
|
try {
|
||
|
// create file
|
||
|
CTFileStream strm;
|
||
|
CTString strFileName = CTString( "temp\\DemoProfile.lst");
|
||
|
strm.Create_t( strFileName, CTStream::CM_TEXT);
|
||
|
// dump results
|
||
|
strm.FPrintF_t( strFragment);
|
||
|
strm.FPrintF_t( strAnalyzed);
|
||
|
// done!
|
||
|
CPrintF( TRANS("Demo profile data dumped to '%s'.\n"), strFileName);
|
||
|
}
|
||
|
catch (char *strError) {
|
||
|
// something went wrong :(
|
||
|
CPrintF( TRANS("Cannot dump demo profile data: %s\n"), strError);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void ReportDemoProfile(void)
|
||
|
{
|
||
|
CTString strFragment, strAnalyzed;
|
||
|
dem_iProfileRate = Clamp( dem_iProfileRate, 0L, 60L);
|
||
|
strFragment = _pGame->DemoReportFragmentsProfile( dem_iProfileRate);
|
||
|
strAnalyzed = _pGame->DemoReportAnalyzedProfile();
|
||
|
CPrintF( strFragment);
|
||
|
CPrintF( strAnalyzed);
|
||
|
CPrintF( "-\n");
|
||
|
}
|
||
|
|
||
|
#define MAX_SCRIPTSOUNDS 16
|
||
|
static CSoundObject *_apsoScriptChannels[MAX_SCRIPTSOUNDS] = {0};
|
||
|
|
||
|
static void PlayScriptSound(INDEX iChannel, const CTString &strSound, FLOAT fVolume, FLOAT fPitch, BOOL bLooping)
|
||
|
{
|
||
|
if (iChannel<0 || iChannel>=MAX_SCRIPTSOUNDS) {
|
||
|
return;
|
||
|
}
|
||
|
if (_apsoScriptChannels[iChannel]==NULL) {
|
||
|
_apsoScriptChannels[iChannel] = new CSoundObject;
|
||
|
}
|
||
|
_apsoScriptChannels[iChannel]->SetVolume(fVolume, fVolume);
|
||
|
_apsoScriptChannels[iChannel]->SetPitch(fPitch);
|
||
|
try {
|
||
|
_apsoScriptChannels[iChannel]->Play_t(strSound, SOF_NONGAME|(bLooping?SOF_LOOP:0));
|
||
|
} catch (char *strError) {
|
||
|
CPrintF("%s\n", strError);
|
||
|
}
|
||
|
}
|
||
|
static void PlayScriptSoundCfunc(void* pArgs)
|
||
|
{
|
||
|
INDEX iChannel = NEXTARGUMENT(INDEX);
|
||
|
CTString strSound = *NEXTARGUMENT(CTString*);
|
||
|
FLOAT fVolume = NEXTARGUMENT(FLOAT);
|
||
|
FLOAT fPitch = NEXTARGUMENT(FLOAT);
|
||
|
BOOL bLooping = NEXTARGUMENT(INDEX);
|
||
|
PlayScriptSound(iChannel, strSound, fVolume, fPitch, bLooping);
|
||
|
}
|
||
|
static void StopScriptSound(void* pArgs)
|
||
|
{
|
||
|
INDEX iChannel = NEXTARGUMENT(INDEX);
|
||
|
if (iChannel<0 || iChannel>=MAX_SCRIPTSOUNDS||_apsoScriptChannels[iChannel]==NULL) {
|
||
|
return;
|
||
|
}
|
||
|
_apsoScriptChannels[iChannel]->Stop();
|
||
|
}
|
||
|
static INDEX IsScriptSoundPlaying(INDEX iChannel)
|
||
|
{
|
||
|
if (iChannel<0 || iChannel>=MAX_SCRIPTSOUNDS||_apsoScriptChannels[iChannel]==NULL) {
|
||
|
return 0;
|
||
|
}
|
||
|
return _apsoScriptChannels[iChannel]->IsPlaying();
|
||
|
}
|
||
|
|
||
|
// Dump recorded profiling stats to file.
|
||
|
static void DumpProfileToFile(void)
|
||
|
{
|
||
|
_bDumpNextTime = TRUE;
|
||
|
}
|
||
|
|
||
|
// Dump recorded profiling stats to console.
|
||
|
static void DumpProfileToConsole(void)
|
||
|
{
|
||
|
CPutString(_strProfile);
|
||
|
}
|
||
|
|
||
|
// Record profiling stats.
|
||
|
static void RecordProfile(void)
|
||
|
{
|
||
|
_bStartProfilingNextTime = TRUE;
|
||
|
}
|
||
|
|
||
|
// screen shot saving feature in console
|
||
|
static BOOL bSaveScreenShot = FALSE;
|
||
|
static INDEX dem_iAnimFrame = -1;
|
||
|
static void SaveScreenShot(void)
|
||
|
{
|
||
|
bSaveScreenShot=TRUE;
|
||
|
}
|
||
|
|
||
|
static void Say(void* pArgs)
|
||
|
{
|
||
|
CTString strText = *NEXTARGUMENT(CTString*);
|
||
|
_pNetwork->SendChat(-1, -1, strText);
|
||
|
}
|
||
|
static void SayFromTo(void* pArgs)
|
||
|
{
|
||
|
INDEX ulFrom = NEXTARGUMENT(INDEX);
|
||
|
INDEX ulTo = NEXTARGUMENT(INDEX);
|
||
|
CTString strText = *NEXTARGUMENT(CTString*);
|
||
|
_pNetwork->SendChat(ulFrom, ulTo, strText);
|
||
|
}
|
||
|
|
||
|
|
||
|
// create name for a new screenshot
|
||
|
static CTFileName MakeScreenShotName(void)
|
||
|
{
|
||
|
// create base name from the world name
|
||
|
CTFileName fnmBase = CTString("ScreenShots\\")+_pNetwork->GetCurrentWorld().FileName();
|
||
|
|
||
|
// start at counter of zero
|
||
|
INDEX iShot = 0;
|
||
|
// repeat forever
|
||
|
FOREVER {
|
||
|
// create number for the file
|
||
|
CTString strNumber;
|
||
|
strNumber.PrintF("_shot%04d", iShot);
|
||
|
// create the full filename
|
||
|
CTFileName fnmFullTGA = fnmBase+strNumber+".tga";
|
||
|
CTFileName fnmFullJPG = fnmBase+strNumber+".jpg";
|
||
|
// if the file doesn't exist
|
||
|
if (!FileExistsForWriting(fnmFullTGA) && !FileExistsForWriting(fnmFullJPG)) {
|
||
|
// that is the right filename
|
||
|
return fnmFullTGA;
|
||
|
}
|
||
|
// if it exists, increment the number and retry
|
||
|
iShot++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
CButtonAction::CButtonAction(void)
|
||
|
{
|
||
|
ba_iFirstKey = KID_NONE;
|
||
|
ba_iSecondKey = KID_NONE;
|
||
|
ba_bFirstKeyDown = FALSE;
|
||
|
ba_bSecondKeyDown = FALSE;
|
||
|
}
|
||
|
|
||
|
// Assignment operator.
|
||
|
CButtonAction &CButtonAction ::operator=(CButtonAction &baOriginal)
|
||
|
{
|
||
|
ba_iFirstKey = baOriginal.ba_iFirstKey;
|
||
|
ba_iSecondKey = baOriginal.ba_iSecondKey;
|
||
|
ba_strName = baOriginal.ba_strName;
|
||
|
ba_strCommandLineWhenPressed = baOriginal.ba_strCommandLineWhenPressed;
|
||
|
ba_strCommandLineWhenReleased = baOriginal.ba_strCommandLineWhenReleased;
|
||
|
ba_bFirstKeyDown = FALSE;
|
||
|
ba_bSecondKeyDown = FALSE;
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
void CButtonAction::Read_t( CTStream &istrm)
|
||
|
{
|
||
|
istrm>>ba_iFirstKey;
|
||
|
istrm>>ba_iSecondKey;
|
||
|
istrm>>ba_strName;
|
||
|
istrm>>ba_strCommandLineWhenPressed;
|
||
|
istrm>>ba_strCommandLineWhenReleased;
|
||
|
}
|
||
|
|
||
|
void CButtonAction::Write_t( CTStream &ostrm)
|
||
|
{
|
||
|
ostrm<<ba_iFirstKey;
|
||
|
ostrm<<ba_iSecondKey;
|
||
|
ostrm<<ba_strName;
|
||
|
ostrm<<ba_strCommandLineWhenPressed;
|
||
|
ostrm<<ba_strCommandLineWhenReleased;
|
||
|
}
|
||
|
|
||
|
void CControls::DoButtonActions(void)
|
||
|
{
|
||
|
// for all button actions
|
||
|
FOREACHINLIST( CButtonAction, ba_lnNode, ctrl_lhButtonActions, itButtonAction)
|
||
|
{
|
||
|
// test if first button is pressed
|
||
|
BOOL bFirstPressed = _pInput->GetButtonState( itButtonAction->ba_iFirstKey);
|
||
|
// if it was just pressed
|
||
|
if (bFirstPressed && !itButtonAction->ba_bFirstKeyDown) {
|
||
|
// call pressed command
|
||
|
_pShell->Execute(itButtonAction->ba_strCommandLineWhenPressed);
|
||
|
// if it was just released
|
||
|
} else if (!bFirstPressed && itButtonAction->ba_bFirstKeyDown) {
|
||
|
// call released command
|
||
|
_pShell->Execute(itButtonAction->ba_strCommandLineWhenReleased);
|
||
|
}
|
||
|
// remember pressed state
|
||
|
itButtonAction->ba_bFirstKeyDown = bFirstPressed;
|
||
|
|
||
|
// test if second button is pressed
|
||
|
BOOL bSecondPressed = _pInput->GetButtonState( itButtonAction->ba_iSecondKey);
|
||
|
// if it was just pressed
|
||
|
if (bSecondPressed && !itButtonAction->ba_bSecondKeyDown) {
|
||
|
// call pressed command
|
||
|
_pShell->Execute(itButtonAction->ba_strCommandLineWhenPressed);
|
||
|
// if it was just released
|
||
|
} else if (!bSecondPressed && itButtonAction->ba_bSecondKeyDown) {
|
||
|
// call released command
|
||
|
_pShell->Execute(itButtonAction->ba_strCommandLineWhenReleased);
|
||
|
}
|
||
|
// remember pressed state
|
||
|
itButtonAction->ba_bSecondKeyDown = bSecondPressed;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// get current reading of an axis
|
||
|
FLOAT CControls::GetAxisValue(INDEX iAxis)
|
||
|
{
|
||
|
CAxisAction &aa = ctrl_aaAxisActions[iAxis];
|
||
|
|
||
|
FLOAT fReading = 0.0f;
|
||
|
|
||
|
if (aa.aa_iAxisAction!=AXIS_NONE) {
|
||
|
// get the reading
|
||
|
fReading = _pInput->GetAxisValue(aa.aa_iAxisAction);
|
||
|
|
||
|
// smooth the reading if needed
|
||
|
if ( ctrl_bSmoothAxes || aa.aa_bSmooth) {
|
||
|
FLOAT fSmoothed = (aa.aa_fLastReading+fReading)/2.0f;
|
||
|
aa.aa_fLastReading = fReading;
|
||
|
fReading = fSmoothed;
|
||
|
}
|
||
|
|
||
|
// integrate to get the absolute reading
|
||
|
aa.aa_fAbsolute+=fReading;
|
||
|
|
||
|
// get relative or absolute reading
|
||
|
if (!aa.aa_bRelativeControler) {
|
||
|
fReading = aa.aa_fAbsolute;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// compensate for the deadzone
|
||
|
if (aa.aa_fDeadZone>0) {
|
||
|
FLOAT fDeadZone = aa.aa_fDeadZone/100.0f;
|
||
|
if (fReading<-fDeadZone) {
|
||
|
fReading = (fReading+fDeadZone)/(1-fDeadZone);
|
||
|
} else if (fReading>fDeadZone) {
|
||
|
fReading = (fReading-fDeadZone)/(1-fDeadZone);
|
||
|
} else {
|
||
|
fReading = 0.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// apply sensitivity and inversion
|
||
|
return fReading*aa.aa_fAxisInfluence;
|
||
|
}
|
||
|
|
||
|
void CControls::CreateAction(const CPlayerCharacter &pc, CPlayerAction &paAction, BOOL bPreScan)
|
||
|
{
|
||
|
// set axis-controlled moving
|
||
|
paAction.pa_vTranslation(1) = -GetAxisValue( AXIS_MOVE_LR);
|
||
|
paAction.pa_vTranslation(2) = GetAxisValue( AXIS_MOVE_UD);
|
||
|
paAction.pa_vTranslation(3) = -GetAxisValue( AXIS_MOVE_FB);
|
||
|
// set axis-controlled rotation
|
||
|
paAction.pa_aRotation(1) = (ANGLE)-GetAxisValue( AXIS_TURN_LR);
|
||
|
paAction.pa_aRotation(2) = (ANGLE)GetAxisValue( AXIS_TURN_UD);
|
||
|
paAction.pa_aRotation(3) = (ANGLE)GetAxisValue( AXIS_TURN_BK);
|
||
|
// set axis-controlled view rotation
|
||
|
paAction.pa_aViewRotation(1) = (ANGLE)GetAxisValue( AXIS_LOOK_LR);
|
||
|
paAction.pa_aViewRotation(2) = (ANGLE)GetAxisValue( AXIS_LOOK_UD);
|
||
|
paAction.pa_aViewRotation(3) = (ANGLE)GetAxisValue( AXIS_LOOK_BK);
|
||
|
|
||
|
// execute all button-action shell commands
|
||
|
if (!bPreScan) {
|
||
|
DoButtonActions();
|
||
|
}
|
||
|
//CPrintF("creating: prescan %d, x:%g\n", bPreScan, paAction.pa_aRotation(1));
|
||
|
|
||
|
// make the player class create the action packet
|
||
|
ctl_ComposeActionPacket(pc, paAction, bPreScan);
|
||
|
}
|
||
|
|
||
|
CButtonAction &CControls::AddButtonAction(void)
|
||
|
{
|
||
|
// create a new action
|
||
|
CButtonAction *pbaNew = new CButtonAction;
|
||
|
// add it to end of list
|
||
|
ctrl_lhButtonActions.AddTail(pbaNew->ba_lnNode);
|
||
|
return *pbaNew;
|
||
|
}
|
||
|
|
||
|
void CControls::RemoveButtonAction( CButtonAction &baButtonAction)
|
||
|
{
|
||
|
// remove from list
|
||
|
baButtonAction.ba_lnNode.Remove();
|
||
|
// free it
|
||
|
delete &baButtonAction;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// calculate some useful demo vars
|
||
|
static void CalcDemoProfile( INDEX ctFrames, INDEX &ctFramesNoPeaks,
|
||
|
DOUBLE &dTimeSum, DOUBLE &dTimeSumNoPeaks, TIME &tmAverage, TIME &tmAverageNoPeaks,
|
||
|
TIME &tmSigma, TIME &tmHighLimit, TIME &tmLowLimit, TIME &tmHighPeak, TIME &tmLowPeak,
|
||
|
FLOAT &fAvgWTris, FLOAT &fAvgMTris, FLOAT &fAvgPTris, FLOAT &fAvgTTris,
|
||
|
FLOAT &fAvgWTrisNoPeaks, FLOAT &fAvgMTrisNoPeaks, FLOAT &fAvgPTrisNoPeaks, FLOAT &fAvgTTrisNoPeaks)
|
||
|
{
|
||
|
// calculate raw average
|
||
|
INDEX i;
|
||
|
TIME tmCurrent;
|
||
|
dTimeSum = 0;
|
||
|
DOUBLE dWTriSum=0, dMTriSum=0, dPTriSum=0, dTTriSum=0;
|
||
|
DOUBLE dWTriSumNoPeaks=0, dMTriSumNoPeaks=0, dPTriSumNoPeaks=0, dTTriSumNoPeaks=0;
|
||
|
for( i=0; i<ctFrames; i++) {
|
||
|
dTimeSum += _atmFrameTimes[i];
|
||
|
dWTriSum += _actTriangles[i*4 +0]; // world
|
||
|
dMTriSum += _actTriangles[i*4 +1]; // model
|
||
|
dPTriSum += _actTriangles[i*4 +2]; // particle
|
||
|
dTTriSum += _actTriangles[i*4 +3]; // total
|
||
|
}
|
||
|
tmAverage = dTimeSum / ctFrames;
|
||
|
fAvgWTris = dWTriSum / ctFrames;
|
||
|
fAvgMTris = dMTriSum / ctFrames;
|
||
|
fAvgPTris = dPTriSum / ctFrames;
|
||
|
fAvgTTris = dTTriSum / ctFrames;
|
||
|
|
||
|
// calc raw sigma and limits
|
||
|
DOUBLE dSigmaSum=0;
|
||
|
for( i=0; i<ctFrames; i++) {
|
||
|
tmCurrent = _atmFrameTimes[i];
|
||
|
TIME tmDelta = tmCurrent-tmAverage;
|
||
|
dSigmaSum += tmDelta*tmDelta;
|
||
|
}
|
||
|
tmSigma = Sqrt(dSigmaSum/ctFrames);
|
||
|
tmHighLimit = (tmAverage-tmSigma*2);
|
||
|
tmLowLimit = (tmAverage+tmSigma*2);
|
||
|
|
||
|
// eliminate low peaks
|
||
|
ctFramesNoPeaks = ctFrames;
|
||
|
dTimeSumNoPeaks = dTimeSum;
|
||
|
dWTriSumNoPeaks = dWTriSum;
|
||
|
dMTriSumNoPeaks = dMTriSum;
|
||
|
dPTriSumNoPeaks = dPTriSum;
|
||
|
dTTriSumNoPeaks = dTTriSum;
|
||
|
|
||
|
for( i=0; i<ctFrames; i++) {
|
||
|
tmCurrent = _atmFrameTimes[i];
|
||
|
if( tmHighLimit>tmCurrent || tmLowLimit<tmCurrent) {
|
||
|
dTimeSumNoPeaks -= tmCurrent;
|
||
|
dWTriSumNoPeaks -= _actTriangles[i*4 +0];
|
||
|
dMTriSumNoPeaks -= _actTriangles[i*4 +1];
|
||
|
dPTriSumNoPeaks -= _actTriangles[i*4 +2];
|
||
|
dTTriSumNoPeaks -= _actTriangles[i*4 +3];
|
||
|
ctFramesNoPeaks--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// calculate peaks, new averages and sigma (without peaks)
|
||
|
tmAverageNoPeaks = dTimeSumNoPeaks / ctFramesNoPeaks;
|
||
|
fAvgWTrisNoPeaks = dWTriSumNoPeaks / ctFramesNoPeaks;
|
||
|
fAvgMTrisNoPeaks = dMTriSumNoPeaks / ctFramesNoPeaks;
|
||
|
fAvgPTrisNoPeaks = dPTriSumNoPeaks / ctFramesNoPeaks;
|
||
|
fAvgTTrisNoPeaks = dTTriSumNoPeaks / ctFramesNoPeaks;
|
||
|
|
||
|
dSigmaSum=0;
|
||
|
tmHighPeak=99999, tmLowPeak=0;
|
||
|
for( i=0; i<ctFrames; i++) {
|
||
|
tmCurrent = _atmFrameTimes[i];
|
||
|
if( tmHighLimit>tmCurrent || tmLowLimit<tmCurrent) continue;
|
||
|
TIME tmDelta = tmCurrent-tmAverageNoPeaks;
|
||
|
dSigmaSum += tmDelta*tmDelta;
|
||
|
if( tmHighPeak > tmCurrent) tmHighPeak = tmCurrent;
|
||
|
if( tmLowPeak < tmCurrent) tmLowPeak = tmCurrent;
|
||
|
}
|
||
|
tmSigma = Sqrt( dSigmaSum/ctFramesNoPeaks);
|
||
|
}
|
||
|
|
||
|
|
||
|
// dump demo profile to file
|
||
|
CTString CGame::DemoReportFragmentsProfile( INDEX iRate)
|
||
|
{
|
||
|
CTString strRes="";
|
||
|
CTString strTmp;
|
||
|
INDEX ctFrames = _atmFrameTimes.Count();
|
||
|
|
||
|
// if report is not required
|
||
|
if( dem_iProfileRate==0) {
|
||
|
strRes.PrintF( TRANS("\nFragments report disabled.\n"));
|
||
|
return strRes;
|
||
|
}
|
||
|
|
||
|
// if not enough frames
|
||
|
if( ctFrames<20) {
|
||
|
strRes.PrintF( TRANS("\nNot enough recorded frames to make fragments report.\n"));
|
||
|
return strRes;
|
||
|
}
|
||
|
|
||
|
// enough frames - calc almost everything
|
||
|
strRes.PrintF( TRANS("\nDemo performance results (fragment time = %d seconds):\n"), dem_iProfileRate);
|
||
|
strTmp.PrintF( "------------------------------------------------------\n\n");
|
||
|
strRes += strTmp;
|
||
|
DOUBLE dTimeSum, dTimeSumNoPeaks;
|
||
|
INDEX ctFramesNoPeaks;
|
||
|
TIME tmAverage, tmAverageNoPeaks;
|
||
|
TIME tmSigma, tmHighLimit, tmLowLimit, tmHighPeak, tmLowPeak;
|
||
|
FLOAT fAvgWTris, fAvgMTris, fAvgPTris, fAvgTTris;
|
||
|
FLOAT fAvgWTrisNoPeaks, fAvgMTrisNoPeaks, fAvgPTrisNoPeaks, fAvgTTrisNoPeaks;
|
||
|
CalcDemoProfile( ctFrames, ctFramesNoPeaks, dTimeSum, dTimeSumNoPeaks, tmAverage, tmAverageNoPeaks,
|
||
|
tmSigma, tmHighLimit, tmLowLimit, tmHighPeak, tmLowPeak,
|
||
|
fAvgWTris, fAvgMTris, fAvgPTris, fAvgTTris,
|
||
|
fAvgWTrisNoPeaks, fAvgMTrisNoPeaks, fAvgPTrisNoPeaks, fAvgTTrisNoPeaks);
|
||
|
strTmp.PrintF( TRANS(" # average FPS average FPS (W/O peaks)\n"));
|
||
|
strRes += strTmp;
|
||
|
// loop thru frames and create output of time fragmens
|
||
|
dTimeSum = 0;
|
||
|
dTimeSumNoPeaks = 0;
|
||
|
ctFramesNoPeaks = 0;
|
||
|
FLOAT fFrameCounter = 0;
|
||
|
FLOAT fFrameCounterNoPeaks = 0;
|
||
|
TIME tmRate = dem_iProfileRate;
|
||
|
INDEX iFragment=1;
|
||
|
for( INDEX i=0; i<ctFrames; i++)
|
||
|
{ // get current frame time and calc sums
|
||
|
TIME tmCurrent = _atmFrameTimes[i];
|
||
|
dTimeSum += tmCurrent;
|
||
|
fFrameCounter++;
|
||
|
if( tmHighLimit<=tmCurrent && tmLowLimit>=tmCurrent) {
|
||
|
dTimeSumNoPeaks += tmCurrent;
|
||
|
fFrameCounterNoPeaks++;
|
||
|
}
|
||
|
// enough data for one time fragment
|
||
|
if( dTimeSum>=tmRate) {
|
||
|
FLOAT fTimeOver = dTimeSum - tmRate;
|
||
|
FLOAT fFrameOver = fTimeOver/tmCurrent;
|
||
|
FLOAT fFragmentAverage = tmRate / (fFrameCounter-fFrameOver);
|
||
|
FLOAT fFragmentNoPeaks = (tmRate-(dTimeSum-dTimeSumNoPeaks)) / (fFrameCounterNoPeaks-fFrameOver);
|
||
|
strTmp.PrintF( "%4d %6.1f %6.1f", iFragment, 1.0f/fFragmentAverage, 1.0f/fFragmentNoPeaks);
|
||
|
strRes += strTmp;
|
||
|
INDEX iFragmentAverage10 = FloatToInt(5.0f/fFragmentAverage);
|
||
|
INDEX iFragmentNoPeaks10 = FloatToInt(5.0f/fFragmentNoPeaks);
|
||
|
if( iFragmentAverage10 != iFragmentNoPeaks10) strTmp.PrintF( " !\n");
|
||
|
else strTmp.PrintF( "\n");
|
||
|
strRes += strTmp;
|
||
|
// restart time and frames
|
||
|
dTimeSum = fTimeOver;
|
||
|
dTimeSumNoPeaks = fTimeOver;
|
||
|
fFrameCounter = fFrameOver;
|
||
|
fFrameCounterNoPeaks = fFrameOver;
|
||
|
iFragment++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// all done
|
||
|
return strRes;
|
||
|
}
|
||
|
|
||
|
|
||
|
// printout extensive demo profile report
|
||
|
CTString CGame::DemoReportAnalyzedProfile(void)
|
||
|
{
|
||
|
CTString strRes="";
|
||
|
INDEX ctFrames = _atmFrameTimes.Count();
|
||
|
// nothing kept?
|
||
|
if( ctFrames<20) {
|
||
|
strRes.PrintF( TRANS("\nNot enough recorded frames to analyze.\n"));
|
||
|
return strRes;
|
||
|
}
|
||
|
|
||
|
// calc almost everything
|
||
|
DOUBLE dTimeSum, dTimeSumNoPeaks;
|
||
|
INDEX ctFramesNoPeaks;
|
||
|
TIME tmAverage, tmAverageNoPeaks;
|
||
|
TIME tmSigma, tmHighLimit, tmLowLimit, tmHighPeak, tmLowPeak;
|
||
|
FLOAT fAvgWTris, fAvgMTris, fAvgPTris, fAvgTTris;
|
||
|
FLOAT fAvgWTrisNoPeaks, fAvgMTrisNoPeaks, fAvgPTrisNoPeaks, fAvgTTrisNoPeaks;
|
||
|
CalcDemoProfile( ctFrames, ctFramesNoPeaks, dTimeSum, dTimeSumNoPeaks, tmAverage, tmAverageNoPeaks,
|
||
|
tmSigma, tmHighLimit, tmLowLimit, tmHighPeak, tmLowPeak,
|
||
|
fAvgWTris, fAvgMTris, fAvgPTris, fAvgTTris,
|
||
|
fAvgWTrisNoPeaks, fAvgMTrisNoPeaks, fAvgPTrisNoPeaks, fAvgTTrisNoPeaks);
|
||
|
// calc sustains
|
||
|
DOUBLE dHighSum=0, dLowSum=0;
|
||
|
DOUBLE dCurrentHighSum=0, dCurrentLowSum=0;
|
||
|
INDEX ctHighFrames=0, ctLowFrames=0;
|
||
|
INDEX ctCurrentHighFrames=0, ctCurrentLowFrames=0;
|
||
|
for( INDEX i=0; i<ctFrames; i++)
|
||
|
{ // skip low peaks
|
||
|
TIME tmCurrent = _atmFrameTimes[i];
|
||
|
if( tmHighLimit>tmCurrent || tmLowLimit<tmCurrent) continue;
|
||
|
|
||
|
// high?
|
||
|
if( (tmAverageNoPeaks-tmSigma) > tmCurrent) {
|
||
|
// keep high sustain
|
||
|
dCurrentHighSum += tmCurrent;
|
||
|
ctCurrentHighFrames++;
|
||
|
} else {
|
||
|
// new high sustain found?
|
||
|
if( ctHighFrames < ctCurrentHighFrames) {
|
||
|
ctHighFrames = ctCurrentHighFrames;
|
||
|
dHighSum = dCurrentHighSum;
|
||
|
}
|
||
|
// reset high sustain
|
||
|
ctCurrentHighFrames = 0;
|
||
|
dCurrentHighSum = 0;
|
||
|
}
|
||
|
// low?
|
||
|
if( (tmAverageNoPeaks+tmSigma) < tmCurrent) {
|
||
|
// keep low sustain
|
||
|
dCurrentLowSum += tmCurrent;
|
||
|
ctCurrentLowFrames++;
|
||
|
} else {
|
||
|
// new low sustain found?
|
||
|
if( ctLowFrames < ctCurrentLowFrames) {
|
||
|
ctLowFrames = ctCurrentLowFrames;
|
||
|
dLowSum = dCurrentLowSum;
|
||
|
}
|
||
|
// reset low sustain
|
||
|
ctCurrentLowFrames = 0;
|
||
|
dCurrentLowSum = 0;
|
||
|
}
|
||
|
}
|
||
|
// and results are ...
|
||
|
TIME tmHighSustained = ctHighFrames / dHighSum;
|
||
|
TIME tmLowSustained = ctLowFrames / dLowSum;
|
||
|
|
||
|
// printout
|
||
|
CTString strTmp;
|
||
|
strTmp.PrintF( TRANS("\n%.1f KB used for demo profile:\n"), 1+ ctFrames*5*sizeof(FLOAT)/1024.0f);
|
||
|
strRes += strTmp;
|
||
|
strTmp.PrintF( TRANS(" Originally recorded: %d frames in %.1f seconds => %5.1f FPS average.\n"),
|
||
|
ctFrames, dTimeSum, 1.0f/tmAverage);
|
||
|
strRes += strTmp;
|
||
|
strTmp.PrintF( TRANS("Without excessive peaks: %d frames in %.1f seconds => %5.1f FPS average.\n"),
|
||
|
ctFramesNoPeaks, dTimeSumNoPeaks, 1.0f/tmAverageNoPeaks);
|
||
|
strRes += strTmp;
|
||
|
strTmp.PrintF( TRANS(" High peak: %5.1f FPS\n"), 1.0f/tmHighPeak);
|
||
|
strRes += strTmp;
|
||
|
strTmp.PrintF( TRANS(" Low peak: %5.1f FPS\n"), 1.0f/tmLowPeak);
|
||
|
strRes += strTmp;
|
||
|
// enough values recorder for high sustain?
|
||
|
if( ctHighFrames > (ctFrames/1024+5)) {
|
||
|
strTmp.PrintF( TRANS(" High sustained: %5.1f FPS (%d frames in %.1f seconds)\n"),
|
||
|
tmHighSustained, ctHighFrames, dHighSum);
|
||
|
strRes += strTmp;
|
||
|
}
|
||
|
// enough values recorder for low sustain?
|
||
|
if( ctLowFrames > (ctFrames/1024+5)) {
|
||
|
strTmp.PrintF( TRANS(" Low sustained: %5.1f FPS (%d frames in %.1f seconds)\n"),
|
||
|
tmLowSustained, ctLowFrames, dLowSum);
|
||
|
strRes += strTmp;
|
||
|
}
|
||
|
|
||
|
// do triangle profile output (hidden - maybe not so wise idea)
|
||
|
if( dem_bProfile==217) {
|
||
|
const FLOAT fAvgRTris = fAvgTTris - (fAvgWTris+fAvgMTris+fAvgPTris);
|
||
|
const FLOAT fAvgRTrisNoPeaks = fAvgTTrisNoPeaks - (fAvgWTrisNoPeaks+fAvgMTrisNoPeaks+fAvgPTrisNoPeaks);
|
||
|
strTmp.PrintF( TRANS("Triangles per frame (with and without excessive peaks):\n")); strRes += "\n"+strTmp;
|
||
|
strTmp.PrintF( TRANS(" World: %7.1f / %.1f\n"), fAvgWTris, fAvgWTrisNoPeaks); strRes += strTmp;
|
||
|
strTmp.PrintF( TRANS(" Model: %7.1f / %.1f\n"), fAvgMTris, fAvgMTrisNoPeaks); strRes += strTmp;
|
||
|
strTmp.PrintF( TRANS(" Particle: %7.1f / %.1f\n"), fAvgPTris, fAvgPTrisNoPeaks); strRes += strTmp;
|
||
|
strTmp.PrintF( TRANS(" rest (2D): %7.1f / %.1f\n"), fAvgRTris, fAvgRTrisNoPeaks); strRes += strTmp;
|
||
|
strRes += " --------------------\n";
|
||
|
strTmp.PrintF( TRANS(" TOTAL: %7.1f / %.1f\n"), fAvgTTris, fAvgTTrisNoPeaks); strRes += strTmp;
|
||
|
}
|
||
|
|
||
|
// all done
|
||
|
return strRes;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* This is called every TickQuantum seconds. */
|
||
|
void CGameTimerHandler::HandleTimer(void)
|
||
|
{
|
||
|
// call game's timer routine
|
||
|
_pGame->GameHandleTimer();
|
||
|
}
|
||
|
|
||
|
|
||
|
void CGame::GameHandleTimer(void)
|
||
|
{
|
||
|
// if direct input is active
|
||
|
if( _pInput->IsInputEnabled() && !gm_bMenuOn)
|
||
|
{
|
||
|
|
||
|
// check if any active control uses joystick
|
||
|
BOOL bAnyJoy = _ctrlCommonControls.UsesJoystick();
|
||
|
for( INDEX iPlayer=0; iPlayer<4; iPlayer++) {
|
||
|
if( gm_lpLocalPlayers[ iPlayer].lp_pplsPlayerSource != NULL) {
|
||
|
INDEX iCurrentPlayer = gm_lpLocalPlayers[ iPlayer].lp_iPlayer;
|
||
|
CControls &ctrls = gm_actrlControls[ iCurrentPlayer];
|
||
|
if (ctrls.UsesJoystick()) {
|
||
|
bAnyJoy = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
_pInput->SetJoyPolling(bAnyJoy);
|
||
|
|
||
|
// read input devices
|
||
|
_pInput->GetInput(FALSE);
|
||
|
|
||
|
// if game is currently active, and not paused
|
||
|
if (gm_bGameOn && !_pNetwork->IsPaused() && !_pNetwork->GetLocalPause())
|
||
|
{
|
||
|
// for all possible local players
|
||
|
for( INDEX iPlayer=0; iPlayer<4; iPlayer++)
|
||
|
{
|
||
|
// if this player exist
|
||
|
if( gm_lpLocalPlayers[ iPlayer].lp_pplsPlayerSource != NULL)
|
||
|
{
|
||
|
// publish player index to console
|
||
|
ctl_iCurrentPlayerLocal = iPlayer;
|
||
|
ctl_iCurrentPlayer = gm_lpLocalPlayers[ iPlayer].lp_pplsPlayerSource->pls_Index;
|
||
|
|
||
|
// copy its local controls to current controls
|
||
|
memcpy(
|
||
|
ctl_pvPlayerControls,
|
||
|
gm_lpLocalPlayers[ iPlayer].lp_ubPlayerControlsState,
|
||
|
ctl_slPlayerControlsSize);
|
||
|
|
||
|
// create action for it for this tick
|
||
|
CPlayerAction paAction;
|
||
|
INDEX iCurrentPlayer = gm_lpLocalPlayers[ iPlayer].lp_iPlayer;
|
||
|
CControls &ctrls = gm_actrlControls[ iCurrentPlayer];
|
||
|
ctrls.CreateAction(gm_apcPlayers[iCurrentPlayer], paAction, FALSE);
|
||
|
// set the action in the client source object
|
||
|
gm_lpLocalPlayers[ iPlayer].lp_pplsPlayerSource->SetAction(paAction);
|
||
|
|
||
|
// copy the local controls back
|
||
|
memcpy(
|
||
|
gm_lpLocalPlayers[ iPlayer].lp_ubPlayerControlsState,
|
||
|
ctl_pvPlayerControls,
|
||
|
ctl_slPlayerControlsSize);
|
||
|
}
|
||
|
}
|
||
|
// clear player indices
|
||
|
ctl_iCurrentPlayerLocal = -1;
|
||
|
ctl_iCurrentPlayer = -1;
|
||
|
}
|
||
|
// execute all button-action shell commands for common controls
|
||
|
if (gm_bGameOn) {
|
||
|
_ctrlCommonControls.DoButtonActions();
|
||
|
}
|
||
|
}
|
||
|
// if DirectInput is disabled, and game is currently active
|
||
|
else if (gm_bGameOn)
|
||
|
{
|
||
|
// for all possible local players
|
||
|
for( INDEX iPlayer=0; iPlayer<4; iPlayer++)
|
||
|
{ // if this player exist
|
||
|
if( gm_lpLocalPlayers[iPlayer].lp_pplsPlayerSource != NULL)
|
||
|
{
|
||
|
CPlayerSource &pls = *gm_lpLocalPlayers[iPlayer].lp_pplsPlayerSource;
|
||
|
// create dummy action for the player for this tick
|
||
|
CPlayerAction paClearAction;
|
||
|
// clear actions
|
||
|
paClearAction = pls.pls_paAction;
|
||
|
paClearAction.pa_vTranslation = FLOAT3D(0.0f,0.0f,0.0f);
|
||
|
// paClearAction.pa_aRotation = ANGLE3D(0,0,0);
|
||
|
// paClearAction.pa_aViewRotation = ANGLE3D(0,0,0);
|
||
|
paClearAction.pa_ulButtons = 0;
|
||
|
|
||
|
// clear the action in the client source object
|
||
|
pls.SetAction(paClearAction);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Global game object (in our case Flesh) initialization function
|
||
|
*/
|
||
|
void CGame::InitInternal( void)
|
||
|
{
|
||
|
gam_strCustomLevel = ""; // filename of custom level chosen
|
||
|
gam_strSessionName = TRANS("Unnamed session"); // name of multiplayer network session
|
||
|
gam_strJoinAddress = TRANS("serveraddress"); // join address
|
||
|
|
||
|
gm_MenuSplitScreenCfg = SSC_PLAY1;
|
||
|
gm_StartSplitScreenCfg = SSC_PLAY1;
|
||
|
gm_CurrentSplitScreenCfg = SSC_PLAY1;
|
||
|
gm_iLastSetHighScore = 0;
|
||
|
gm_iSinglePlayer = 0;
|
||
|
gm_iWEDSinglePlayer = 0;
|
||
|
gm_bGameOn = FALSE;
|
||
|
gm_bMenuOn = FALSE;
|
||
|
gm_bFirstLoading = FALSE;
|
||
|
gm_bProfileDemo = FALSE;
|
||
|
gm_slPlayerControlsSize = 0;
|
||
|
gm_pvGlobalPlayerControls = NULL;
|
||
|
memset(gm_aiMenuLocalPlayers, 0, sizeof(gm_aiMenuLocalPlayers));
|
||
|
memset(gm_aiStartLocalPlayers, 0, sizeof(gm_aiStartLocalPlayers));
|
||
|
|
||
|
// first assign translated to make dependcy catcher extract the translations
|
||
|
gm_astrAxisNames[AXIS_MOVE_UD] = TRANS("move u/d");
|
||
|
gm_astrAxisNames[AXIS_MOVE_LR] = TRANS("move l/r");
|
||
|
gm_astrAxisNames[AXIS_MOVE_FB] = TRANS("move f/b");
|
||
|
gm_astrAxisNames[AXIS_TURN_UD] = TRANS("look u/d");
|
||
|
gm_astrAxisNames[AXIS_TURN_LR] = TRANS("turn l/r");
|
||
|
gm_astrAxisNames[AXIS_TURN_BK] = TRANS("banking");
|
||
|
gm_astrAxisNames[AXIS_LOOK_UD] = TRANS("view u/d");
|
||
|
gm_astrAxisNames[AXIS_LOOK_LR] = TRANS("view l/r");
|
||
|
gm_astrAxisNames[AXIS_LOOK_BK] = TRANS("view banking");
|
||
|
// but we must not really use the translation for loading
|
||
|
gm_astrAxisNames[AXIS_MOVE_UD] = "move u/d"; //
|
||
|
gm_astrAxisNames[AXIS_MOVE_LR] = "move l/r"; //
|
||
|
gm_astrAxisNames[AXIS_MOVE_FB] = "move f/b"; //
|
||
|
gm_astrAxisNames[AXIS_TURN_UD] = "look u/d"; //
|
||
|
gm_astrAxisNames[AXIS_TURN_LR] = "turn l/r"; //
|
||
|
gm_astrAxisNames[AXIS_TURN_BK] = "banking"; //
|
||
|
gm_astrAxisNames[AXIS_LOOK_UD] = "view u/d"; //
|
||
|
gm_astrAxisNames[AXIS_LOOK_LR] = "view l/r"; //
|
||
|
gm_astrAxisNames[AXIS_LOOK_BK] = "view banking"; //
|
||
|
|
||
|
gm_csConsoleState = CS_OFF;
|
||
|
gm_csComputerState = CS_OFF;
|
||
|
|
||
|
gm_bGameOn = FALSE;
|
||
|
gm_bMenuOn = FALSE;
|
||
|
gm_bFirstLoading = FALSE;
|
||
|
gm_aiMenuLocalPlayers[0] = 0;
|
||
|
gm_aiMenuLocalPlayers[1] = -1;
|
||
|
gm_aiMenuLocalPlayers[2] = -1;
|
||
|
gm_aiMenuLocalPlayers[3] = -1;
|
||
|
|
||
|
gm_MenuSplitScreenCfg = SSC_PLAY1;
|
||
|
|
||
|
LoadPlayersAndControls();
|
||
|
|
||
|
gm_iWEDSinglePlayer = 0;
|
||
|
gm_iSinglePlayer = 0;
|
||
|
|
||
|
// add game timer handler
|
||
|
_pTimer->AddHandler(&m_gthGameTimerHandler);
|
||
|
// add shell variables
|
||
|
_pShell->DeclareSymbol("user void RecordProfile(void);", &RecordProfile);
|
||
|
_pShell->DeclareSymbol("user void SaveScreenShot(void);", &SaveScreenShot);
|
||
|
_pShell->DeclareSymbol("user void DumpProfileToConsole(void);", &DumpProfileToConsole);
|
||
|
_pShell->DeclareSymbol("user void DumpProfileToFile(void);", &DumpProfileToFile);
|
||
|
_pShell->DeclareSymbol("user INDEX hud_iStats;", &hud_iStats);
|
||
|
_pShell->DeclareSymbol("user INDEX hud_bShowResolution;", &hud_bShowResolution);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX hud_bShowTime;", &hud_bShowTime);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX hud_bShowClock;", &hud_bShowClock);
|
||
|
_pShell->DeclareSymbol("user INDEX dem_bOnScreenDisplay;", &dem_bOSD);
|
||
|
_pShell->DeclareSymbol("user INDEX dem_bPlay;", &dem_bPlay);
|
||
|
_pShell->DeclareSymbol("user INDEX dem_bPlayByName;", &dem_bPlayByName);
|
||
|
_pShell->DeclareSymbol("user INDEX dem_bProfile;", &dem_bProfile);
|
||
|
_pShell->DeclareSymbol("user INDEX dem_iAnimFrame;", &dem_iAnimFrame);
|
||
|
_pShell->DeclareSymbol("user CTString dem_strPostExec;", &dem_strPostExec);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX dem_iProfileRate;", &dem_iProfileRate);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX hud_bShowNetGraph;", &hud_bShowNetGraph);
|
||
|
_pShell->DeclareSymbol("FLOAT gam_afEnemyMovementSpeed[5];", &gam_afEnemyMovementSpeed);
|
||
|
_pShell->DeclareSymbol("FLOAT gam_afEnemyAttackSpeed[5];", &gam_afEnemyAttackSpeed);
|
||
|
_pShell->DeclareSymbol("FLOAT gam_afDamageStrength[5];", &gam_afDamageStrength);
|
||
|
_pShell->DeclareSymbol("FLOAT gam_afAmmoQuantity[5];", &gam_afAmmoQuantity);
|
||
|
_pShell->DeclareSymbol("persistent user FLOAT gam_fManaTransferFactor;", &gam_fManaTransferFactor);
|
||
|
_pShell->DeclareSymbol("persistent user FLOAT gam_fExtraEnemyStrength ;", &gam_fExtraEnemyStrength );
|
||
|
_pShell->DeclareSymbol("persistent user FLOAT gam_fExtraEnemyStrengthPerPlayer;", &gam_fExtraEnemyStrengthPerPlayer );
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_iInitialMana;", &gam_iInitialMana);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_iScoreLimit;", &gam_iScoreLimit);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_iFragLimit;", &gam_iFragLimit);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_iTimeLimit;", &gam_iTimeLimit);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_ctMaxPlayers;", &gam_ctMaxPlayers);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_bWaitAllPlayers;", &gam_bWaitAllPlayers);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_bFriendlyFire;", &gam_bFriendlyFire);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_bPlayEntireGame;", &gam_bPlayEntireGame);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_bWeaponsStay;", &gam_bWeaponsStay);
|
||
|
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_bAmmoStays ;", &gam_bAmmoStays );
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_bHealthArmorStays;", &gam_bHealthArmorStays);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_bAllowHealth ;", &gam_bAllowHealth );
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_bAllowArmor ;", &gam_bAllowArmor );
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_bInfiniteAmmo ;", &gam_bInfiniteAmmo );
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_bRespawnInPlace ;", &gam_bRespawnInPlace );
|
||
|
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_iCredits;", &gam_iCredits);
|
||
|
_pShell->DeclareSymbol("persistent user FLOAT gam_tmSpawnInvulnerability;", &gam_tmSpawnInvulnerability);
|
||
|
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_iBlood;", &gam_iBlood);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_bGibs;", &gam_bGibs);
|
||
|
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_bUseExtraEnemies;", &gam_bUseExtraEnemies);
|
||
|
|
||
|
_pShell->DeclareSymbol("user INDEX gam_bQuickLoad;", &gam_bQuickLoad);
|
||
|
_pShell->DeclareSymbol("user INDEX gam_bQuickSave;", &gam_bQuickSave);
|
||
|
_pShell->DeclareSymbol("user INDEX gam_iQuickSaveSlots;", &gam_iQuickSaveSlots);
|
||
|
_pShell->DeclareSymbol("user INDEX gam_iQuickStartDifficulty;", &gam_iQuickStartDifficulty);
|
||
|
_pShell->DeclareSymbol("user INDEX gam_iQuickStartMode;", &gam_iQuickStartMode);
|
||
|
_pShell->DeclareSymbol("user INDEX gam_bQuickStartMP;", &gam_bQuickStartMP);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_iStartDifficulty;", &gam_iStartDifficulty);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_iStartMode;", &gam_iStartMode);
|
||
|
_pShell->DeclareSymbol("persistent user CTString gam_strGameAgentExtras;", &gam_strGameAgentExtras);
|
||
|
_pShell->DeclareSymbol("persistent user CTString gam_strCustomLevel;", &gam_strCustomLevel);
|
||
|
_pShell->DeclareSymbol("persistent user CTString gam_strSessionName;", &gam_strSessionName);
|
||
|
_pShell->DeclareSymbol("persistent user CTString gam_strJoinAddress;", &gam_strJoinAddress);
|
||
|
_pShell->DeclareSymbol("persistent user INDEX gam_bEnableAdvancedObserving;", &gam_bEnableAdvancedObserving);
|
||
|
_pShell->DeclareSymbol("user INDEX gam_iObserverConfig;", &gam_iObserverConfig);
|
||
|
_pShell->DeclareSymbol("user INDEX gam_iObserverOffset;", &gam_iObserverOffset);
|
||
|
|
||
|
_pShell->DeclareSymbol("INDEX gam_iRecordHighScore;", &gam_iRecordHighScore);
|
||
|
|
||
|
_pShell->DeclareSymbol("persistent user FLOAT con_fHeightFactor;", &con_fHeightFactor);
|
||
|
_pShell->DeclareSymbol("persistent user FLOAT con_tmLastLines;", &con_tmLastLines);
|
||
|
_pShell->DeclareSymbol("user INDEX con_bTalk;", &con_bTalk);
|
||
|
_pShell->DeclareSymbol("user void ReportDemoProfile(void);", &ReportDemoProfile);
|
||
|
_pShell->DeclareSymbol("user void DumpDemoProfile(void);", &DumpDemoProfile);
|
||
|
extern CTString GetGameAgentRulesInfo(void);
|
||
|
extern CTString GetGameTypeName(INDEX);
|
||
|
extern CTString GetGameTypeNameCfunc(void* pArgs);
|
||
|
extern CTString GetCurrentGameTypeName(void);
|
||
|
extern ULONG GetSpawnFlagsForGameType(INDEX);
|
||
|
extern ULONG GetSpawnFlagsForGameTypeCfunc(void* pArgs);
|
||
|
extern BOOL IsMenuEnabled(const CTString &);
|
||
|
extern BOOL IsMenuEnabledCfunc(void* pArgs);
|
||
|
_pShell->DeclareSymbol("user CTString GetGameAgentRulesInfo(void);", &GetGameAgentRulesInfo);
|
||
|
_pShell->DeclareSymbol("user CTString GetGameTypeName(INDEX);", &GetGameTypeNameCfunc);
|
||
|
_pShell->DeclareSymbol("user CTString GetCurrentGameTypeName(void);", &GetCurrentGameTypeName);
|
||
|
_pShell->DeclareSymbol("user INDEX GetSpawnFlagsForGameType(INDEX);", &GetSpawnFlagsForGameTypeCfunc);
|
||
|
_pShell->DeclareSymbol("user INDEX IsMenuEnabled(CTString);", &IsMenuEnabledCfunc);
|
||
|
_pShell->DeclareSymbol("user void Say(CTString);", &Say);
|
||
|
_pShell->DeclareSymbol("user void SayFromTo(INDEX, INDEX, CTString);", &SayFromTo);
|
||
|
|
||
|
_pShell->DeclareSymbol("CTString GetGameTypeNameSS(INDEX);", &GetGameTypeName);
|
||
|
_pShell->DeclareSymbol("INDEX GetSpawnFlagsForGameTypeSS(INDEX);", &GetSpawnFlagsForGameType);
|
||
|
_pShell->DeclareSymbol("INDEX IsMenuEnabledSS(CTString);", &IsMenuEnabled);
|
||
|
|
||
|
_pShell->DeclareSymbol("user const INDEX ctl_iCurrentPlayerLocal;", &ctl_iCurrentPlayerLocal);
|
||
|
_pShell->DeclareSymbol("user const INDEX ctl_iCurrentPlayer;", &ctl_iCurrentPlayer);
|
||
|
|
||
|
_pShell->DeclareSymbol("user FLOAT gam_fChatSoundVolume;", &gam_fChatSoundVolume);
|
||
|
|
||
|
_pShell->DeclareSymbol("user void PlaySound(INDEX, CTString, FLOAT, FLOAT, INDEX);", &PlayScriptSound);
|
||
|
_pShell->DeclareSymbol("user void StopSound(INDEX);", &StopScriptSound);
|
||
|
_pShell->DeclareSymbol("user INDEX IsSoundPlaying(INDEX);", &IsScriptSoundPlaying);
|
||
|
|
||
|
CAM_Init();
|
||
|
|
||
|
// load persistent symbols
|
||
|
if (!_bDedicatedServer) {
|
||
|
_pShell->Execute(CTString("include \"")+fnmPersistentSymbols+"\";");
|
||
|
}
|
||
|
// execute the startup script
|
||
|
_pShell->Execute(CTString("include \"")+fnmStartupScript+"\";");
|
||
|
|
||
|
// check the size and pointer of player control variables that are local to each player
|
||
|
if (ctl_slPlayerControlsSize<=0
|
||
|
||ctl_slPlayerControlsSize>sizeof(((CLocalPlayer*)NULL)->lp_ubPlayerControlsState)
|
||
|
||ctl_pvPlayerControls==NULL) {
|
||
|
FatalError(TRANS("Current player controls are invalid."));
|
||
|
}
|
||
|
|
||
|
// load common controls
|
||
|
try {
|
||
|
_ctrlCommonControls.Load_t(fnmCommonControls);
|
||
|
} catch (char * /*strError*/) {
|
||
|
//FatalError(TRANS("Cannot load common controls: %s\n"), strError);
|
||
|
}
|
||
|
|
||
|
// init LCD textures/fonts
|
||
|
LCDInit();
|
||
|
|
||
|
// load console history
|
||
|
CTString strConsole;
|
||
|
try {
|
||
|
strConsole.LoadKeepCRLF_t(fnmConsoleHistory);
|
||
|
gam_strConsoleInputBuffer = strConsole;
|
||
|
} catch (char *strError) {
|
||
|
(void)strError; // must ignore if there is no history file
|
||
|
}
|
||
|
|
||
|
// load game shell settings
|
||
|
try {
|
||
|
Load_t();
|
||
|
} catch (char *strError) {
|
||
|
CPrintF(TRANS("Cannot load game settings:\n%s\n Using defaults\n"), strError);
|
||
|
}
|
||
|
|
||
|
CON_DiscardLastLineTimes();
|
||
|
|
||
|
// provide URL to the engine
|
||
|
_strModURL = "http://www.croteam.com/mods/TheSecondEncounter";
|
||
|
}
|
||
|
|
||
|
// internal cleanup
|
||
|
void CGame::EndInternal(void)
|
||
|
{
|
||
|
// stop game if eventually started
|
||
|
StopGame();
|
||
|
// remove game timer handler
|
||
|
_pTimer->RemHandler( &m_gthGameTimerHandler);
|
||
|
// save persistent symbols
|
||
|
if (!_bDedicatedServer) {
|
||
|
_pShell->StorePersistentSymbols(fnmPersistentSymbols);
|
||
|
}
|
||
|
|
||
|
LCDEnd();
|
||
|
|
||
|
// stop and delete any playing sound
|
||
|
#define MAX_SCRIPTSOUNDS 16
|
||
|
for( INDEX i=0; i<MAX_SCRIPTSOUNDS; i++) {
|
||
|
if( _apsoScriptChannels[i]==NULL) continue;
|
||
|
_apsoScriptChannels[i]->Stop();
|
||
|
delete _apsoScriptChannels[i];
|
||
|
}
|
||
|
|
||
|
// save console history
|
||
|
CTString strConsole = gam_strConsoleInputBuffer;
|
||
|
strConsole.TrimLeft(8192);
|
||
|
try {
|
||
|
strConsole.SaveKeepCRLF_t(fnmConsoleHistory);
|
||
|
} catch (char *strError) {
|
||
|
WarningMessage(TRANS("Cannot save console history:\n%s"), strError);
|
||
|
}
|
||
|
SavePlayersAndControls();
|
||
|
|
||
|
// save game shell settings
|
||
|
try {
|
||
|
Save_t();
|
||
|
} catch (char *strError) {
|
||
|
WarningMessage("Cannot load game settings:\n%s\nUsing defaults!", strError);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL CGame::NewGame(const CTString &strSessionName, const CTFileName &fnWorld,
|
||
|
CSessionProperties &sp)
|
||
|
{
|
||
|
gam_iObserverConfig = 0;
|
||
|
gam_iObserverOffset = 0;
|
||
|
// stop eventually running game
|
||
|
StopGame();
|
||
|
|
||
|
CEnableUserBreak eub;
|
||
|
if (!gm_bFirstLoading) {
|
||
|
_bUserBreakEnabled = FALSE;
|
||
|
}
|
||
|
|
||
|
// try to start current network provider
|
||
|
if( !StartProviderFromName()) {
|
||
|
return FALSE;
|
||
|
gm_bFirstLoading = FALSE;
|
||
|
}
|
||
|
|
||
|
// clear profile array and start game
|
||
|
_atmFrameTimes.Clear();
|
||
|
_actTriangles.Clear();
|
||
|
gm_bProfileDemo = FALSE;
|
||
|
|
||
|
// start the new session
|
||
|
try {
|
||
|
if( dem_bPlay) {
|
||
|
gm_aiStartLocalPlayers[0] = -2;
|
||
|
|
||
|
CTFileName fnmDemo = CTString("Temp\\Play.dem");
|
||
|
if( dem_bPlayByName) {
|
||
|
fnmDemo = fnWorld;
|
||
|
}
|
||
|
CAM_Start(fnmDemo);
|
||
|
_pNetwork->StartDemoPlay_t(fnmDemo);
|
||
|
} else {
|
||
|
BOOL bWaitAllPlayers = sp.sp_bWaitAllPlayers && _pNetwork->IsNetworkEnabled();
|
||
|
_pNetwork->StartPeerToPeer_t( strSessionName, fnWorld,
|
||
|
sp.sp_ulSpawnFlags, sp.sp_ctMaxPlayers, bWaitAllPlayers, &sp);
|
||
|
}
|
||
|
} catch (char *strError) {
|
||
|
gm_bFirstLoading = FALSE;
|
||
|
// stop network provider
|
||
|
_pNetwork->StopProvider();
|
||
|
// and display error
|
||
|
CPrintF(TRANS("Cannot start game:\n%s\n"), strError);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// setup players from given indices
|
||
|
SetupLocalPlayers();
|
||
|
|
||
|
if( !dem_bPlay && !AddPlayers()) {
|
||
|
_pNetwork->StopGame();
|
||
|
_pNetwork->StopProvider();
|
||
|
gm_bFirstLoading = FALSE;
|
||
|
return FALSE;
|
||
|
}
|
||
|
gm_bFirstLoading = FALSE;
|
||
|
gm_bGameOn = TRUE;
|
||
|
gm_CurrentSplitScreenCfg = gm_StartSplitScreenCfg;
|
||
|
// clear last set highscore
|
||
|
gm_iLastSetHighScore = -1;
|
||
|
|
||
|
MaybeDiscardLastLines();
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL CGame::JoinGame(CNetworkSession &session)
|
||
|
{
|
||
|
CEnableUserBreak eub;
|
||
|
gam_iObserverConfig = 0;
|
||
|
gam_iObserverOffset = 0;
|
||
|
|
||
|
// stop eventually running game
|
||
|
StopGame();
|
||
|
|
||
|
// try to start current network provider
|
||
|
if( !StartProviderFromName()) return FALSE;
|
||
|
|
||
|
// start the new session
|
||
|
try {
|
||
|
INDEX ctLocalPlayers = 0;
|
||
|
if (gm_StartSplitScreenCfg>=SSC_PLAY1 && gm_StartSplitScreenCfg<=SSC_PLAY4) {
|
||
|
ctLocalPlayers = (gm_StartSplitScreenCfg-SSC_PLAY1)+1;
|
||
|
}
|
||
|
_pNetwork->JoinSession_t(session, ctLocalPlayers);
|
||
|
} catch (char *strError) {
|
||
|
// stop network provider
|
||
|
_pNetwork->StopProvider();
|
||
|
// and display error
|
||
|
CPrintF(TRANS("Cannot join game:\n%s\n"), strError);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// setup players from given indices
|
||
|
SetupLocalPlayers();
|
||
|
|
||
|
if( !AddPlayers())
|
||
|
{
|
||
|
_pNetwork->StopGame();
|
||
|
_pNetwork->StopProvider();
|
||
|
return FALSE;
|
||
|
}
|
||
|
gm_bGameOn = TRUE;
|
||
|
gm_CurrentSplitScreenCfg = gm_StartSplitScreenCfg;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL CGame::LoadGame(const CTFileName &fnGame)
|
||
|
{
|
||
|
gam_iObserverConfig = 0;
|
||
|
gam_iObserverOffset = 0;
|
||
|
|
||
|
// stop eventually running game
|
||
|
StopGame();
|
||
|
|
||
|
// try to start current network provider
|
||
|
if( !StartProviderFromName()) return FALSE;
|
||
|
|
||
|
// start the new session
|
||
|
try {
|
||
|
_pNetwork->Load_t( fnGame);
|
||
|
CPrintF(TRANS("Loaded game: %s\n"), fnGame);
|
||
|
} catch (char *strError) {
|
||
|
// stop network provider
|
||
|
_pNetwork->StopProvider();
|
||
|
// and display error
|
||
|
CPrintF(TRANS("Cannot load game: %s\n"), strError);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// setup players from given indices
|
||
|
SetupLocalPlayers();
|
||
|
|
||
|
if( !AddPlayers())
|
||
|
{
|
||
|
_pNetwork->StopGame();
|
||
|
_pNetwork->StopProvider();
|
||
|
return FALSE;
|
||
|
}
|
||
|
gm_bGameOn = TRUE;
|
||
|
gm_CurrentSplitScreenCfg = gm_StartSplitScreenCfg;
|
||
|
// clear last set highscore
|
||
|
gm_iLastSetHighScore = -1;
|
||
|
|
||
|
// if it was a quicksave, and not the newest one
|
||
|
if (fnGame.Matches("*\\QuickSave*") && fnGame!=GetQuickSaveName(FALSE)) {
|
||
|
// mark that it should be resaved as newest
|
||
|
gam_bQuickSave = TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
MaybeDiscardLastLines();
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL CGame::StartDemoPlay(const CTFileName &fnDemo)
|
||
|
{
|
||
|
CEnableUserBreak eub;
|
||
|
|
||
|
// stop eventually running game
|
||
|
StopGame();
|
||
|
|
||
|
// try to start current network provider
|
||
|
if( !StartProviderFromName()) {
|
||
|
gm_bFirstLoading = FALSE;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// start the new session
|
||
|
try {
|
||
|
_pNetwork->StartDemoPlay_t( fnDemo);
|
||
|
CPrintF(TRANS("Started playing demo: %s\n"), fnDemo);
|
||
|
} catch (char *strError) {
|
||
|
// stop network provider
|
||
|
_pNetwork->StopProvider();
|
||
|
// and display error
|
||
|
CPrintF(TRANS("Cannot play demo: %s\n"), strError);
|
||
|
gm_bFirstLoading = FALSE;
|
||
|
return FALSE;
|
||
|
}
|
||
|
gm_bFirstLoading = FALSE;
|
||
|
|
||
|
// setup players from given indices
|
||
|
gm_StartSplitScreenCfg = CGame::SSC_OBSERVER;
|
||
|
SetupLocalPlayers();
|
||
|
gm_bGameOn = TRUE;
|
||
|
gm_CurrentSplitScreenCfg = gm_StartSplitScreenCfg;
|
||
|
|
||
|
// prepare array for eventual profiling
|
||
|
_atmFrameTimes.PopAll();
|
||
|
_actTriangles.PopAll();
|
||
|
gm_bProfileDemo = FALSE;
|
||
|
if( dem_bProfile) gm_bProfileDemo = TRUE;
|
||
|
_tvDemoStarted = _pTimer->GetHighPrecisionTimer();
|
||
|
_tvLastFrame = _tvDemoStarted;
|
||
|
|
||
|
CTFileName fnmScript = fnDemo.NoExt()+".ini";
|
||
|
if (!FileExists(fnmScript)) {
|
||
|
fnmScript = CTString("Demos\\Default.ini");
|
||
|
}
|
||
|
CTString strCmd;
|
||
|
strCmd.PrintF("include \"%s\"", (const char*)fnmScript);
|
||
|
_pShell->Execute(strCmd);
|
||
|
|
||
|
MaybeDiscardLastLines();
|
||
|
// all done
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL CGame::StartDemoRec(const CTFileName &fnDemo)
|
||
|
{
|
||
|
// save demo recording
|
||
|
try {
|
||
|
_pNetwork->StartDemoRec_t( fnDemo);
|
||
|
CPrintF(TRANS("Started recording demo: %s\n"), fnDemo);
|
||
|
// save a thumbnail
|
||
|
SaveThumbnail(fnDemo.NoExt()+"Tbn.tex");
|
||
|
return TRUE;
|
||
|
} catch (char *strError) {
|
||
|
// and display error
|
||
|
CPrintF(TRANS("Cannot start recording: %s\n"), strError);
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CGame::StopDemoRec(void)
|
||
|
{
|
||
|
// if no game is currently running
|
||
|
if (!gm_bGameOn) return;
|
||
|
|
||
|
_pNetwork->StopDemoRec();
|
||
|
CPrintF(TRANS("Finished recording.\n"));
|
||
|
}
|
||
|
|
||
|
BOOL CGame::SaveGame(const CTFileName &fnGame)
|
||
|
{
|
||
|
// if no live players
|
||
|
INDEX ctPlayers = GetPlayersCount();
|
||
|
INDEX ctLivePlayers = GetLivePlayersCount();
|
||
|
if (ctPlayers>0 && ctLivePlayers<=0) {
|
||
|
// display error
|
||
|
CPrintF(TRANS("Won't save game when dead!\n"));
|
||
|
// do not save
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// save new session
|
||
|
try {
|
||
|
_pNetwork->Save_t( fnGame);
|
||
|
CPrintF(TRANS("Saved game: %s\n"), fnGame);
|
||
|
SaveThumbnail(fnGame.NoExt()+"Tbn.tex");
|
||
|
return TRUE;
|
||
|
} catch (char *strError) {
|
||
|
// and display error
|
||
|
CPrintF(TRANS("Cannot save game: %s\n"), strError);
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CGame::StopGame(void)
|
||
|
{
|
||
|
// disable computer quickly
|
||
|
ComputerForceOff();
|
||
|
|
||
|
// if no game is currently running
|
||
|
if (!gm_bGameOn)
|
||
|
{
|
||
|
// do nothing
|
||
|
return;
|
||
|
}
|
||
|
// stop eventual camera
|
||
|
CAM_Stop();
|
||
|
// disable direct input
|
||
|
// _pInput->DisableInput();
|
||
|
// and game
|
||
|
gm_bGameOn = FALSE;
|
||
|
// stop the game
|
||
|
_pNetwork->StopGame();
|
||
|
// stop the provider
|
||
|
_pNetwork->StopProvider();
|
||
|
// for all four local players
|
||
|
for( INDEX iPlayer=0; iPlayer<4; iPlayer++)
|
||
|
{
|
||
|
// mark that current player does not exist any more
|
||
|
gm_lpLocalPlayers[ iPlayer].lp_bActive = FALSE;
|
||
|
gm_lpLocalPlayers[ iPlayer].lp_pplsPlayerSource = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL CGame::StartProviderFromName(void)
|
||
|
{
|
||
|
BOOL bSuccess = FALSE;
|
||
|
// list to contain available network providers
|
||
|
CListHead lhAvailableProviders;
|
||
|
// enumerate all providers
|
||
|
_pNetwork->EnumNetworkProviders( lhAvailableProviders);
|
||
|
|
||
|
// for each provider
|
||
|
FOREACHINLIST(CNetworkProvider, np_Node, lhAvailableProviders, litProviders)
|
||
|
{
|
||
|
// generate provider description
|
||
|
CTString strProviderName = litProviders->GetDescription();
|
||
|
// is this the provider we are searching for ?
|
||
|
if( strProviderName == gm_strNetworkProvider)
|
||
|
{
|
||
|
// it is, set it as active network provider
|
||
|
gm_npNetworkProvider = litProviders.Current();
|
||
|
bSuccess = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// delete list of providers
|
||
|
FORDELETELIST(CNetworkProvider, np_Node, lhAvailableProviders, litDelete)
|
||
|
{
|
||
|
delete &litDelete.Current();
|
||
|
}
|
||
|
|
||
|
// try to
|
||
|
try
|
||
|
{
|
||
|
// start selected network provider
|
||
|
_pNetwork->StartProvider_t( gm_npNetworkProvider);
|
||
|
}
|
||
|
// catch throwed error
|
||
|
catch (char *strError)
|
||
|
{
|
||
|
// if unable, report error
|
||
|
CPrintF( TRANS("Can't start provider:\n%s\n"), strError);
|
||
|
bSuccess = FALSE;
|
||
|
}
|
||
|
return bSuccess;
|
||
|
}
|
||
|
|
||
|
CHighScoreEntry::CHighScoreEntry(void)
|
||
|
{
|
||
|
hse_strPlayer = "";
|
||
|
hse_gdDifficulty = (CSessionProperties::GameDifficulty)-100;
|
||
|
hse_tmTime = -1.0f;
|
||
|
hse_ctKills = -1;
|
||
|
hse_ctScore = 0;
|
||
|
}
|
||
|
SLONG CGame::PackHighScoreTable(void)
|
||
|
{
|
||
|
// start at the beginning of buffer
|
||
|
UBYTE *pub = _aubHighScoreBuffer;
|
||
|
// for each entry
|
||
|
for (INDEX i=0; i<HIGHSCORE_COUNT; i++) {
|
||
|
// make its string
|
||
|
char str[MAX_HIGHSCORENAME+1];
|
||
|
memset(str, 0, sizeof(str));
|
||
|
strncpy(str, gm_ahseHighScores[i].hse_strPlayer, MAX_HIGHSCORENAME);
|
||
|
// copy the value and the string
|
||
|
memcpy(pub, str, sizeof(str));
|
||
|
pub += MAX_HIGHSCORENAME+1;
|
||
|
memcpy(pub, &gm_ahseHighScores[i].hse_gdDifficulty, sizeof(INDEX));
|
||
|
pub += sizeof(INDEX);
|
||
|
memcpy(pub, &gm_ahseHighScores[i].hse_tmTime, sizeof(FLOAT));
|
||
|
pub += sizeof(FLOAT);
|
||
|
memcpy(pub, &gm_ahseHighScores[i].hse_ctKills, sizeof(INDEX));
|
||
|
pub += sizeof(INDEX);
|
||
|
memcpy(pub, &gm_ahseHighScores[i].hse_ctScore, sizeof(INDEX));
|
||
|
pub += sizeof(INDEX);
|
||
|
}
|
||
|
// just copy it for now
|
||
|
memcpy(_aubHighScorePacked, _aubHighScoreBuffer, MAX_HIGHSCORETABLESIZE);
|
||
|
return MAX_HIGHSCORETABLESIZE;
|
||
|
}
|
||
|
|
||
|
void CGame::UnpackHighScoreTable(SLONG slSize)
|
||
|
{
|
||
|
// just copy it for now
|
||
|
memcpy(_aubHighScoreBuffer, _aubHighScorePacked, slSize);
|
||
|
// start at the beginning of buffer
|
||
|
UBYTE *pub = _aubHighScoreBuffer;
|
||
|
// for each entry
|
||
|
for (INDEX i=0; i<HIGHSCORE_COUNT; i++) {
|
||
|
gm_ahseHighScores[i].hse_strPlayer = (const char*)pub;
|
||
|
pub += MAX_HIGHSCORENAME+1;
|
||
|
memcpy(&gm_ahseHighScores[i].hse_gdDifficulty, pub, sizeof(INDEX));
|
||
|
pub += sizeof(INDEX);
|
||
|
memcpy(&gm_ahseHighScores[i].hse_tmTime , pub, sizeof(FLOAT));
|
||
|
pub += sizeof(FLOAT);
|
||
|
memcpy(&gm_ahseHighScores[i].hse_ctKills , pub, sizeof(INDEX));
|
||
|
pub += sizeof(INDEX);
|
||
|
memcpy(&gm_ahseHighScores[i].hse_ctScore , pub, sizeof(INDEX));
|
||
|
pub += sizeof(INDEX);
|
||
|
}
|
||
|
|
||
|
// try to
|
||
|
try {
|
||
|
CTFileStream strm;
|
||
|
strm.Open_t(CTString("table.txt"));
|
||
|
{for (INDEX i=0; i<HIGHSCORE_COUNT; i++) {
|
||
|
gm_ahseHighScores[i].hse_gdDifficulty = (CSessionProperties::GameDifficulty)-100;
|
||
|
}}
|
||
|
{for (INDEX i=0; i<HIGHSCORE_COUNT; i++) {
|
||
|
CTString strLine;
|
||
|
strm.GetLine_t(strLine);
|
||
|
char strName[256];
|
||
|
strLine.ScanF("\"%256[^\"]\" %d %g %d %d",
|
||
|
strName,
|
||
|
&gm_ahseHighScores[i].hse_gdDifficulty,
|
||
|
&gm_ahseHighScores[i].hse_tmTime,
|
||
|
&gm_ahseHighScores[i].hse_ctKills,
|
||
|
&gm_ahseHighScores[i].hse_ctScore);
|
||
|
gm_ahseHighScores[i].hse_strPlayer = strName;
|
||
|
}}
|
||
|
} catch (char *strError) {
|
||
|
(void)strError;
|
||
|
}
|
||
|
|
||
|
// remember best for player hud and statistics
|
||
|
plr_iHiScore = gm_ahseHighScores[0].hse_ctScore;
|
||
|
|
||
|
// no last set
|
||
|
gm_iLastSetHighScore = -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Loads CGame from file with file name given trough SetGameSettingsSaveFileName() function
|
||
|
*/
|
||
|
void CGame::Load_t( void)
|
||
|
{
|
||
|
ASSERT( gm_fnSaveFileName != "");
|
||
|
CTFileStream strmFile;
|
||
|
// open file with saved CGameObject
|
||
|
strmFile.Open_t( gm_fnSaveFileName,CTStream::OM_READ);
|
||
|
// read file ID
|
||
|
strmFile.ExpectID_t( CChunkID( "GAME")); // game
|
||
|
// check version number
|
||
|
if( !( CChunkID(GAME_SHELL_VER) == strmFile.GetID_t()) )
|
||
|
{
|
||
|
throw( TRANS("Invalid version of game shell."));
|
||
|
}
|
||
|
// load all of the class members
|
||
|
strmFile>>gm_strNetworkProvider;
|
||
|
strmFile>>gm_iWEDSinglePlayer;
|
||
|
strmFile>>gm_iSinglePlayer;
|
||
|
strmFile>>gm_aiMenuLocalPlayers[0];
|
||
|
strmFile>>gm_aiMenuLocalPlayers[1];
|
||
|
strmFile>>gm_aiMenuLocalPlayers[2];
|
||
|
strmFile>>gm_aiMenuLocalPlayers[3];
|
||
|
|
||
|
strmFile.Read_t( &gm_MenuSplitScreenCfg, sizeof( enum SplitScreenCfg));
|
||
|
|
||
|
// read high-score table
|
||
|
SLONG slHSSize;
|
||
|
strmFile>>slHSSize;
|
||
|
strmFile.Read_t(&_aubHighScorePacked, slHSSize);
|
||
|
UnpackHighScoreTable(slHSSize);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Saves current state of CGame under file name given trough SetGameSettingsSaveFileName() function
|
||
|
*/
|
||
|
void CGame::Save_t( void)
|
||
|
{
|
||
|
ASSERT( gm_fnSaveFileName != "");
|
||
|
CTFileStream strmFile;
|
||
|
// create file to save CGameObject
|
||
|
strmFile.Create_t( gm_fnSaveFileName);
|
||
|
// write file ID
|
||
|
strmFile.WriteID_t( CChunkID( "GAME")); // game shell
|
||
|
// write version number
|
||
|
strmFile.WriteID_t( CChunkID(GAME_SHELL_VER));
|
||
|
|
||
|
// save all of the class members
|
||
|
strmFile<<gm_strNetworkProvider;
|
||
|
strmFile<<gm_iWEDSinglePlayer;
|
||
|
strmFile<<gm_iSinglePlayer;
|
||
|
strmFile<<gm_aiMenuLocalPlayers[0];
|
||
|
strmFile<<gm_aiMenuLocalPlayers[1];
|
||
|
strmFile<<gm_aiMenuLocalPlayers[2];
|
||
|
strmFile<<gm_aiMenuLocalPlayers[3];
|
||
|
|
||
|
strmFile.Write_t( &gm_MenuSplitScreenCfg, sizeof( enum SplitScreenCfg));
|
||
|
|
||
|
// write high-score table
|
||
|
SLONG slHSSize = PackHighScoreTable();
|
||
|
strmFile<<slHSSize;
|
||
|
strmFile.Write_t(&_aubHighScorePacked, slHSSize);
|
||
|
}
|
||
|
|
||
|
|
||
|
void LoadControls(CControls &ctrl, INDEX i)
|
||
|
{
|
||
|
try {
|
||
|
CTFileName fnm;
|
||
|
fnm.PrintF("Controls\\Controls%d.ctl", i);
|
||
|
ctrl.Load_t(fnm);
|
||
|
} catch (char *strError) {
|
||
|
(void) strError;
|
||
|
try {
|
||
|
ctrl.Load_t(CTFILENAME("Controls\\00-Default.ctl"));
|
||
|
} catch (char *strError) {
|
||
|
(void) strError;
|
||
|
ctrl.SwitchToDefaults();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void LoadPlayer(CPlayerCharacter &pc, INDEX i)
|
||
|
{
|
||
|
try {
|
||
|
CTFileName fnm;
|
||
|
fnm.PrintF("Players\\Player%d.plr", i);
|
||
|
pc.Load_t(fnm);
|
||
|
} catch (char *strError) {
|
||
|
(void) strError;
|
||
|
CTString strName;
|
||
|
if (i==0) {
|
||
|
LoadStringVar(CTString("Data\\Var\\DefaultPlayer.var"), strName);
|
||
|
strName.OnlyFirstLine();
|
||
|
}
|
||
|
if (strName=="") {
|
||
|
strName.PrintF("Player %d", i);
|
||
|
}
|
||
|
pc = CPlayerCharacter(strName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Loads 8 players and 8 controls
|
||
|
*/
|
||
|
void CGame::LoadPlayersAndControls( void)
|
||
|
{
|
||
|
for (INDEX iControls=0; iControls<8; iControls++) {
|
||
|
LoadControls(gm_actrlControls[iControls], iControls);
|
||
|
}
|
||
|
|
||
|
for (INDEX iPlayer=0; iPlayer<8; iPlayer++) {
|
||
|
LoadPlayer(gm_apcPlayers[iPlayer], iPlayer);
|
||
|
}
|
||
|
|
||
|
SavePlayersAndControls();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Saves 8 players and 8 controls
|
||
|
*/
|
||
|
void CGame::SavePlayersAndControls( void)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
// save players
|
||
|
gm_apcPlayers[0].Save_t( CTString( "Players\\Player0.plr"));
|
||
|
gm_apcPlayers[1].Save_t( CTString( "Players\\Player1.plr"));
|
||
|
gm_apcPlayers[2].Save_t( CTString( "Players\\Player2.plr"));
|
||
|
gm_apcPlayers[3].Save_t( CTString( "Players\\Player3.plr"));
|
||
|
gm_apcPlayers[4].Save_t( CTString( "Players\\Player4.plr"));
|
||
|
gm_apcPlayers[5].Save_t( CTString( "Players\\Player5.plr"));
|
||
|
gm_apcPlayers[6].Save_t( CTString( "Players\\Player6.plr"));
|
||
|
gm_apcPlayers[7].Save_t( CTString( "Players\\Player7.plr"));
|
||
|
// save controls
|
||
|
gm_actrlControls[0].Save_t( CTString( "Controls\\Controls0.ctl"));
|
||
|
gm_actrlControls[1].Save_t( CTString( "Controls\\Controls1.ctl"));
|
||
|
gm_actrlControls[2].Save_t( CTString( "Controls\\Controls2.ctl"));
|
||
|
gm_actrlControls[3].Save_t( CTString( "Controls\\Controls3.ctl"));
|
||
|
gm_actrlControls[4].Save_t( CTString( "Controls\\Controls4.ctl"));
|
||
|
gm_actrlControls[5].Save_t( CTString( "Controls\\Controls5.ctl"));
|
||
|
gm_actrlControls[6].Save_t( CTString( "Controls\\Controls6.ctl"));
|
||
|
gm_actrlControls[7].Save_t( CTString( "Controls\\Controls7.ctl"));
|
||
|
}
|
||
|
// catch throwed error
|
||
|
catch (char *strError)
|
||
|
{
|
||
|
(void) strError;
|
||
|
}
|
||
|
|
||
|
// skip checking of players if game isn't on
|
||
|
if( !gm_bGameOn) return;
|
||
|
|
||
|
// for each local player
|
||
|
for( INDEX i=0; i<4; i++) {
|
||
|
CLocalPlayer &lp = gm_lpLocalPlayers[i];
|
||
|
// if active
|
||
|
if( lp.lp_bActive && lp.lp_pplsPlayerSource!=NULL && lp.lp_iPlayer>=0 && lp.lp_iPlayer<8) {
|
||
|
// if its character in game is different than the one in config
|
||
|
CPlayerCharacter &pcInGame = lp.lp_pplsPlayerSource->pls_pcCharacter;
|
||
|
CPlayerCharacter &pcConfig = gm_apcPlayers[lp.lp_iPlayer];
|
||
|
if( pcConfig.pc_strName!=pcInGame.pc_strName
|
||
|
|| pcConfig.pc_strTeam!=pcInGame.pc_strTeam
|
||
|
|| memcmp(pcConfig.pc_aubAppearance, pcInGame.pc_aubAppearance, sizeof(pcInGame.pc_aubAppearance))!=0 ) {
|
||
|
// demand change in game
|
||
|
lp.lp_pplsPlayerSource->ChangeCharacter(pcConfig);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CGame::SetupLocalPlayers( void)
|
||
|
{
|
||
|
// setup local players and their controls
|
||
|
gm_lpLocalPlayers[0].lp_iPlayer = gm_aiStartLocalPlayers[0];
|
||
|
gm_lpLocalPlayers[1].lp_iPlayer = gm_aiStartLocalPlayers[1];
|
||
|
gm_lpLocalPlayers[2].lp_iPlayer = gm_aiStartLocalPlayers[2];
|
||
|
gm_lpLocalPlayers[3].lp_iPlayer = gm_aiStartLocalPlayers[3];
|
||
|
if (gm_StartSplitScreenCfg < CGame::SSC_PLAY1) {
|
||
|
gm_lpLocalPlayers[0].lp_iPlayer = -1;
|
||
|
}
|
||
|
if (gm_StartSplitScreenCfg < CGame::SSC_PLAY2) {
|
||
|
gm_lpLocalPlayers[1].lp_iPlayer = -1;
|
||
|
}
|
||
|
if (gm_StartSplitScreenCfg < CGame::SSC_PLAY3) {
|
||
|
gm_lpLocalPlayers[2].lp_iPlayer = -1;
|
||
|
}
|
||
|
if (gm_StartSplitScreenCfg < CGame::SSC_PLAY4) {
|
||
|
gm_lpLocalPlayers[3].lp_iPlayer = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL CGame::AddPlayers(void)
|
||
|
{
|
||
|
// add local player(s) into game
|
||
|
try {
|
||
|
for(INDEX i=0; i<4; i++) {
|
||
|
CLocalPlayer &lp = gm_lpLocalPlayers[i];
|
||
|
INDEX iPlayer = lp.lp_iPlayer;
|
||
|
if (iPlayer>=0) {
|
||
|
ASSERT(iPlayer>=0 && iPlayer<8);
|
||
|
lp.lp_pplsPlayerSource = _pNetwork->AddPlayer_t(gm_apcPlayers[iPlayer]);
|
||
|
lp.lp_bActive = TRUE;
|
||
|
}
|
||
|
}
|
||
|
} catch (char *strError) {
|
||
|
CPrintF(TRANS("Cannot add player:\n%s\n"), strError);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// save thumbnail for savegame
|
||
|
static CTFileName _fnmThumb=CTString("");
|
||
|
void CGame::SaveThumbnail( const CTFileName &fnm)
|
||
|
{
|
||
|
// request saving of thumbnail only (need drawport)
|
||
|
// (saving will take place in GameRedrawView())
|
||
|
_fnmThumb = fnm;
|
||
|
}
|
||
|
|
||
|
|
||
|
// timer variables
|
||
|
#define FRAMES_AVERAGING_MAX 20L
|
||
|
static CTimerValue _tvLasts[FRAMES_AVERAGING_MAX];
|
||
|
static CTimerValue _tvDelta[FRAMES_AVERAGING_MAX];
|
||
|
static INDEX _iCheckNow = 0;
|
||
|
static INDEX _iCheckMax = 0;
|
||
|
|
||
|
// print resolution, frame rate or extensive profiling, and elapsed time
|
||
|
static void PrintStats( CDrawPort *pdpDrawPort)
|
||
|
{
|
||
|
// cache some general vars
|
||
|
SLONG slDPWidth = pdpDrawPort->GetWidth();
|
||
|
SLONG slDPHeight = pdpDrawPort->GetHeight();
|
||
|
|
||
|
// determine proper text scale for statistics display
|
||
|
FLOAT fTextScale = (FLOAT)slDPWidth/640.0f;
|
||
|
|
||
|
// display resolution info (if needed)
|
||
|
if( hud_bShowResolution) {
|
||
|
CTString strRes;
|
||
|
strRes.PrintF( "%dx%dx%s", slDPWidth, slDPHeight, _pGfx->gl_dmCurrentDisplayMode.DepthString());
|
||
|
pdpDrawPort->SetFont( _pfdDisplayFont);
|
||
|
pdpDrawPort->SetTextScaling( fTextScale);
|
||
|
pdpDrawPort->SetTextAspect( 1.0f);
|
||
|
pdpDrawPort->PutTextC( strRes, slDPWidth*0.5f, slDPHeight*0.15f, C_WHITE|255);
|
||
|
}
|
||
|
|
||
|
// if required, print elapsed playing time
|
||
|
if( hud_bShowTime) {
|
||
|
// set font, spacing and scale
|
||
|
pdpDrawPort->SetFont( _pfdDisplayFont);
|
||
|
pdpDrawPort->SetTextScaling( fTextScale);
|
||
|
pdpDrawPort->SetTextAspect( 1.0f);
|
||
|
// calculate elapsed time
|
||
|
CTimerValue tvNow = _pTimer->CurrentTick();
|
||
|
ULONG ulTime = (ULONG)tvNow.GetSeconds();
|
||
|
// printout elapsed time
|
||
|
CTString strTime;
|
||
|
if( ulTime >= (60*60)) {
|
||
|
// print hours
|
||
|
strTime.PrintF( "%02d:%02d:%02d", ulTime/(60*60), (ulTime/60)%60, ulTime%60);
|
||
|
} else {
|
||
|
// skip hours
|
||
|
strTime.PrintF( "%2d:%02d", ulTime/60, ulTime%60);
|
||
|
}
|
||
|
pdpDrawPort->PutTextC( strTime, slDPWidth*0.5f, slDPHeight*0.06f, C_WHITE|CT_OPAQUE);
|
||
|
}
|
||
|
|
||
|
// if required, print real time
|
||
|
if( hud_bShowClock) {
|
||
|
// set font, spacing and scale
|
||
|
pdpDrawPort->SetFont( _pfdConsoleFont);
|
||
|
pdpDrawPort->SetTextScaling(1.0f);
|
||
|
pdpDrawPort->SetTextAspect( 1.0f);
|
||
|
// determine time
|
||
|
struct tm *newtime;
|
||
|
time_t long_time;
|
||
|
time(&long_time);
|
||
|
newtime = localtime(&long_time);
|
||
|
// printout
|
||
|
CTString strTime;
|
||
|
strTime.PrintF( "%2d:%02d", newtime->tm_hour, newtime->tm_min);
|
||
|
pdpDrawPort->PutTextR( strTime, slDPWidth-3, 2, C_lYELLOW|CT_OPAQUE);
|
||
|
}
|
||
|
|
||
|
// if required, draw netgraph
|
||
|
if( hud_bShowNetGraph)
|
||
|
{ // determine and clamp size
|
||
|
INDEX ctLines = _pNetwork->ga_angeNetGraph.Count();
|
||
|
ctLines = ClampUp( ctLines, (INDEX)(slDPHeight*0.7f));
|
||
|
FLOAT f192oLines = 192.0f / (FLOAT)ctLines;
|
||
|
const FLOAT fMaxWidth = slDPWidth *0.1f;
|
||
|
const PIX pixJ = slDPHeight *0.9f;
|
||
|
// draw canvas
|
||
|
pdpDrawPort->Fill( slDPWidth-1-fMaxWidth, pixJ-ctLines+1, fMaxWidth, ctLines, SE_COL_BLUE_DARK_HV|64);
|
||
|
pdpDrawPort->DrawBorder( slDPWidth-2-fMaxWidth, pixJ-ctLines, fMaxWidth+2, ctLines+2, C_WHITE |192);
|
||
|
// draw graph
|
||
|
for( INDEX i=0; i<ctLines; i++) {
|
||
|
FLOAT fValue = _pNetwork->ga_angeNetGraph[i].nge_fLatency;
|
||
|
enum NetGraphEntryType nge = _pNetwork->ga_angeNetGraph[i].nge_ngetType;
|
||
|
FLOAT fWidth = Clamp( fValue, 0.0f, 1.0f)*fMaxWidth;
|
||
|
COLOR colLine = C_GREEN;
|
||
|
if( nge==NGET_ACTION) colLine = C_GREEN; // normal action (default)
|
||
|
else if( nge==NGET_MISSING) colLine = C_RED; // missing sequence
|
||
|
else if( nge==NGET_NONACTION) colLine = C_WHITE; // non-action sequence
|
||
|
else if( nge==NGET_REPLICATEDACTION) colLine = C_BLUE; // duplicated action
|
||
|
else if( nge==NGET_SKIPPEDACTION) colLine = C_YELLOW; // skipped action
|
||
|
else colLine = C_BLACK; // unknown ???
|
||
|
ULONG ulAlpha = FloatToInt( ((FLOAT)ctLines-(i*0.3333f)) *f192oLines);
|
||
|
pdpDrawPort->DrawLine( slDPWidth-2, pixJ-i, slDPWidth-2-fWidth, pixJ-i, colLine|ulAlpha);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if stats aren't required
|
||
|
hud_iStats = Clamp( hud_iStats, 0L, 2L);
|
||
|
if( hud_iStats==0 || (hud_iEnableStats==0 && hud_fEnableFPS==0)) {
|
||
|
// display nothing
|
||
|
_iCheckNow = 0;
|
||
|
_iCheckMax = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// calculate FPS
|
||
|
FLOAT fFPS = 0.0f;
|
||
|
_iCheckMax++;
|
||
|
if( _iCheckMax >= FRAMES_AVERAGING_MAX) {
|
||
|
for( INDEX i=0; i<FRAMES_AVERAGING_MAX; i++) fFPS += _tvDelta[i].GetSeconds();
|
||
|
fFPS = FRAMES_AVERAGING_MAX*FRAMES_AVERAGING_MAX / fFPS;
|
||
|
_iCheckMax = FRAMES_AVERAGING_MAX;
|
||
|
}
|
||
|
|
||
|
// determine newest time
|
||
|
CTimerValue tvNow = _pTimer->GetHighPrecisionTimer();
|
||
|
_tvDelta[_iCheckNow] = tvNow - _tvLasts[_iCheckNow];
|
||
|
_tvLasts[_iCheckNow] = tvNow;
|
||
|
_iCheckNow = (_iCheckNow+1) % FRAMES_AVERAGING_MAX;
|
||
|
|
||
|
// set display interface (proportional) font
|
||
|
pdpDrawPort->SetFont( _pfdDisplayFont);
|
||
|
pdpDrawPort->SetTextAspect( 1.0f);
|
||
|
pdpDrawPort->SetTextScaling( fTextScale);
|
||
|
|
||
|
// display colored FPS
|
||
|
COLOR colFPS = C_RED;
|
||
|
if( fFPS >= 20) colFPS = C_GREEN;
|
||
|
if( fFPS >= 60) colFPS = C_WHITE;
|
||
|
if( fFPS < 10) pdpDrawPort->SetTextScaling( fTextScale*1.5); // enlarge output if FPS is extremely low
|
||
|
|
||
|
// prepare FPS string for printing
|
||
|
CTString strFPS = "?";
|
||
|
if( fFPS >= 30) strFPS.PrintF( "%3.0f", fFPS);
|
||
|
else if( fFPS >= 0.1f) strFPS.PrintF( "%3.1f", fFPS);
|
||
|
// printout FPS number (if allowed)
|
||
|
if( hud_fEnableFPS) pdpDrawPort->PutTextC( strFPS, slDPWidth*0.75f, slDPHeight*0.005f, colFPS|192);
|
||
|
|
||
|
// if in extensive stats mode
|
||
|
if( hud_iStats==2 && hud_iEnableStats)
|
||
|
{ // display extensive statistics
|
||
|
CTString strReport;
|
||
|
STAT_Report(strReport);
|
||
|
STAT_Reset();
|
||
|
|
||
|
// adjust and set font
|
||
|
pdpDrawPort->SetFont( _pfdConsoleFont);
|
||
|
pdpDrawPort->SetTextScaling( 1.0f);
|
||
|
pdpDrawPort->SetTextLineSpacing( -1);
|
||
|
|
||
|
// put filter
|
||
|
pdpDrawPort->Fill( 0,0, 128,slDPHeight, C_BLACK|128, C_BLACK|0, C_BLACK|128, C_BLACK|0);
|
||
|
// printout statistics
|
||
|
strFPS.PrintF( " frame =%3.0f ms\n---------------\n", 1000.0f/fFPS);
|
||
|
pdpDrawPort->PutText( strFPS, 0, 40, C_WHITE|CT_OPAQUE);
|
||
|
pdpDrawPort->PutText( strReport, 4, 65, C_GREEN|CT_OPAQUE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// max possible drawports
|
||
|
CDrawPort adpDrawPorts[7];
|
||
|
// and ptrs to them
|
||
|
CDrawPort *apdpDrawPorts[7];
|
||
|
|
||
|
INDEX iFirstObserver = 0;
|
||
|
|
||
|
static void MakeSplitDrawports(enum CGame::SplitScreenCfg ssc, INDEX iCount, CDrawPort *pdp)
|
||
|
{
|
||
|
apdpDrawPorts[0] = NULL;
|
||
|
apdpDrawPorts[1] = NULL;
|
||
|
apdpDrawPorts[2] = NULL;
|
||
|
apdpDrawPorts[3] = NULL;
|
||
|
apdpDrawPorts[4] = NULL;
|
||
|
apdpDrawPorts[5] = NULL;
|
||
|
apdpDrawPorts[6] = NULL;
|
||
|
|
||
|
// if observer
|
||
|
if (ssc==CGame::SSC_OBSERVER) {
|
||
|
// must have at least one screen
|
||
|
iCount = Clamp(iCount, 1L, 4L);
|
||
|
// starting at first drawport
|
||
|
iFirstObserver = 0;
|
||
|
}
|
||
|
|
||
|
// if one player or observer with one screen
|
||
|
if (ssc==CGame::SSC_PLAY1 || ssc==CGame::SSC_OBSERVER && iCount==1) {
|
||
|
// the only drawport covers entire screen
|
||
|
adpDrawPorts[0] = CDrawPort( pdp, 0.0, 0.0, 1.0, 1.0);
|
||
|
apdpDrawPorts[0] = &adpDrawPorts[0];
|
||
|
// if two players or observer with two screens
|
||
|
} else if (ssc==CGame::SSC_PLAY2 || ssc==CGame::SSC_OBSERVER && iCount==2) {
|
||
|
// if the drawport is not dualhead
|
||
|
if (!pdp->IsDualHead()) {
|
||
|
// need two drawports for filling the empty spaces left and right
|
||
|
CDrawPort dpL( pdp, 0.0, 0.0, 0.2, 1.0f);
|
||
|
CDrawPort dpR( pdp, 0.8, 0.0, 0.2, 1.0f);
|
||
|
dpL.Lock(); dpL.Fill(C_BLACK|CT_OPAQUE); dpL.Unlock();
|
||
|
dpR.Lock(); dpR.Fill(C_BLACK|CT_OPAQUE); dpR.Unlock();
|
||
|
// first of two draw ports covers upper half of the screen
|
||
|
adpDrawPorts[0] = CDrawPort( pdp, 0.1666, 0.0, 0.6668, 0.5);
|
||
|
apdpDrawPorts[0] = &adpDrawPorts[0];
|
||
|
// second draw port covers lower half of the screen
|
||
|
adpDrawPorts[1] = CDrawPort( pdp, 0.1666, 0.5, 0.6668, 0.5);
|
||
|
apdpDrawPorts[1] = &adpDrawPorts[1];
|
||
|
// if the drawport is dualhead
|
||
|
} else {
|
||
|
// first of two draw ports covers left half of the screen
|
||
|
adpDrawPorts[0] = CDrawPort( pdp, 0.0, 0.0, 0.5, 1.0);
|
||
|
apdpDrawPorts[0] = &adpDrawPorts[0];
|
||
|
// second draw port covers right half of the screen
|
||
|
adpDrawPorts[1] = CDrawPort( pdp, 0.5, 0.0, 0.5, 1.0);
|
||
|
apdpDrawPorts[1] = &adpDrawPorts[1];
|
||
|
}
|
||
|
// if three players or observer with three screens
|
||
|
} else if (ssc==CGame::SSC_PLAY3 || ssc==CGame::SSC_OBSERVER && iCount==3) {
|
||
|
// if the drawport is not dualhead
|
||
|
if (!pdp->IsDualHead()) {
|
||
|
// need two drawports for filling the empty spaces left and right
|
||
|
CDrawPort dpL( pdp, 0.0, 0.0, 0.2, 0.5f);
|
||
|
CDrawPort dpR( pdp, 0.8, 0.0, 0.2, 0.5f);
|
||
|
dpL.Lock(); dpL.Fill(C_BLACK|CT_OPAQUE); dpL.Unlock();
|
||
|
dpR.Lock(); dpR.Fill(C_BLACK|CT_OPAQUE); dpR.Unlock();
|
||
|
// first of three draw ports covers center upper half of the screen
|
||
|
adpDrawPorts[0] = CDrawPort( pdp, 0.1666, 0.0, 0.6667, 0.5);
|
||
|
apdpDrawPorts[0] = &adpDrawPorts[0];
|
||
|
// second draw port covers lower-left part of the screen
|
||
|
adpDrawPorts[1] = CDrawPort( pdp, 0.0, 0.5, 0.5, 0.5);
|
||
|
apdpDrawPorts[1] = &adpDrawPorts[1];
|
||
|
// third draw port covers lower-right part of the screen
|
||
|
adpDrawPorts[2] = CDrawPort( pdp, 0.5, 0.5, 0.5, 0.5);
|
||
|
apdpDrawPorts[2] = &adpDrawPorts[2];
|
||
|
// if the drawport is dualhead
|
||
|
} else {
|
||
|
// first player uses entire left part
|
||
|
adpDrawPorts[0] = CDrawPort( pdp, 0.0, 0.0, 0.5, 1.0);
|
||
|
apdpDrawPorts[0] = &adpDrawPorts[0];
|
||
|
// make right DH part
|
||
|
CDrawPort dpDHR( pdp, 0.5, 0.0, 0.5, 1.0);
|
||
|
// need two drawports for filling the empty spaces left and right on the right DH part
|
||
|
CDrawPort dpL( &dpDHR, 0.0, 0.0, 0.2, 1.0f);
|
||
|
CDrawPort dpR( &dpDHR, 0.8, 0.0, 0.2, 1.0f);
|
||
|
dpL.Lock(); dpL.Fill(C_BLACK|CT_OPAQUE); dpL.Unlock();
|
||
|
dpR.Lock(); dpR.Fill(C_BLACK|CT_OPAQUE); dpR.Unlock();
|
||
|
// second draw port covers upper half of the right screen
|
||
|
adpDrawPorts[1] = CDrawPort( &dpDHR, 0.1666, 0.0, 0.6667, 0.5);
|
||
|
apdpDrawPorts[1] = &adpDrawPorts[1];
|
||
|
// third draw port covers lower half of the right screen
|
||
|
adpDrawPorts[2] = CDrawPort( &dpDHR, 0.1666, 0.5, 0.6667, 0.5);
|
||
|
apdpDrawPorts[2] = &adpDrawPorts[2];
|
||
|
}
|
||
|
// if four players or observer with four screens
|
||
|
} else if (ssc==CGame::SSC_PLAY4 || ssc==CGame::SSC_OBSERVER && iCount==4) {
|
||
|
// if the drawport is not dualhead
|
||
|
if (!pdp->IsDualHead()) {
|
||
|
// first of four draw ports covers upper-left part of the screen
|
||
|
adpDrawPorts[0] = CDrawPort( pdp, 0.0, 0.0, 0.5, 0.5);
|
||
|
apdpDrawPorts[0] = &adpDrawPorts[0];
|
||
|
// second draw port covers upper-right part of the screen
|
||
|
adpDrawPorts[1] = CDrawPort( pdp, 0.5, 0.0, 0.5, 0.5);
|
||
|
apdpDrawPorts[1] = &adpDrawPorts[1];
|
||
|
// third draw port covers lower-left part of the screen
|
||
|
adpDrawPorts[2] = CDrawPort( pdp, 0.0, 0.5, 0.5, 0.5);
|
||
|
apdpDrawPorts[2] = &adpDrawPorts[2];
|
||
|
// fourth draw port covers lower-right part of the screen
|
||
|
adpDrawPorts[3] = CDrawPort( pdp, 0.5, 0.5, 0.5, 0.5);
|
||
|
apdpDrawPorts[3] = &adpDrawPorts[3];
|
||
|
// if the drawport is dualhead
|
||
|
} else {
|
||
|
// make the DH parts
|
||
|
CDrawPort dpDHL( pdp, 0.0, 0.0, 0.5, 1.0);
|
||
|
CDrawPort dpDHR( pdp, 0.5, 0.0, 0.5, 1.0);
|
||
|
// on the left part
|
||
|
{
|
||
|
// need two drawports for filling the empty spaces left and right
|
||
|
CDrawPort dpL( &dpDHL, 0.0, 0.0, 0.2, 1.0f);
|
||
|
CDrawPort dpR( &dpDHL, 0.8, 0.0, 0.2, 1.0f);
|
||
|
dpL.Lock(); dpL.Fill(C_BLACK|CT_OPAQUE); dpL.Unlock();
|
||
|
dpR.Lock(); dpR.Fill(C_BLACK|CT_OPAQUE); dpR.Unlock();
|
||
|
// first of two draw ports covers upper half of the screen
|
||
|
adpDrawPorts[0] = CDrawPort( &dpDHL, 0.1666, 0.0, 0.6667, 0.5);
|
||
|
apdpDrawPorts[0] = &adpDrawPorts[0];
|
||
|
// second draw port covers lower half of the screen
|
||
|
adpDrawPorts[1] = CDrawPort( &dpDHL, 0.1666, 0.5, 0.6667, 0.5);
|
||
|
apdpDrawPorts[1] = &adpDrawPorts[1];
|
||
|
}
|
||
|
// on the right part
|
||
|
{
|
||
|
// need two drawports for filling the empty spaces left and right
|
||
|
CDrawPort dpL( &dpDHR, 0.0, 0.0, 0.2, 1.0f);
|
||
|
CDrawPort dpR( &dpDHR, 0.8, 0.0, 0.2, 1.0f);
|
||
|
dpL.Lock(); dpL.Fill(C_BLACK|CT_OPAQUE); dpL.Unlock();
|
||
|
dpR.Lock(); dpR.Fill(C_BLACK|CT_OPAQUE); dpR.Unlock();
|
||
|
// first of two draw ports covers upper half of the screen
|
||
|
adpDrawPorts[2] = CDrawPort( &dpDHR, 0.1666, 0.0, 0.6667, 0.5);
|
||
|
apdpDrawPorts[2] = &adpDrawPorts[2];
|
||
|
// second draw port covers lower half of the screen
|
||
|
adpDrawPorts[3] = CDrawPort( &dpDHR, 0.1666, 0.5, 0.6667, 0.5);
|
||
|
apdpDrawPorts[3] = &adpDrawPorts[3];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if observer
|
||
|
if (ssc==CGame::SSC_OBSERVER) {
|
||
|
// observing starts at first drawport
|
||
|
iFirstObserver = 0;
|
||
|
// if not observer
|
||
|
} else {
|
||
|
// observing starts after all players
|
||
|
iFirstObserver = INDEX(ssc)+1;
|
||
|
}
|
||
|
|
||
|
// if not observer and using more than one screen
|
||
|
if (ssc!=CGame::SSC_OBSERVER && iCount>=1) {
|
||
|
// create extra small screens
|
||
|
#define FREE (1/16.0)
|
||
|
#define FULL (4/16.0)
|
||
|
if (iCount==1) {
|
||
|
adpDrawPorts[iFirstObserver+0] = CDrawPort( pdp, 1.0-FREE-FULL, FREE, FULL, FULL);
|
||
|
apdpDrawPorts[iFirstObserver+0] = &adpDrawPorts[iFirstObserver+0];
|
||
|
} else if (iCount==2) {
|
||
|
adpDrawPorts[iFirstObserver+0] = CDrawPort( pdp, 1.0-FREE-FULL, FREE+0*(FULL+FREE), FULL, FULL);
|
||
|
apdpDrawPorts[iFirstObserver+0] = &adpDrawPorts[iFirstObserver+0];
|
||
|
adpDrawPorts[iFirstObserver+1] = CDrawPort( pdp, 1.0-FREE-FULL, FREE+1*(FULL+FREE), FULL, FULL);
|
||
|
apdpDrawPorts[iFirstObserver+1] = &adpDrawPorts[iFirstObserver+1];
|
||
|
} else if (iCount==3) {
|
||
|
adpDrawPorts[iFirstObserver+0] = CDrawPort( pdp, 1.0-FREE-FULL, FREE+0*(FULL+FREE), FULL, FULL);
|
||
|
apdpDrawPorts[iFirstObserver+0] = &adpDrawPorts[iFirstObserver+0];
|
||
|
adpDrawPorts[iFirstObserver+1] = CDrawPort( pdp, 1.0-FREE-FULL, FREE+1*(FULL+FREE), FULL, FULL);
|
||
|
apdpDrawPorts[iFirstObserver+1] = &adpDrawPorts[iFirstObserver+1];
|
||
|
adpDrawPorts[iFirstObserver+2] = CDrawPort( pdp, 1.0-FREE-FULL, FREE+2*(FULL+FREE), FULL, FULL);
|
||
|
apdpDrawPorts[iFirstObserver+2] = &adpDrawPorts[iFirstObserver+2];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// this is used to make sure that the thumbnail is never saved with an empty screen
|
||
|
static BOOL _bPlayerViewRendered = FALSE;
|
||
|
|
||
|
// redraw all game views (for case of split-screens and such)
|
||
|
void CGame::GameRedrawView( CDrawPort *pdpDrawPort, ULONG ulFlags)
|
||
|
{
|
||
|
// if thumbnail saving has been required
|
||
|
if( _fnmThumb!="")
|
||
|
{ // reset the need for saving thumbnail
|
||
|
CTFileName fnm = _fnmThumb;
|
||
|
_fnmThumb = CTString("");
|
||
|
// render one game view to a small cloned drawport
|
||
|
PIX pixSizeJ = pdpDrawPort->GetHeight();
|
||
|
PIXaabbox2D boxThumb( PIX2D(0,0), PIX2D(128,128));
|
||
|
CDrawPort dpThumb( pdpDrawPort, boxThumb);
|
||
|
_bPlayerViewRendered = FALSE;
|
||
|
GameRedrawView( &dpThumb, 0);
|
||
|
if (_bPlayerViewRendered) {
|
||
|
// grab screenshot
|
||
|
CImageInfo iiThumb;
|
||
|
CTextureData tdThumb;
|
||
|
dpThumb.GrabScreen( iiThumb);
|
||
|
// try to save thumbnail
|
||
|
try {
|
||
|
CTFileStream strmThumb;
|
||
|
tdThumb.Create_t( &iiThumb, 128, MAX_MEX_LOG2, FALSE);
|
||
|
strmThumb.Create_t(fnm);
|
||
|
tdThumb.Write_t( &strmThumb);
|
||
|
strmThumb.Close();
|
||
|
} catch( char *strError) {
|
||
|
// report an error to console, if failed
|
||
|
CPrintF( "%s\n", strError);
|
||
|
}
|
||
|
} else {
|
||
|
_fnmThumb = fnm;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( ulFlags) {
|
||
|
// pretouch memory if required (only if in game mode, not screengrabbing or profiling!)
|
||
|
SE_PretouchIfNeeded();
|
||
|
}
|
||
|
|
||
|
// if game is started and computer isn't on
|
||
|
BOOL bClientJoined = FALSE;
|
||
|
if( gm_bGameOn && (_pGame->gm_csComputerState==CS_OFF || pdpDrawPort->IsDualHead())
|
||
|
&& gm_CurrentSplitScreenCfg!=SSC_DEDICATED )
|
||
|
{
|
||
|
|
||
|
INDEX ctObservers = Clamp(gam_iObserverConfig, 0L, 4L);
|
||
|
INDEX iObserverOffset = ClampDn(gam_iObserverOffset, 0L);
|
||
|
if (gm_CurrentSplitScreenCfg==SSC_OBSERVER) {
|
||
|
ctObservers = ClampDn(ctObservers, 1L);
|
||
|
}
|
||
|
if (gm_CurrentSplitScreenCfg!=SSC_OBSERVER) {
|
||
|
if (!gam_bEnableAdvancedObserving || !GetSP()->sp_bCooperative) {
|
||
|
ctObservers = 0;
|
||
|
}
|
||
|
}
|
||
|
MakeSplitDrawports(gm_CurrentSplitScreenCfg, ctObservers, pdpDrawPort);
|
||
|
|
||
|
// get number of local players
|
||
|
INDEX ctLocals = 0;
|
||
|
{for (INDEX i=0; i<4; i++) {
|
||
|
if (gm_lpLocalPlayers[i].lp_pplsPlayerSource!=NULL) {
|
||
|
ctLocals++;
|
||
|
}
|
||
|
}}
|
||
|
|
||
|
CEntity *apenViewers[7];
|
||
|
apenViewers[0] = NULL;
|
||
|
apenViewers[1] = NULL;
|
||
|
apenViewers[2] = NULL;
|
||
|
apenViewers[3] = NULL;
|
||
|
apenViewers[4] = NULL;
|
||
|
apenViewers[5] = NULL;
|
||
|
apenViewers[6] = NULL;
|
||
|
INDEX ctViewers = 0;
|
||
|
|
||
|
// check if input is enabled
|
||
|
BOOL bDoPrescan = _pInput->IsInputEnabled() &&
|
||
|
!_pNetwork->IsPaused() && !_pNetwork->GetLocalPause() &&
|
||
|
_pShell->GetINDEX("inp_bAllowPrescan");
|
||
|
// prescan input
|
||
|
if (bDoPrescan) {
|
||
|
_pInput->GetInput(TRUE);
|
||
|
}
|
||
|
// timer must not occur during prescanning
|
||
|
{ CTSingleLock csTimer(&_pTimer->tm_csHooks, TRUE);
|
||
|
// for each local player
|
||
|
for( INDEX i=0; i<4; i++) {
|
||
|
// if local player
|
||
|
CPlayerSource *ppls = gm_lpLocalPlayers[i].lp_pplsPlayerSource;
|
||
|
if( ppls!=NULL) {
|
||
|
// get local player entity
|
||
|
apenViewers[ctViewers++] = _pNetwork->GetLocalPlayerEntity(ppls);
|
||
|
// precreate action for it for this tick
|
||
|
if (bDoPrescan) {
|
||
|
// copy its local controls to current controls
|
||
|
memcpy(
|
||
|
ctl_pvPlayerControls,
|
||
|
gm_lpLocalPlayers[i].lp_ubPlayerControlsState,
|
||
|
ctl_slPlayerControlsSize);
|
||
|
|
||
|
// do prescanning
|
||
|
CPlayerAction paPreAction;
|
||
|
INDEX iCurrentPlayer = gm_lpLocalPlayers[i].lp_iPlayer;
|
||
|
CControls &ctrls = gm_actrlControls[ iCurrentPlayer];
|
||
|
ctrls.CreateAction(gm_apcPlayers[iCurrentPlayer], paPreAction, TRUE);
|
||
|
|
||
|
// copy the local controls back
|
||
|
memcpy(
|
||
|
gm_lpLocalPlayers[i].lp_ubPlayerControlsState,
|
||
|
ctl_pvPlayerControls,
|
||
|
ctl_slPlayerControlsSize);
|
||
|
}
|
||
|
}
|
||
|
}}
|
||
|
|
||
|
// fill in all players that are not local
|
||
|
INDEX ctNonlocals = 0;
|
||
|
CEntity *apenNonlocals[16];
|
||
|
memset(apenNonlocals, 0, sizeof(apenNonlocals));
|
||
|
{for (INDEX i=0; i<16; i++) {
|
||
|
CEntity *pen = CEntity::GetPlayerEntity(i);
|
||
|
if (pen!=NULL && !_pNetwork->IsPlayerLocal(pen)) {
|
||
|
apenNonlocals[ctNonlocals++] = pen;
|
||
|
}
|
||
|
}}
|
||
|
|
||
|
// if there are any non-local players
|
||
|
if (ctNonlocals>0) {
|
||
|
// for each observer
|
||
|
{for (INDEX i=0; i<ctObservers; i++) {
|
||
|
// get the given player with given offset that is not local
|
||
|
INDEX iPlayer = (i+iObserverOffset)%ctNonlocals;
|
||
|
apenViewers[ctViewers++] = apenNonlocals[iPlayer];
|
||
|
}}
|
||
|
}
|
||
|
|
||
|
// for each view
|
||
|
BOOL bHadViewers = FALSE;
|
||
|
{for (INDEX i=0; i<ctViewers; i++) {
|
||
|
CDrawPort *pdp = apdpDrawPorts[i];
|
||
|
if (pdp!=NULL && pdp->Lock()) {
|
||
|
|
||
|
// if there is a viewer
|
||
|
if (apenViewers[i]!=NULL) {
|
||
|
bHadViewers = TRUE;
|
||
|
// if predicted
|
||
|
if (apenViewers[i]->IsPredicted()) {
|
||
|
// use predictor instead
|
||
|
apenViewers[i] = apenViewers[i]->GetPredictor();
|
||
|
}
|
||
|
|
||
|
if (!CAM_IsOn()) {
|
||
|
_bPlayerViewRendered = TRUE;
|
||
|
// render it
|
||
|
apenViewers[i]->RenderGameView(pdp, (void*)ulFlags);
|
||
|
} else {
|
||
|
CAM_Render(apenViewers[i], pdp);
|
||
|
}
|
||
|
} else {
|
||
|
pdp->Fill( C_BLACK|CT_OPAQUE);
|
||
|
}
|
||
|
pdp->Unlock();
|
||
|
}
|
||
|
}}
|
||
|
if (!bHadViewers) {
|
||
|
pdpDrawPort->Lock();
|
||
|
pdpDrawPort->Fill( C_BLACK|CT_OPAQUE);
|
||
|
pdpDrawPort->Unlock();
|
||
|
}
|
||
|
|
||
|
// create drawport for messages (left on DH)
|
||
|
CDrawPort dpMsg(pdpDrawPort, TRUE);
|
||
|
if ((ulFlags&GRV_SHOWEXTRAS) && dpMsg.Lock())
|
||
|
{
|
||
|
// print pause indicators
|
||
|
CTString strIndicator;
|
||
|
if (_pNetwork->IsDisconnected()) {
|
||
|
strIndicator.PrintF(TRANS("Disconnected: %s\nPress F9 to reconnect"), (const char *)_pNetwork->WhyDisconnected());
|
||
|
} else if (_pNetwork->IsWaitingForPlayers()) {
|
||
|
strIndicator = TRANS("Waiting for all players to connect");
|
||
|
} else if (_pNetwork->IsWaitingForServer()) {
|
||
|
strIndicator = TRANS("Waiting for server to continue");
|
||
|
} else if (!_pNetwork->IsConnectionStable()) {
|
||
|
strIndicator = TRANS("Trying to stabilize connection...");
|
||
|
} else if (_pNetwork->IsGameFinished()) {
|
||
|
strIndicator = TRANS("Game finished");
|
||
|
} else if (_pNetwork->IsPaused() || _pNetwork->GetLocalPause()) {
|
||
|
strIndicator = TRANS("Paused");
|
||
|
} else if (_tvMenuQuickSave.tv_llValue!=0I64 &&
|
||
|
(_pTimer->GetHighPrecisionTimer()-_tvMenuQuickSave).GetSeconds()<3) {
|
||
|
strIndicator = TRANS("Use F6 for QuickSave during game!");
|
||
|
} else if (_pNetwork->ga_sesSessionState.ses_strMOTD!="") {
|
||
|
CTString strMotd = _pNetwork->ga_sesSessionState.ses_strMOTD;
|
||
|
static CTString strLastMotd = "";
|
||
|
static CTimerValue tvLastMotd(0I64);
|
||
|
if (strLastMotd!=strMotd) {
|
||
|
tvLastMotd = _pTimer->GetHighPrecisionTimer();
|
||
|
strLastMotd = strMotd;
|
||
|
}
|
||
|
if (tvLastMotd.tv_llValue!=0I64 && (_pTimer->GetHighPrecisionTimer()-tvLastMotd).GetSeconds()<3) {
|
||
|
strIndicator = strMotd;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (strIndicator!="") {
|
||
|
// setup font
|
||
|
dpMsg.SetFont( _pfdDisplayFont);
|
||
|
dpMsg.SetTextAspect( 1.0f);
|
||
|
dpMsg.PutTextCXY( strIndicator,
|
||
|
dpMsg.GetWidth()*0.5f,
|
||
|
dpMsg.GetHeight()*0.4f, SE_COL_BLUEGREEN_LT|192);
|
||
|
}
|
||
|
// print recording indicator
|
||
|
if (_pNetwork->IsRecordingDemo()) {
|
||
|
// setup font
|
||
|
dpMsg.SetFont( _pfdDisplayFont);
|
||
|
dpMsg.SetTextScaling( 1.0f);
|
||
|
dpMsg.SetTextAspect( 1.0f);
|
||
|
dpMsg.PutText( TRANS("Recording"),
|
||
|
dpMsg.GetWidth()*0.1f,
|
||
|
dpMsg.GetHeight()*0.1f, C_CYAN|192);
|
||
|
}
|
||
|
|
||
|
// print some statistics
|
||
|
PrintStats( &dpMsg);
|
||
|
|
||
|
// print last few lines from console to top of screen
|
||
|
if (_pGame->gm_csConsoleState==CS_OFF) ConsolePrintLastLines( &dpMsg);
|
||
|
|
||
|
// print demo OSD
|
||
|
if( dem_bOSD) {
|
||
|
CTString strMessage;
|
||
|
// print the message
|
||
|
strMessage.PrintF("%.2fs", _pNetwork->ga_fDemoTimer);
|
||
|
dpMsg.SetFont( _pfdDisplayFont);
|
||
|
dpMsg.SetTextAspect( 1.0f);
|
||
|
dpMsg.PutText( strMessage, 20, 20);
|
||
|
}
|
||
|
dpMsg.Unlock();
|
||
|
}
|
||
|
|
||
|
// keep frames' time if required
|
||
|
if( gm_bProfileDemo)
|
||
|
{
|
||
|
CTimerValue tvThisFrame = _pTimer->GetHighPrecisionTimer();
|
||
|
// if demo has been finished
|
||
|
if( _pNetwork->IsDemoPlayFinished()) {
|
||
|
// end profile
|
||
|
gm_bProfileDemo = FALSE;
|
||
|
CPrintF( DemoReportAnalyzedProfile());
|
||
|
CPrintF( "-\n");
|
||
|
} else {
|
||
|
// determine frame time delta
|
||
|
TIME tmDelta = (tvThisFrame - _tvLastFrame).GetSeconds();
|
||
|
_tvLastFrame = tvThisFrame;
|
||
|
_atmFrameTimes.Push() = tmDelta; // add new frame time stamp
|
||
|
INDEX *piTriangles = _actTriangles.Push(4); // and polygons count
|
||
|
piTriangles[0] = _pGfx->gl_ctWorldTriangles;
|
||
|
piTriangles[1] = _pGfx->gl_ctModelTriangles;
|
||
|
piTriangles[2] = _pGfx->gl_ctParticleTriangles;
|
||
|
piTriangles[3] = _pGfx->gl_ctTotalTriangles;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// execute cvar after demoplay
|
||
|
if( _pNetwork->IsDemoPlayFinished() && dem_strPostExec!="") _pShell->Execute(dem_strPostExec);
|
||
|
}
|
||
|
|
||
|
// if no game is active
|
||
|
else
|
||
|
{
|
||
|
// clear background
|
||
|
if( pdpDrawPort->Lock()) {
|
||
|
pdpDrawPort->Fill( SE_COL_BLUE_DARK|CT_OPAQUE);
|
||
|
pdpDrawPort->FillZBuffer( ZBUF_BACK);
|
||
|
pdpDrawPort->Unlock();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check for new chat message
|
||
|
static INDEX ctChatMessages=0;
|
||
|
INDEX ctNewChatMessages = _pShell->GetINDEX("net_ctChatMessages");
|
||
|
if (ctNewChatMessages!=ctChatMessages) {
|
||
|
ctChatMessages=ctNewChatMessages;
|
||
|
PlayScriptSound(MAX_SCRIPTSOUNDS-1, CTFILENAME("Sounds\\Menu\\Chat.wav"), 4.0f*gam_fChatSoundVolume, 1.0f, FALSE);
|
||
|
}
|
||
|
|
||
|
// update sounds and forbid probing
|
||
|
_pSound->UpdateSounds();
|
||
|
_pGfx->gl_bAllowProbing = FALSE;
|
||
|
|
||
|
if( bSaveScreenShot || dem_iAnimFrame>=0)
|
||
|
{
|
||
|
// make the screen shot directory if it doesn't already exist
|
||
|
bSaveScreenShot = FALSE;
|
||
|
CTFileName fnmExpanded;
|
||
|
ExpandFilePath(EFP_WRITE, CTString("ScreenShots"), fnmExpanded);
|
||
|
_mkdir(fnmExpanded);
|
||
|
|
||
|
// create a name for screenshot
|
||
|
CTFileName fnmScreenShot;
|
||
|
if( dem_iAnimFrame<0) {
|
||
|
fnmScreenShot = MakeScreenShotName();
|
||
|
} else {
|
||
|
// create number for the file
|
||
|
CTString strNumber;
|
||
|
strNumber.PrintF("%05d", (INDEX)dem_iAnimFrame);
|
||
|
fnmScreenShot = CTString("ScreenShots\\Anim_")+strNumber+".tga";
|
||
|
dem_iAnimFrame+=1;
|
||
|
}
|
||
|
// grab screen creating image info
|
||
|
CImageInfo iiImageInfo;
|
||
|
if( pdpDrawPort->Lock()) {
|
||
|
pdpDrawPort->GrabScreen( iiImageInfo, 1);
|
||
|
pdpDrawPort->Unlock();
|
||
|
}
|
||
|
// try to
|
||
|
try {
|
||
|
// save screen shot as TGA
|
||
|
iiImageInfo.SaveTGA_t( fnmScreenShot);
|
||
|
if( dem_iAnimFrame<0) CPrintF( TRANS("screen shot: %s\n"), (CTString&)fnmScreenShot);
|
||
|
}
|
||
|
// if failed
|
||
|
catch (char *strError) {
|
||
|
// report error
|
||
|
CPrintF( TRANS("Cannot save screenshot:\n%s\n"), strError);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CGame::RecordHighScore(void)
|
||
|
{
|
||
|
// if game is not running
|
||
|
if (!gm_bGameOn) {
|
||
|
// do nothing
|
||
|
return;
|
||
|
}
|
||
|
// find that player
|
||
|
INDEX ipl= Clamp(int(gam_iRecordHighScore), 0, NET_MAXGAMEPLAYERS);
|
||
|
CPlayer *penpl = (CPlayer *)&*CEntity::GetPlayerEntity(ipl);
|
||
|
if (penpl==NULL) {
|
||
|
//CPrintF( TRANS("Warning: cannot record score for player %d!\n"), ipl);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// get its score
|
||
|
INDEX ctScore = penpl->m_psGameStats.ps_iScore;
|
||
|
|
||
|
// find entry with lower score
|
||
|
INDEX iLess=0;
|
||
|
for(; iLess<HIGHSCORE_COUNT; iLess++) {
|
||
|
if (gm_ahseHighScores[iLess].hse_ctScore<ctScore) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// if none
|
||
|
if (iLess>=HIGHSCORE_COUNT) {
|
||
|
// do nothing more
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// move all lower entries one place down, dropping the last one
|
||
|
for(INDEX i=HIGHSCORE_COUNT-1; i>iLess; i--) {
|
||
|
gm_ahseHighScores[i] = gm_ahseHighScores[i-1];
|
||
|
}
|
||
|
|
||
|
// remember new one
|
||
|
gm_ahseHighScores[iLess].hse_ctScore = ctScore;
|
||
|
gm_ahseHighScores[iLess].hse_strPlayer = penpl->GetPlayerName();
|
||
|
gm_ahseHighScores[iLess].hse_gdDifficulty = GetSP()->sp_gdGameDifficulty;
|
||
|
if (GetSP()->sp_bMental) {
|
||
|
(INDEX&)gm_ahseHighScores[iLess].hse_gdDifficulty = CSessionProperties::GD_EXTREME+1;
|
||
|
}
|
||
|
gm_ahseHighScores[iLess].hse_tmTime = _pTimer->CurrentTick();
|
||
|
gm_ahseHighScores[iLess].hse_ctKills = penpl->m_psGameStats.ps_iKills;
|
||
|
|
||
|
// remember best for player hud and statistics
|
||
|
plr_iHiScore = gm_ahseHighScores[0].hse_ctScore;
|
||
|
|
||
|
// remember last set
|
||
|
gm_iLastSetHighScore = iLess;
|
||
|
}
|
||
|
|
||
|
INDEX CGame::GetLivePlayersCount(void)
|
||
|
{
|
||
|
INDEX ctLive = 0;
|
||
|
|
||
|
for (INDEX ipl=0; ipl<NET_MAXGAMEPLAYERS; ipl++) {
|
||
|
CEntity *penpl = CEntity::GetPlayerEntity(ipl);
|
||
|
if (penpl!=NULL && (penpl->GetFlags()&ENF_ALIVE)) {
|
||
|
ctLive++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ctLive;
|
||
|
}
|
||
|
|
||
|
INDEX CGame::GetPlayersCount(void)
|
||
|
{
|
||
|
INDEX ctPlayers = 0;
|
||
|
|
||
|
for (INDEX ipl=0; ipl<NET_MAXGAMEPLAYERS; ipl++) {
|
||
|
CEntity *penpl = CEntity::GetPlayerEntity(ipl);
|
||
|
if (penpl!=NULL) {
|
||
|
ctPlayers++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ctPlayers;
|
||
|
}
|
||
|
|
||
|
// get default description for a game (for save games/demos)
|
||
|
CTString CGame::GetDefaultGameDescription(BOOL bWithInfo)
|
||
|
{
|
||
|
CTString strDescription;
|
||
|
|
||
|
struct tm *newtime;
|
||
|
time_t long_time;
|
||
|
time(&long_time);
|
||
|
newtime = localtime(&long_time);
|
||
|
|
||
|
setlocale(LC_ALL, "");
|
||
|
CTString strTimeline;
|
||
|
char achTimeLine[256];
|
||
|
strftime( achTimeLine, sizeof(achTimeLine)-1, "%a %x %H:%M", newtime);
|
||
|
strTimeline = achTimeLine;
|
||
|
setlocale(LC_ALL, "C");
|
||
|
|
||
|
strDescription.PrintF( "%s - %s", TranslateConst(_pNetwork->ga_World.GetName(), 0), strTimeline);
|
||
|
|
||
|
if (bWithInfo) {
|
||
|
CPlayer *penPlayer = (CPlayer *)&*CEntity::GetPlayerEntity(0);
|
||
|
CTString strStats;
|
||
|
if (penPlayer!=NULL) {
|
||
|
penPlayer->GetStats(strStats, CST_SHORT, 40);
|
||
|
}
|
||
|
strDescription += "\n"+strStats;
|
||
|
}
|
||
|
|
||
|
return strDescription;
|
||
|
}
|
||
|
|
||
|
struct QuickSave {
|
||
|
CListNode qs_lnNode;
|
||
|
CTFileName qs_fnm;
|
||
|
INDEX qs_iNumber;
|
||
|
};
|
||
|
|
||
|
int qsort_CompareQuickSaves_FileUp( const void *elem1, const void *elem2)
|
||
|
{
|
||
|
const QuickSave &qs1 = **(QuickSave **)elem1;
|
||
|
const QuickSave &qs2 = **(QuickSave **)elem2;
|
||
|
return strcmp(qs1.qs_fnm, qs2.qs_fnm);
|
||
|
}
|
||
|
|
||
|
// delete extra quicksaves and find the next free number
|
||
|
INDEX FixQuicksaveDir(const CTFileName &fnmDir, INDEX ctMax)
|
||
|
{
|
||
|
// list the directory
|
||
|
CDynamicStackArray<CTFileName> afnmDir;
|
||
|
MakeDirList(afnmDir, fnmDir, "*.sav", 0);
|
||
|
|
||
|
CListHead lh;
|
||
|
INDEX iMaxNo = -1;
|
||
|
|
||
|
// for each file in the directory
|
||
|
for (INDEX i=0; i<afnmDir.Count(); i++) {
|
||
|
CTFileName fnmName = afnmDir[i];
|
||
|
|
||
|
// parse it
|
||
|
INDEX iFile = -1;
|
||
|
fnmName.FileName().ScanF("QuickSave%d", &iFile);
|
||
|
// if it can be parsed
|
||
|
if (iFile>=0) {
|
||
|
// create new info for that file
|
||
|
QuickSave *pqs = new QuickSave;
|
||
|
pqs->qs_fnm = fnmName;
|
||
|
pqs->qs_iNumber = iFile;
|
||
|
if (iFile>iMaxNo) {
|
||
|
iMaxNo = iFile;
|
||
|
}
|
||
|
// add it to list
|
||
|
lh.AddTail(pqs->qs_lnNode);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// sort the list
|
||
|
lh.Sort(qsort_CompareQuickSaves_FileUp, offsetof(QuickSave, qs_lnNode));
|
||
|
INDEX ctCount = lh.Count();
|
||
|
|
||
|
// delete all old ones while number of files is too large
|
||
|
FORDELETELIST(QuickSave, qs_lnNode, lh, itqs) {
|
||
|
if (ctCount>ctMax) {
|
||
|
RemoveFile(itqs->qs_fnm);
|
||
|
RemoveFile(itqs->qs_fnm.NoExt()+"Tbn.tex");
|
||
|
RemoveFile(itqs->qs_fnm.NoExt()+".des");
|
||
|
ctCount--;
|
||
|
}
|
||
|
delete &*itqs;
|
||
|
}
|
||
|
|
||
|
return iMaxNo;
|
||
|
}
|
||
|
|
||
|
CTFileName CGame::GetQuickSaveName(BOOL bSave)
|
||
|
{
|
||
|
// find out base directory
|
||
|
CTFileName fnmDir;
|
||
|
if (GetSP()->sp_ctMaxPlayers==1) {
|
||
|
INDEX iPlayer = gm_iSinglePlayer;
|
||
|
if (GetSP()->sp_bQuickTest) {
|
||
|
iPlayer = gm_iWEDSinglePlayer;
|
||
|
}
|
||
|
fnmDir.PrintF("SaveGame\\Player%d\\Quick\\", iPlayer);
|
||
|
} else {
|
||
|
if (_pNetwork->IsNetworkEnabled()) {
|
||
|
fnmDir = CTString("SaveGame\\Network\\Quick\\");
|
||
|
} else {
|
||
|
fnmDir = CTString("SaveGame\\SplitScreen\\Quick\\");
|
||
|
}
|
||
|
}
|
||
|
// load last saved number
|
||
|
INDEX iLast = FixQuicksaveDir(fnmDir, bSave ? gam_iQuickSaveSlots-1 : gam_iQuickSaveSlots );
|
||
|
if (bSave) {
|
||
|
iLast++;
|
||
|
}
|
||
|
|
||
|
// add save name to that
|
||
|
CTFileName fnmName;
|
||
|
fnmName.PrintF("QuickSave%06d.sav", iLast);
|
||
|
return fnmDir+fnmName;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CGame::GameMainLoop(void)
|
||
|
{
|
||
|
if (gam_bQuickSave && GetSP()->sp_gmGameMode!=CSessionProperties::GM_FLYOVER) {
|
||
|
if (gam_bQuickSave==2) {
|
||
|
_tvMenuQuickSave = _pTimer->GetHighPrecisionTimer();
|
||
|
}
|
||
|
gam_bQuickSave = FALSE;
|
||
|
CTFileName fnm = GetQuickSaveName(TRUE);
|
||
|
CTString strDes = GetDefaultGameDescription(TRUE);
|
||
|
SaveGame(fnm);
|
||
|
SaveStringVar(fnm.NoExt()+".des", strDes);
|
||
|
}
|
||
|
// if quickload invoked
|
||
|
if (gam_bQuickLoad && GetSP()->sp_gmGameMode!=CSessionProperties::GM_FLYOVER) {
|
||
|
gam_bQuickLoad = FALSE;
|
||
|
// if no game active, or this computer is server
|
||
|
if (!gm_bGameOn || _pNetwork->IsServer()) {
|
||
|
// do a quickload
|
||
|
LoadGame(GetQuickSaveName(FALSE));
|
||
|
// otherwise
|
||
|
} else {
|
||
|
// rejoin current section
|
||
|
JoinGame(CNetworkSession(gam_strJoinAddress));
|
||
|
}
|
||
|
}
|
||
|
if (gam_iRecordHighScore>=0) {
|
||
|
RecordHighScore();
|
||
|
gam_iRecordHighScore = -1.0f;
|
||
|
}
|
||
|
// if server was restarted
|
||
|
if (gm_bGameOn && !_pNetwork->IsServer() && _pNetwork->IsGameFinished() && _pNetwork->IsDisconnected()) {
|
||
|
// automatically reconnect
|
||
|
JoinGame(CNetworkSession(gam_strJoinAddress));
|
||
|
}
|
||
|
|
||
|
if (_bStartProfilingNextTime) {
|
||
|
_bStartProfilingNextTime = FALSE;
|
||
|
_bProfiling = TRUE;
|
||
|
_ctProfileRecording = 50;
|
||
|
// reset the profiles
|
||
|
_pfRenderProfile.Reset();
|
||
|
_pfGfxProfile.Reset();
|
||
|
_pfModelProfile.Reset();
|
||
|
_pfSoundProfile.Reset();
|
||
|
_pfNetworkProfile.Reset();
|
||
|
_pfPhysicsProfile.Reset();
|
||
|
} else if (_bProfiling) {
|
||
|
_ctProfileRecording--;
|
||
|
if (_ctProfileRecording<=0) {
|
||
|
_bProfiling = FALSE;
|
||
|
_bDumpNextTime = TRUE;
|
||
|
_strProfile = "===========================================================\n";
|
||
|
/* Render profile */
|
||
|
CTString strRenderReport;
|
||
|
_pfRenderProfile.Report(strRenderReport);
|
||
|
_strProfile+=strRenderReport;
|
||
|
_pfRenderProfile.Reset();
|
||
|
|
||
|
/* Model profile */
|
||
|
CTString strModelReport;
|
||
|
_pfModelProfile.Report(strModelReport);
|
||
|
_strProfile+=strModelReport;
|
||
|
_pfModelProfile.Reset();
|
||
|
|
||
|
/* Gfx profile */
|
||
|
CTString strGfxReport;
|
||
|
_pfGfxProfile.Report(strGfxReport);
|
||
|
_strProfile+=strGfxReport;
|
||
|
_pfGfxProfile.Reset();
|
||
|
|
||
|
/* Sound profile */
|
||
|
CTString strSoundReport;
|
||
|
_pfSoundProfile.Report(strSoundReport);
|
||
|
_strProfile+=strSoundReport;
|
||
|
_pfSoundProfile.Reset();
|
||
|
|
||
|
/* Network profile */
|
||
|
CTString strNetworkReport;
|
||
|
_pfNetworkProfile.Report(strNetworkReport);
|
||
|
_strProfile+=strNetworkReport;
|
||
|
_pfNetworkProfile.Reset();
|
||
|
|
||
|
/* Physics profile */
|
||
|
CTString strPhysicsReport;
|
||
|
_pfPhysicsProfile.Report(strPhysicsReport);
|
||
|
_strProfile+=strPhysicsReport;
|
||
|
_pfPhysicsProfile.Reset();
|
||
|
|
||
|
CPrintF( TRANS("Profiling done.\n"));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (_bDumpNextTime) {
|
||
|
_bDumpNextTime = FALSE;
|
||
|
try {
|
||
|
// create a file for profile
|
||
|
CTFileStream strmProfile;
|
||
|
strmProfile.Create_t(CTString("Game.profile"));
|
||
|
strmProfile.Write_t(_strProfile, strlen(_strProfile));
|
||
|
} catch (char *strError) {
|
||
|
CPutString(strError);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if game is started
|
||
|
if (gm_bGameOn) {
|
||
|
// do main loop procesing
|
||
|
_pNetwork->MainLoop();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*************************************************************
|
||
|
* S E C O N D E N C O U N T E R M E N U *
|
||
|
*************************************************************/
|
||
|
|
||
|
static CTextureObject _toPointer;
|
||
|
static CTextureObject _toBcgClouds;
|
||
|
static CTextureObject _toBcgGrid;
|
||
|
static CTextureObject _toBackdrop;
|
||
|
static CTextureObject _toSamU;
|
||
|
static CTextureObject _toSamD;
|
||
|
static CTextureObject _toLeftU;
|
||
|
static CTextureObject _toLeftD;
|
||
|
|
||
|
static PIXaabbox2D _boxScreen_SE;
|
||
|
static PIX _pixSizeI_SE;
|
||
|
static PIX _pixSizeJ_SE;
|
||
|
CDrawPort *_pdp_SE = NULL;
|
||
|
static FLOAT _tmNow_SE;
|
||
|
static ULONG _ulA_SE;
|
||
|
static BOOL _bPopup;
|
||
|
|
||
|
void TiledTextureSE( PIXaabbox2D &_boxScreen, FLOAT fStretch, MEX2D &vScreen, MEXaabbox2D &boxTexture)
|
||
|
{
|
||
|
PIX pixW = _boxScreen.Size()(1);
|
||
|
PIX pixH = _boxScreen.Size()(2);
|
||
|
boxTexture = MEXaabbox2D(MEX2D(0, 0), MEX2D(pixW/fStretch, pixH/fStretch));
|
||
|
boxTexture+=vScreen;
|
||
|
}
|
||
|
|
||
|
////
|
||
|
|
||
|
void CGame::LCDInit(void)
|
||
|
{
|
||
|
try {
|
||
|
_toBcgClouds.SetData_t(CTFILENAME("Textures\\General\\Background6.tex"));
|
||
|
_toPointer.SetData_t(CTFILENAME("TexturesMP\\General\\Pointer.tex"));
|
||
|
_toBcgGrid.SetData_t(CTFILENAME("TexturesMP\\General\\grid.tex"));
|
||
|
_toBackdrop.SetData_t(CTFILENAME("TexturesMP\\General\\MenuBack.tex"));
|
||
|
_toSamU.SetData_t(CTFILENAME("TexturesMP\\General\\SamU.tex"));
|
||
|
_toSamD.SetData_t(CTFILENAME("TexturesMP\\General\\SamD.tex"));
|
||
|
_toLeftU.SetData_t(CTFILENAME("TexturesMP\\General\\LeftU.tex"));
|
||
|
_toLeftD.SetData_t(CTFILENAME("TexturesMP\\General\\LeftD.tex"));
|
||
|
// force constant textures
|
||
|
((CTextureData*)_toBcgClouds.GetData())->Force(TEX_CONSTANT);
|
||
|
((CTextureData*)_toPointer .GetData())->Force(TEX_CONSTANT);
|
||
|
((CTextureData*)_toBcgGrid .GetData())->Force(TEX_CONSTANT);
|
||
|
((CTextureData*)_toBackdrop .GetData())->Force(TEX_CONSTANT);
|
||
|
((CTextureData*)_toSamU .GetData())->Force(TEX_CONSTANT);
|
||
|
((CTextureData*)_toSamD .GetData())->Force(TEX_CONSTANT);
|
||
|
((CTextureData*)_toLeftU .GetData())->Force(TEX_CONSTANT);
|
||
|
((CTextureData*)_toLeftD .GetData())->Force(TEX_CONSTANT);
|
||
|
|
||
|
} catch (char *strError) {
|
||
|
FatalError("%s\n", strError);
|
||
|
}
|
||
|
::LCDInit();
|
||
|
}
|
||
|
void CGame::LCDEnd(void)
|
||
|
{
|
||
|
::LCDEnd();
|
||
|
}
|
||
|
void CGame::LCDPrepare(FLOAT fFade)
|
||
|
{
|
||
|
// get current time and alpha value
|
||
|
_tmNow_SE = (FLOAT)_pTimer->GetHighPrecisionTimer().GetSeconds();
|
||
|
_ulA_SE = NormFloatToByte(fFade);
|
||
|
|
||
|
::LCDPrepare(fFade);
|
||
|
}
|
||
|
void CGame::LCDSetDrawport(CDrawPort *pdp)
|
||
|
{
|
||
|
_pdp_SE = pdp;
|
||
|
_pixSizeI_SE = _pdp_SE->GetWidth();
|
||
|
_pixSizeJ_SE = _pdp_SE->GetHeight();
|
||
|
_boxScreen_SE = PIXaabbox2D ( PIX2D(0,0), PIX2D(_pixSizeI_SE, _pixSizeJ_SE));
|
||
|
|
||
|
if (pdp->dp_SizeIOverRasterSizeI==1.0f) {
|
||
|
_bPopup = FALSE;
|
||
|
} else {
|
||
|
_bPopup = TRUE;
|
||
|
}
|
||
|
|
||
|
::LCDSetDrawport(pdp);
|
||
|
}
|
||
|
void CGame::LCDDrawBox(PIX pixUL, PIX pixDR, PIXaabbox2D &box, COLOR col)
|
||
|
{
|
||
|
col = SE_COL_BLUE_NEUTRAL|255;
|
||
|
|
||
|
::LCDDrawBox(pixUL, pixDR, box, col);
|
||
|
}
|
||
|
void CGame::LCDScreenBox(COLOR col)
|
||
|
{
|
||
|
col = SE_COL_BLUE_NEUTRAL|255;
|
||
|
|
||
|
::LCDScreenBox(col);
|
||
|
}
|
||
|
void CGame::LCDScreenBoxOpenLeft(COLOR col)
|
||
|
{
|
||
|
col = SE_COL_BLUE_NEUTRAL|255;
|
||
|
|
||
|
::LCDScreenBoxOpenLeft(col);
|
||
|
}
|
||
|
void CGame::LCDScreenBoxOpenRight(COLOR col)
|
||
|
{
|
||
|
col = SE_COL_BLUE_NEUTRAL|255;
|
||
|
|
||
|
::LCDScreenBoxOpenRight(col);
|
||
|
}
|
||
|
void CGame::LCDRenderClouds1(void)
|
||
|
{
|
||
|
_pdp_SE->PutTexture(&_toBackdrop, _boxScreen_SE, C_WHITE|255);
|
||
|
|
||
|
if (!_bPopup) {
|
||
|
|
||
|
PIXaabbox2D box;
|
||
|
|
||
|
// right character - Sam
|
||
|
INDEX iSize = 170;
|
||
|
INDEX iYU = 120;
|
||
|
INDEX iYM = iYU + iSize;
|
||
|
INDEX iYB = iYM + iSize;
|
||
|
INDEX iXL = 420;
|
||
|
INDEX iXR = iXL + iSize*_pdp_SE->dp_fWideAdjustment;
|
||
|
|
||
|
box = PIXaabbox2D( PIX2D( iXL*_pdp_SE->GetWidth()/640, iYU*_pdp_SE->GetHeight()/480) ,
|
||
|
PIX2D( iXR*_pdp_SE->GetWidth()/640, iYM*_pdp_SE->GetHeight()/480));
|
||
|
_pdp_SE->PutTexture(&_toSamU, box, SE_COL_BLUE_NEUTRAL|255);
|
||
|
box = PIXaabbox2D( PIX2D( iXL*_pdp_SE->GetWidth()/640, iYM*_pdp_SE->GetHeight()/480) ,
|
||
|
PIX2D( iXR*_pdp_SE->GetWidth()/640, iYB*_pdp_SE->GetHeight()/480));
|
||
|
_pdp_SE->PutTexture(&_toSamD, box, SE_COL_BLUE_NEUTRAL|255);
|
||
|
|
||
|
iSize = 120;
|
||
|
iYU = 0;
|
||
|
iYM = iYU + iSize;
|
||
|
iYB = iYM + iSize;
|
||
|
iXL = -20;
|
||
|
iXR = iXL + iSize;
|
||
|
box = PIXaabbox2D( PIX2D( iXL*_pdp_SE->GetWidth()/640, iYU*_pdp_SE->GetWidth()/640) ,
|
||
|
PIX2D( iXR*_pdp_SE->GetWidth()/640, iYM*_pdp_SE->GetWidth()/640));
|
||
|
_pdp_SE->PutTexture(&_toLeftU, box, SE_COL_BLUE_NEUTRAL|200);
|
||
|
box = PIXaabbox2D( PIX2D( iXL*_pdp_SE->GetWidth()/640, iYM*_pdp_SE->GetWidth()/640) ,
|
||
|
PIX2D( iXR*_pdp_SE->GetWidth()/640, iYB*_pdp_SE->GetWidth()/640));
|
||
|
_pdp_SE->PutTexture(&_toLeftD, box, SE_COL_BLUE_NEUTRAL|200);
|
||
|
iYU = iYB;
|
||
|
iYM = iYU + iSize;
|
||
|
iYB = iYM + iSize;
|
||
|
iXL = -20;
|
||
|
iXR = iXL + iSize;
|
||
|
box = PIXaabbox2D( PIX2D( iXL*_pdp_SE->GetWidth()/640, iYU*_pdp_SE->GetWidth()/640) ,
|
||
|
PIX2D( iXR*_pdp_SE->GetWidth()/640, iYM*_pdp_SE->GetWidth()/640));
|
||
|
_pdp_SE->PutTexture(&_toLeftU, box, SE_COL_BLUE_NEUTRAL|200);
|
||
|
box = PIXaabbox2D( PIX2D( iXL*_pdp_SE->GetWidth()/640, iYM*_pdp_SE->GetWidth()/640) ,
|
||
|
PIX2D( iXR*_pdp_SE->GetWidth()/640, iYB*_pdp_SE->GetWidth()/640));
|
||
|
_pdp_SE->PutTexture(&_toLeftD, box, SE_COL_BLUE_NEUTRAL|200);
|
||
|
|
||
|
}
|
||
|
|
||
|
MEXaabbox2D boxBcgClouds1;
|
||
|
TiledTextureSE(_boxScreen_SE, 1.2f*_pdp_SE->GetWidth()/640.0f,
|
||
|
MEX2D(sin(_tmNow_SE*0.5f)*35,sin(_tmNow_SE*0.7f+1)*21), boxBcgClouds1);
|
||
|
_pdp_SE->PutTexture(&_toBcgClouds, _boxScreen_SE, boxBcgClouds1, C_BLACK|_ulA_SE>>2);
|
||
|
TiledTextureSE(_boxScreen_SE, 0.7f*_pdp_SE->GetWidth()/640.0f,
|
||
|
MEX2D(sin(_tmNow_SE*0.6f+1)*32,sin(_tmNow_SE*0.8f)*25), boxBcgClouds1);
|
||
|
_pdp_SE->PutTexture(&_toBcgClouds, _boxScreen_SE, boxBcgClouds1, C_BLACK|_ulA_SE>>2);
|
||
|
}
|
||
|
void CGame::LCDRenderCloudsForComp(void)
|
||
|
{
|
||
|
MEXaabbox2D boxBcgClouds1;
|
||
|
TiledTextureSE(_boxScreen_SE, 1.856f*_pdp_SE->GetWidth()/640.0f,
|
||
|
MEX2D(sin(_tmNow_SE*0.5f)*35,sin(_tmNow_SE*0.7f)*21), boxBcgClouds1);
|
||
|
_pdp_SE->PutTexture(&_toBcgClouds, _boxScreen_SE, boxBcgClouds1, SE_COL_BLUE_NEUTRAL|_ulA_SE>>2);
|
||
|
TiledTextureSE(_boxScreen_SE, 1.323f*_pdp_SE->GetWidth()/640.0f,
|
||
|
MEX2D(sin(_tmNow_SE*0.6f)*31,sin(_tmNow_SE*0.8f)*25), boxBcgClouds1);
|
||
|
_pdp_SE->PutTexture(&_toBcgClouds, _boxScreen_SE, boxBcgClouds1, SE_COL_BLUE_NEUTRAL|_ulA_SE>>2);
|
||
|
}
|
||
|
void CGame::LCDRenderClouds2(void)
|
||
|
{
|
||
|
NOTHING;
|
||
|
}
|
||
|
void CGame::LCDRenderGrid(void)
|
||
|
{
|
||
|
NOTHING;
|
||
|
}
|
||
|
void CGame::LCDRenderCompGrid(void)
|
||
|
{
|
||
|
MEXaabbox2D boxBcgGrid;
|
||
|
TiledTextureSE(_boxScreen_SE, 0.5f*_pdp_SE->GetWidth()/(_pdp_SE->dp_SizeIOverRasterSizeI*640.0f), MEX2D(0,0), boxBcgGrid);
|
||
|
_pdp_SE->PutTexture(&_toBcgGrid, _boxScreen_SE, boxBcgGrid, SE_COL_BLUE_NEUTRAL|_ulA_SE>>1);
|
||
|
}
|
||
|
void CGame::LCDDrawPointer(PIX pixI, PIX pixJ)
|
||
|
{
|
||
|
CDisplayMode dmCurrent;
|
||
|
_pGfx->GetCurrentDisplayMode(dmCurrent);
|
||
|
if (dmCurrent.IsFullScreen()) {
|
||
|
while (ShowCursor(FALSE) >= 0);
|
||
|
} else {
|
||
|
if (!_pInput->IsInputEnabled()) {
|
||
|
while (ShowCursor(TRUE) < 0);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
PIX pixSizeI = _toPointer.GetWidth();
|
||
|
PIX pixSizeJ = _toPointer.GetHeight();
|
||
|
pixI-=1;
|
||
|
pixJ-=1;
|
||
|
_pdp_SE->PutTexture( &_toPointer, PIXaabbox2D( PIX2D(pixI, pixJ), PIX2D(pixI+pixSizeI, pixJ+pixSizeJ)),
|
||
|
LCDFadedColor(C_WHITE|255));
|
||
|
|
||
|
//::LCDDrawPointer(pixI, pixJ);
|
||
|
}
|
||
|
COLOR CGame::LCDGetColor(COLOR colDefault, const char *strName)
|
||
|
{
|
||
|
if (!strcmp(strName, "thumbnail border")) {
|
||
|
colDefault = SE_COL_BLUE_NEUTRAL|255;
|
||
|
} else if (!strcmp(strName, "no thumbnail")) {
|
||
|
colDefault = SE_COL_ORANGE_NEUTRAL|255;
|
||
|
} else if (!strcmp(strName, "popup box")) {
|
||
|
colDefault = SE_COL_BLUE_NEUTRAL|255;
|
||
|
} else if (!strcmp(strName, "tool tip")) {
|
||
|
colDefault = SE_COL_ORANGE_LIGHT|255;
|
||
|
} else if (!strcmp(strName, "unselected")) {
|
||
|
colDefault = SE_COL_ORANGE_NEUTRAL|255;
|
||
|
} else if (!strcmp(strName, "selected")) {
|
||
|
colDefault = SE_COL_ORANGE_LIGHT|255;
|
||
|
} else if (!strcmp(strName, "disabled selected")) {
|
||
|
colDefault = SE_COL_ORANGE_DARK_LT |255;
|
||
|
} else if (!strcmp(strName, "disabled unselected")) {
|
||
|
colDefault = SE_COL_ORANGE_DARK|255;
|
||
|
} else if (!strcmp(strName, "label")) {
|
||
|
colDefault = C_WHITE|255;
|
||
|
} else if (!strcmp(strName, "title")) {
|
||
|
colDefault = C_WHITE|255;
|
||
|
} else if (!strcmp(strName, "editing")) {
|
||
|
colDefault = SE_COL_ORANGE_NEUTRAL|255;
|
||
|
} else if (!strcmp(strName, "hilited")) {
|
||
|
colDefault = SE_COL_ORANGE_LIGHT|255;
|
||
|
} else if (!strcmp(strName, "hilited rectangle")) {
|
||
|
colDefault = SE_COL_ORANGE_NEUTRAL|255;
|
||
|
} else if (!strcmp(strName, "edit fill")) {
|
||
|
colDefault = SE_COL_BLUE_DARK_LT|75;
|
||
|
} else if (!strcmp(strName, "editing cursor")) {
|
||
|
colDefault = SE_COL_ORANGE_NEUTRAL|255;
|
||
|
} else if (!strcmp(strName, "model box")) {
|
||
|
colDefault = SE_COL_ORANGE_NEUTRAL|255;
|
||
|
} else if (!strcmp(strName, "hiscore header")) {
|
||
|
colDefault = SE_COL_ORANGE_LIGHT|255;
|
||
|
} else if (!strcmp(strName, "hiscore data")) {
|
||
|
colDefault = SE_COL_ORANGE_NEUTRAL|255;
|
||
|
} else if (!strcmp(strName, "hiscore last set")) {
|
||
|
colDefault = SE_COL_ORANGE_NEUTRAL|255;
|
||
|
} else if (!strcmp(strName, "slider box")) {
|
||
|
colDefault = SE_COL_ORANGE_NEUTRAL|255;
|
||
|
} else if (!strcmp(strName, "file info")) {
|
||
|
colDefault = SE_COL_ORANGE_NEUTRAL|255;
|
||
|
} else if (!strcmp(strName, "display mode")) {
|
||
|
colDefault = SE_COL_ORANGE_NEUTRAL|255;
|
||
|
} else if (!strcmp(strName, "bcg fill")) {
|
||
|
colDefault = SE_COL_BLUE_DARK|255;
|
||
|
}
|
||
|
return ::LCDGetColor(colDefault, strName);
|
||
|
}
|
||
|
COLOR CGame::LCDFadedColor(COLOR col)
|
||
|
{
|
||
|
return ::LCDFadedColor(col);
|
||
|
}
|
||
|
COLOR CGame::LCDBlinkingColor(COLOR col0, COLOR col1)
|
||
|
{
|
||
|
return ::LCDBlinkingColor(col0, col1);
|
||
|
}
|
||
|
|
||
|
// menu interface functions
|
||
|
void CGame::MenuPreRenderMenu(const char *strMenuName)
|
||
|
{
|
||
|
}
|
||
|
void CGame::MenuPostRenderMenu(const char *strMenuName)
|
||
|
{
|
||
|
}
|