Files
FC1/CryGame/GameLoading.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

3012 lines
83 KiB
C++

//////////////////////////////////////////////////////////////////////
//
// Game source code
//
// File: GameLoading.cpp
//
// History:
// -December 11,2001:
// Created by Alberto and Petar
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Game.h"
#include "XNetwork.h"
#include "XServer.h"
#include "XClient.h"
#include "UIHud.h"
#include "XPlayer.h"
#include "XVehicle.h"
#include "XVehicleSystem.h"
#include "PlayerSystem.h"
#include "XServer.h"
#include "WeaponSystemEx.h"
#include "EntityClassRegistry.h"
#include "ScriptObjectGame.h"
#include "ScriptObjectInput.h"
#include "ScriptObjectLanguage.h"
#include "ScriptObjectStream.h"
#include "ScriptObjectRenderer.h"
#include "ScriptObjectAI.h"
#include <IEntitySystem.h>
#include <IMovieSystem.h>
#include "UISystem.h"
#include "ScriptObjectUI.h"
#include "TagPoint.h"
//#include "XPath.h"
#include <ISound.h>
#include <IAgent.h>
#if !defined(LINUX)
# include <dbghelp.h>
# pragma comment(lib, "dbghelp.lib")
#else
# include <stdio.h>
#endif
#if defined(LINUX)
#include "ILog.h"
#endif
//////////////////////////////////////////////////////////////////////
#define CHUNK_ENTITY 0x01
#define CHUNK_PLAYER 0x02
#define CHUNK_AI 0x03
#define CHUNK_INGAME_SEQUENCE 0x04
#define CHUNK_HUD 0x05
//#define _INTERNET_SIMULATOR
#define SAVEGAME_THUMBNAIL_SIZEX 128
#define SAVEGAME_THUMBNAIL_SIZEY 96
//////////////////////////////////////////////////////////////////////
// load level batch process
bool CXGame::LoadLevelForEditor(const char *pszLevelDirectory, const char *pszMissionName)
{
if (pszMissionName)
m_currentMission = pszMissionName;
// start local server
if(!StartupServer(false,"__editor__"))
return false;
//////////////////////////////////////////////////////////////////////////
// Init common stuff.
//////////////////////////////////////////////////////////////////////////
//init the entity registry
m_pServer->m_pISystem->LoadLevel( pszLevelDirectory,m_currentMission.c_str(),true );
// start local client and connect
if(!StartupLocalClient())
return false;
m_pClient->XConnect("localhost");
m_pSystem->GetIInput()->GetIKeyboard()->ClearKeyState();
return true;
}
#define TABLE_END (-1) // a value not used in enum ScriptVarType
//////////////////////////////////////////////////////////////////////////
struct PropertyWriter : IScriptObjectDumpSink
{
IScriptObject *table;
CStream &stm;
IScriptSystem *m_pScriptSystem;
PropertyWriter(IScriptObject *p, CStream &s, IScriptSystem *ss) : table(p), stm(s), m_pScriptSystem(ss) {};
void Element(const char *sName, int nIdx, ScriptVarType type, bool iskey)
{
switch(type)
{
case svtUserData:
case svtFunction: TRACE("WARNING: userdata or function found in properties table (%s)", sName);
case svtNull: break;
case svtString: { const char *s = ""; _VERIFY(iskey ? table->GetValue(sName, s) : table->GetAt(nIdx, s)); stm.Write(s); }; break;
case svtNumber: { float f = 0; _VERIFY(iskey ? table->GetValue(sName, f) : table->GetAt(nIdx, f)); stm.Write(f); }; break;
case svtObject:
{
_SmartScriptObject t(m_pScriptSystem, true);
_VERIFY(iskey ? table->GetValue(sName, t) : table->GetAt(nIdx, t));
t->Dump(&PropertyWriter(t, stm, m_pScriptSystem));
stm.Write((char)TABLE_END);
break;
};
};
};
//////////////////////////////////////////////////////////////////////////
void OnElementFound(const char *sName, ScriptVarType type)
{
stm.Write((char)type);
stm.Write((char)true);
stm.Write(sName);
Element(sName, 0, type, true);
};
void OnElementFound(int nIdx, ScriptVarType type)
{
stm.Write((char)type);
stm.Write((char)false);
stm.Write(nIdx);
Element("", nIdx, type, false);
};
};
//////////////////////////////////////////////////////////////////////////
void LoadProperties(IScriptObject *table, CStream &stm, IScriptSystem *ss, char *parent)
{
ASSERT(table);
ASSERT(ss); // martin made me do it
for(;;)
{
char what;
stm.Read(what);
if(what==TABLE_END) return;
char iskey;
stm.Read(iskey);
int idx = 0;
string key;
iskey ? stm.Read(key) : stm.Read(idx);
switch(what)
{
case svtNull: { iskey ? table->SetToNull(key.c_str()) : table->SetNullAt(idx); break; };
case svtString: { string s; stm.Read(s); iskey ? table->SetValue(key.c_str(), s.c_str()) : table->SetAt(idx, s.c_str()); break; };
case svtNumber: { float f = 0; stm.Read(f); iskey ? table->SetValue(key.c_str(), f) : table->SetAt(idx, f); break; };
case svtObject: { _SmartScriptObject t(ss); iskey ? table->SetValue(key.c_str(), *t) : table->SetAt(idx, *t); LoadProperties(t, stm, ss, (char *)key.c_str()); break; };
case svtUserData:
case svtFunction: TRACE("WARNING: can't restore userdata or function in properties table (%s.%s)", parent, key.c_str());
};
};
};
//////////////////////////////////////////////////////////////////////////
class CCVarSerializeGameSave : public ICVarDumpSink
{
public:
CCVarSerializeGameSave(CStream *pStm, bool bSave)
{
m_pStm=pStm;
m_bSave=bSave;
m_nCount=0;
}
void OnElementFound(ICVar *pCVar)
{
if (m_bSave)
{
m_pStm->Write(pCVar->GetName());
m_pStm->Write(pCVar->GetString());
}
m_nCount++;
}
int GetCount() { return(m_nCount); }
private:
CStream *m_pStm;
bool m_bSave;
int m_nCount;
};
// IMPORTANT: DONT FORGET TO UPDATE "SAVEVERSION" DEFINE IN GAME.H IF THE FORMAT CHANGES
//
//////////////////////////////////////////////////////////////////////////
void SaveName(string &s, string &prof)
{
if(!s[0]) s = "quicksave";
for(unsigned int i = 0; i<s.size(); i++) if(!isalnum(s[i])) s[i] = '_';
s = "profiles/player/" + prof + "/savegames/" + s + ".sav";
};
//////////////////////////////////////////////////////////////////////////
bool CXGame::SaveToStream(CStream &stm, Vec3d *pos, Vec3d *angles,string sFilename)
{
IBitStream *pBitStream = GetIBitStream();
if(m_bEditor)
{
m_pLog->Log("Skipping savegame in editor...");
return false;
};
IEntitySystem *pEntitySystem=m_pSystem->GetIEntitySystem();
IEntity *pPlayerEnt=NULL;
if (m_pClient)
pPlayerEnt=pEntitySystem->GetEntity(m_pClient->GetPlayerId());
//ASSERT(pPlayerEnt);
if (!pPlayerEnt)
//CryError("Saving to checkpoint - Current player not set or invalid - data error - possible reasons: \n - the first respawn point might be inside a checkpoint \n - something else other than shapes is used to trigger game events \n the trigger used to trigger checkpoint is not trigger once \n solution: check out the map and fix");
CryError("A checkpoint has been triggered to save data when the player is not existing yet, generally right after respawning. \n This is a data error, and must be fixed by the designer working on this map. \n Possible data errors are: \n - the first respawn point might be inside a checkpoint \n - something else other than shapes is used to trigger game events; \n - the area trigger used to trigger checkpoint is not trigger once; \n how to proceed: get the designer working on this map to fix this");
CPlayer *pPlayer=NULL;
if(pPlayerEnt->GetContainer()) pPlayerEnt->GetContainer()->QueryContainerInterface(CIT_IPLAYER,(void**) &pPlayer);
//ASSERT(pPlayer);
if (!pPlayer)
CryError("Cannot get player container");
if(pPlayer->m_stats.health<=0)
{
m_pLog->Log("Cannot save while player is dead");
return false;
};
CScriptObjectStream scriptStream;
scriptStream.Create(m_pScriptSystem);
scriptStream.Attach(&stm);
//m_p3DEngine->UnRegisterAllEntities();
stm.Reset();
// save header
stm.Write(SAVEMAGIC);
stm.Write((int)PATCH2_SAVEVERSION);
// get lowercase versions of levelname and missionname
char szLowerCaseStr[256] = {0};
strncpy(szLowerCaseStr, g_LevelName->GetString(), 255);
strlwr(szLowerCaseStr);
// save levelname
stm.Write(szLowerCaseStr);
strncpy(szLowerCaseStr, m_pServer->m_GameContext.strMission.c_str(), 255);
strlwr(szLowerCaseStr);
// save mission name
stm.Write(szLowerCaseStr);
// write current time and date
SYSTEMTIME pSystemTime;
GetLocalTime(&pSystemTime); // FIX: this should be moved to crysystem
stm.Write((unsigned char)pSystemTime.wHour); // hour
stm.Write((unsigned char)pSystemTime.wMinute);// minute
stm.Write((unsigned char)pSystemTime.wSecond);// second
stm.Write((unsigned char)pSystemTime.wDay); // day
stm.Write((unsigned char)pSystemTime.wMonth); // month
stm.Write((unsigned short)pSystemTime.wYear); // year
// save savegame name
stm.Write(sFilename);
// save the number of entities, for the loading screen bar
int nEnt=pEntitySystem->GetNumEntities();
stm.Write(nEnt);
WRITE_COOKIE_NO(stm,0x22);
// save current EAX preset
int nPreset;
CS_REVERB_PROPERTIES tProps;
m_pSystem->GetISoundSystem()->GetCurrentEaxEnvironment(nPreset,tProps);
stm.Write(nPreset);
if (nPreset==-1)
{
stm.WriteBits((BYTE *)&tProps,sizeof(CS_REVERB_PROPERTIES));
}
WRITE_COOKIE_NO(stm,0x12);
// save saveable cvars
CCVarSerializeGameSave tCount(&stm,false);
m_pSystem->GetIConsole()->DumpCVars(&tCount,VF_SAVEGAME);
int nCount=tCount.GetCount(); // get the number of cvars to save
stm.Write(nCount);
CCVarSerializeGameSave t(&stm,true);
m_pSystem->GetIConsole()->DumpCVars(&t,VF_SAVEGAME); // save them
ASSERT(t.GetCount()==nCount);
//[petar] save autobalancing stuff
IAISystem *pAISystem = m_pSystem->GetAISystem();
AIBalanceStats stats;
pAISystem->GetAutoBalanceInterface()->GetAutobalanceStats(stats);
stm.Write(stats.nAllowedDeaths);
/*
WRITE_COOKIE_NO(stm,0x11);
// save played cutscenes
nCount=m_lstPlayedCutScenes.size();
stm.Write(nCount);
for (std::set<string>::iterator it=m_lstPlayedCutScenes.begin();it!=m_lstPlayedCutScenes.end();it++)
{
string sName=*it;
stm.Write(sName);
} //it
*/
IEntityClassRegistry *pECR=GetClassRegistry();
IEntityItPtr pEntities=pEntitySystem->GetEntityIterator();
pEntities->MoveFirst();
IEntity *pEnt=NULL;
WRITE_COOKIE_NO(stm,0x3c);
// [kirill] savig IDs of all dynamicaly spawn BUT saved entities
// so would be able to preserve IDs from being taken by other dynamic entities on loadind
std::vector<int> dynSaveableEntities;
while((pEnt=pEntities->Next())!=NULL)
{
if (!pEnt->IsSaveEnabled())
continue;
if (pEntitySystem->IsDynamicEntityId( pEnt->GetId() ))
dynSaveableEntities.push_back( pEnt->GetId() );
}
stm.Write((int)dynSaveableEntities.size());
for(int cntr=0; cntr<(int)(dynSaveableEntities.size()); ++cntr )
stm.Write((int)dynSaveableEntities[cntr]);
WRITE_COOKIE_NO(stm,61);
pEntities=pEntitySystem->GetEntityIterator();
while((pEnt=pEntities->Next())!=NULL)
{
if (!pEnt->IsSaveEnabled())
continue;
EntityClass *pClass = pECR->GetByClass(pEnt->GetEntityClassName());
CPlayer *pPlayer = NULL;
if(pEnt->GetContainer()) pEnt->GetContainer()->QueryContainerInterface(CIT_IPLAYER,(void**) &pPlayer);
if(pPlayer && pPlayer->m_stats.health<=0) continue;
//[kirill]
// fixme somtimes health of dead players somehow not 0 so let's make sure we don't save dead guy
if(pPlayer && pEnt->GetPhysics() && pEnt->GetPhysics()->GetType()!=PE_LIVING)
{
m_pSystem->GetILog()->Log("\001 WARNING, dead player [%s] has health %d ", pEnt->GetName(), pPlayer->m_stats.health );
continue;
}
stm.Write((BYTE)CHUNK_ENTITY);
pBitStream->WriteBitStream(stm,pClass->ClassId,eEntityClassId);
if(pPlayer && pPlayer->IsMyPlayer())
stm.Write((EntityId)0); // don't save id for local player - generate it on spawn, to avoid ID's collision
else
stm.Write(pEnt->GetId());
// the position is saved later
// with pEnt->Save(
//////////////////////////////////////////////////////////////////////////
// [marco] the position must be saved before / must be done the same way to
// be consistent with load level!
// save relative position if bound
Vec3d vPos = pEnt->GetPos(!pEnt->IsBound());
Vec3d vAng = pEnt->GetAngles(pEnt->IsBound());
if (!stm.Write(vPos))
CryError("Error while saving entity %s, id=%d",pEnt->GetName(),(int)pClass->ClassId);
if (!stm.Write(vAng))
CryError("Error while saving entity %s, id=%d",pEnt->GetName(),(int)pClass->ClassId);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// rendering stuff
stm.Write(pEnt->GetScale());
stm.Write(pEnt->GetRndFlags());
unsigned char uViewDistRatio=(int)(pEnt->GetViewDistRatioNormilized()*100.0f);
unsigned char uLodRatio=(int)(pEnt->GetLodRatioNormilized()*100.0f);
stm.Write(uViewDistRatio);
stm.Write(uLodRatio);
IMatInfo *pMat=pEnt->GetMaterial();
string sMat;
if (pMat)
sMat=pMat->GetName();
stm.Write(sMat);
stm.Write(pEnt->IsHidden());
//////////////////////////////////////////////////////////////////////////
WRITE_COOKIE_NO(stm,76);
stm.Write(pEnt->GetName());
if (m_pLog->GetVerbosityLevel()>5)
m_pLog->Log("SAVED entity name %s classname %s classid=%d id=%d",pEnt->GetName(),pClass->strClassName.c_str(),(int)pClass->ClassId,pEnt->GetId());
IScriptObject *so = pEnt->GetScriptObject();
ASSERT(so);
WRITE_COOKIE_NO(stm,77);
stm.AlignWrite();
_SmartScriptObject props(m_pScriptSystem, true);
if(so->GetValue("Properties", props)) props->Dump(&PropertyWriter(props, stm, m_pScriptSystem));
stm.Write((char)TABLE_END);
_SmartScriptObject propsi(m_pScriptSystem, true);
if(so->GetValue("PropertiesInstance", propsi)) propsi->Dump(&PropertyWriter(propsi, stm, m_pScriptSystem));
stm.Write((char)TABLE_END);
_SmartScriptObject events(m_pScriptSystem, true);
if(so->GetValue("Events", events)) events->Dump(&PropertyWriter(events, stm, m_pScriptSystem));
stm.Write((char)TABLE_END);
WRITE_COOKIE_NO(stm,78);
WRITE_COOKIE_NO(stm,45);
if(pPlayer)
stm.Write(pPlayer->m_stats.health); // int
else
stm.Write((int)0); // int
WRITE_COOKIE_NO(stm,46);
pEnt->Save(stm,scriptStream.GetScriptObject());
if(pPlayer)
{
pPlayer->SaveGame(stm);
if (pEnt->GetAI())
stm.Write(pEnt->GetAI()->GetName());
}
}
stm.Write((BYTE)CHUNK_PLAYER);
if(pPlayer->IsMyPlayer())
stm.Write((EntityId)0); // don't save id for local player - generate it on spawn, to avoid ID's collision
else
stm.Write(m_pClient->GetPlayerId());
stm.Write(pPlayer->m_bFirstPerson);
IEntityCamera *pCam=pPlayerEnt->GetCamera();
ASSERT(pCam);
if(pos && angles)
{
stm.Write(*pos);
stm.Write(*angles);
}
else
{
//[PETAR] please do not touch anything that is not clear to you
//the next lines serialize THE CAMERA POSITION, not the player entity position
//the entity position is serialized in a normal entity chunk
stm.Write(pCam->GetPos());
stm.Write(pCam->GetAngles());
};
// now loop through entities again to save their AI state
// it has to be done here because all entities need to be spawned when AI state is recreated
pEntities->MoveFirst();
while((pEnt=pEntities->Next())!=NULL)
{
CPlayer *pPlayer=NULL;
CVehicle *pVehicle=NULL;
IAIObject *pAIObject = pEnt->GetAI();
if(pEnt->GetContainer())
{
pEnt->GetContainer()->QueryContainerInterface(CIT_IPLAYER,(void**) &pPlayer);
pEnt->GetContainer()->QueryContainerInterface(CIT_IVEHICLE,(void**) &pVehicle);
}
if (!pEnt->IsSaveEnabled())
continue;
if (!pPlayer && !pVehicle && !pAIObject)
continue;
if (pPlayer)
{
if (!pPlayer->IsAlive())
continue;
// if (pPlayer->IsMyPlayer())
// continue;
stm.Write((BYTE)CHUNK_AI);
// [kirill]
// for local player ID will be different - it will be spawn
if(pPlayer->IsMyPlayer())
stm.Write((int)0);
else
stm.Write((int)pEnt->GetId()); // for which player is this
pPlayer->SaveAIState(stm, scriptStream);
}
if (pVehicle)
{
stm.Write((BYTE)CHUNK_AI);
stm.Write((int)pEnt->GetId()); // for which player is this
pVehicle->SaveAIState(stm, scriptStream);
}
if (!pPlayer && !pVehicle)
{
stm.Write((BYTE)CHUNK_AI);
stm.Write((int)pEnt->GetId()); // for which player is this
pAIObject->Save(stm);
IScriptSystem *pScriptSystem = GetSystem()->GetIScriptSystem();
HSCRIPTFUNCTION saveOverallFunction=NULL;
if( pEnt->GetScriptObject() && pEnt->GetScriptObject()->GetValue("OnSaveOverall", saveOverallFunction) )
{
pScriptSystem->BeginCall(saveOverallFunction);
pScriptSystem->PushFuncParam(pEnt->GetScriptObject());
pScriptSystem->PushFuncParam(scriptStream.GetScriptObject());
pScriptSystem->EndCall();
}
}
WRITE_COOKIE_NO(stm,13);
}
// serialize any playing cutscenes
IMovieSystem *pMovies = m_pSystem->GetIMovieSystem();
ISequenceIt *pIt = pMovies->GetSequences();
IAnimSequence *pSeq = pIt->first();
while (pSeq)
{
if (pMovies->IsPlaying(pSeq))
{
stm.Write((BYTE)CHUNK_INGAME_SEQUENCE);
stm.Write(pSeq->GetName());
stm.Write(pMovies->GetPlayingTime(pSeq));
}
pSeq = pIt->next();
}
pIt->Release();
// save required hud/clientstuff data
stm.Write((BYTE)CHUNK_HUD);
if (m_pUIHud)
{
GetScriptSystem()->BeginCall("Hud", "OnSave");
GetScriptSystem()->PushFuncParam(m_pUIHud->GetScript());
GetScriptSystem()->PushFuncParam(scriptStream.GetScriptObject());
GetScriptSystem()->EndCall();
}
_SmartScriptObject pClientStuff(m_pScriptSystem,true);
if(m_pScriptSystem->GetGlobalValue("ClientStuff",pClientStuff))
{
m_pScriptSystem->BeginCall("ClientStuff","OnSave");
m_pScriptSystem->PushFuncParam(pClientStuff);
m_pScriptSystem->PushFuncParam(scriptStream.GetScriptObject());
m_pScriptSystem->EndCall();
}
return true;
};
//////////////////////////////////////////////////////////////////////////
void CXGame::Save(string sFileName, Vec3d *pos, Vec3d *angles,bool bFirstCheckpoint )
{
FUNCTION_PROFILER( GetISystem(), PROFILE_GAME );
//// Saving in Multiplayer only possible on server.
//if (!GetServer() && IsMultiplayer())
// return;
// No saving in mp
if(IsMultiplayer())
{
m_pLog->Log("Cannot save multiplayer game");
return;
}
m_sLastSavedCheckpointFilename = "";
if (m_bIsLoadingLevelFromFile)
{
m_pLog->Log("\001 [ERROR!!!] CANNOT SAVE WHILE LOADING!!!");
return;
}
if(!m_pClient)
{
m_pLog->Log("Cannot save game with no map loaded");
return;
};
// ?
if(!m_pServer)
{
m_pLog->Log("Cannot save multiplayer game");
return;
};
CDefaultStreamAllocator sa;
CStream stm(3000, &sa);
if (SaveToStream(stm, pos, angles,sFileName))
{
m_strLastSaveGame = sFileName;
assert(g_playerprofile);
if (g_playerprofile->GetString() && strlen(g_playerprofile->GetString()))
{
string tmp( g_playerprofile->GetString() );
SaveName(sFileName, tmp);
}
else
{
string tmp( "default" );
SaveName(sFileName, tmp);
}
m_pLog->LogToConsole("Level saved in %d bytes(%s)", BITS2BYTES(stm.GetSize()), sFileName.c_str());
// replace / by \ because MakeSureDirectoryPathExists does not work with unix paths
size_t pos = 1;
for(;;)
{
pos = sFileName.find_first_of("/", pos);
if (pos == string::npos)
{
break;
}
sFileName.replace(pos, 1, "\\", 1);
pos+=1;
}
if (MakeSureDirectoryPathExists(sFileName.c_str()))
{
if(!m_pSystem->WriteCompressedFile((char *)sFileName.c_str(), stm.GetPtr(), stm.GetSize()))
{
m_pLog->Log("cannot write savegame to file %s", sFileName.c_str());
return;
};
m_sLastSavedCheckpointFilename = sFileName;
/*
//////////////////////////////////////////////////////////////////////////
// Make screenshot of current location, and save it to the .dds file.
//////////////////////////////////////////////////////////////////////////
if (!bFirstCheckpoint)
m_fTimeToSaveThumbnail = 1.0f; // Save checkpoint thumbnail 1 second from now.
else
{
m_fTimeToSaveThumbnail = 5.0f; // Save checkpoint thumbnail 5 seconds from now.
}
//////////////////////////////////////////////////////////////////////////
*/
}
};
}
//////////////////////////////////////////////////////////////////////////
bool CXGame::LoadFromStream(CStream &stm, bool isdemo)
{
if(IsMultiplayer())
{
assert(0);
m_pLog->LogError("ERROR: LoadFromStream IsMultiplayer=true"); // severe problem - stream is different for MP
return false;
}
m_bIsLoadingLevelFromFile = true;
// [anton] make sure physical world has the most recent IsMultiplayer flag before loading
m_pSystem->GetIPhysicalWorld()->GetPhysVars()->bMultiplayer = IsMultiplayer() ? 1:0;
CScriptObjectStream scriptStream;
scriptStream.Create(m_pScriptSystem);
scriptStream.Attach(&stm);
string sMagic;
stm.Read(sMagic);
if(strcmp(sMagic.c_str(), SAVEMAGIC))
{
m_pLog->LogToConsole("ERROR: this is not a valid savegame file");
m_bIsLoadingLevelFromFile = false;
return false;
};
int nVersion;
stm.Read(nVersion);
stm.SetStreamVersion(nVersion);
switch (nVersion)
{
case SAVEVERSION:
return LoadFromStream_RELEASEVERSION(stm,isdemo,scriptStream);
case PATCH1_SAVEVERSION:
return LoadFromStream_PATCH_1(stm,isdemo,scriptStream);
// add more here as more patches are released
}
if(nVersion!=PATCH1_SAVEVERSION && nVersion!=PATCH2_SAVEVERSION)
{
m_pLog->LogToConsole("ERROR: savegame file from different version of the game");
m_bIsLoadingLevelFromFile = false;
return false;
};
IBitStream *pBitStream=GetIBitStream();
IEntitySystem *pEntitySystem=m_pSystem->GetIEntitySystem();
IEntityClassRegistry *pECR=GetClassRegistry();
IEntity *pEnt=NULL;
CEntityDesc ed;
int localPlayerId=0;
string sLevelName;
string sMissionName;
stm.Read(sLevelName);
stm.Read(sMissionName);
// read dummy save date and time
unsigned char bDummy;
unsigned short wDummy;
stm.Read(bDummy); // hour
stm.Read(bDummy); // minute
stm.Read(bDummy); // second
stm.Read(bDummy); // day
stm.Read(bDummy); // month
stm.Read(wDummy); // year
// load savegame name
string sFilename;
stm.Read(sFilename);
// load the number of entities, for loading screen bar
int nEnt;
stm.Read(nEnt);
bool bS=IsServer();
bool bC=IsClient();
assert(!IsMultiplayer());
VERIFY_COOKIE_NO(stm,0x22);
// Load EAX preset
int nPreset;
CS_REVERB_PROPERTIES tProps;
m_pSystem->GetISoundSystem()->GetCurrentEaxEnvironment(nPreset,tProps);
stm.Read(nPreset);
if (nPreset==-1)
{
stm.ReadBits((BYTE *)&tProps,sizeof(CS_REVERB_PROPERTIES));
}
VERIFY_COOKIE_NO(stm,0x12);
// load saved cvars
string varname,val;
int nCount,i;
stm.Read(nCount);
IConsole *pCon=m_pSystem->GetIConsole();
for (i=0;i<nCount;i++)
{
if(stm.Read(varname))
if(stm.Read(val))
{
ICVar *pCVar=m_pSystem->GetIConsole()->GetCVar(varname.c_str());
if (!pCVar)
{
m_pSystem->GetILog()->Log("\001 WARNING, CVar %s(%s) was saved but is not present",varname.c_str(),val.c_str());
}
else
pCVar->Set(val.c_str());
}
else
{
m_pSystem->GetILog()->LogError("CXGame::LoadFromStream %d/%d critical error",i,nCount);
stm.Debug();
m_bIsLoadingLevelFromFile = false;
return false;
}
} //i
if(m_pSystem->GetISoundSystem())
m_pSystem->GetISoundSystem()->Silence();
m_pSystem->GetISoundSystem()->Mute(true);
bool bLoadBar = false;
IConsole *pConsole = m_pSystem->GetIConsole();
assert(pConsole);
if(stricmp(g_LevelName->GetString(),sLevelName.c_str()) != 0 || !m_pServer || (stricmp(m_pServer->m_GameContext.strMission.c_str(),sMissionName.c_str()) != 0))
{
m_pLog->LogToConsole("Loading %s / %s", sLevelName.c_str(), sMissionName.c_str());
if (m_pServer)
ShutdownServer();
if (isdemo)
{
//StartupClient();
m_pClient->DemoConnect();
LoadLevelCS( false,sLevelName.c_str(), sMissionName.c_str(), false);
}
else
{
GetISystem()->GetIInput()->EnableEventPosting(false);
//m_pSystem->GetIInput()->GetIKeyboard()->ClearKeyState();
LoadLevelCS(false,sLevelName.c_str(), sMissionName.c_str(), false);
//m_pClient->Connect("localhost");
//m_pSystem->GetIInput()->GetIKeyboard()->ClearKeyState();
GetISystem()->GetIInput()->EnableEventPosting(true);
};
}
else
{
bLoadBar = 1;
string sLoadingScreenTexture = m_currentLevelFolder + "/loadscreen_" + m_currentLevel + ".dds";
pConsole->SetLoadingImage( sLoadingScreenTexture.c_str() );
pConsole->Clear();
pConsole->ResetProgressBar(nEnt + 3);
pConsole->SetScrollMax(600);
pConsole->ShowConsole(1);
DeleteMessage("Switch"); // no switching during loading
// local player has to exit all areas before starting to delete entities
IEntity *pIMyPlayer = GetMyPlayer();
if( pIMyPlayer )
{
IEntityContainer *pIContainer = pIMyPlayer->GetContainer();
if (pIContainer)
{
CPlayer *pPlayer = NULL;
if (pIContainer->QueryContainerInterface(CIT_IPLAYER, (void**)&pPlayer))
m_XAreaMgr.ExitAllAreas( pPlayer->m_AreaUser );
}
}
m_pSystem->GetI3DEngine()->RestoreTerrainFromDisk();
m_pSystem->GetIMovieSystem()->Reset( false );
m_pLog->Log("REMOVING entities:");
IEntityItPtr pEntities=pEntitySystem->GetEntityIterator();
pEntities->MoveFirst();
IEntity *pEnt=NULL;
while((pEnt=pEntities->Next())!=NULL)
{
EntityClass *pClass=pECR->GetByClass(pEnt->GetEntityClassName());
if (m_pWeaponSystemEx->IsProjectileClass(pClass->ClassId)) continue;
#ifdef _DEBUG
m_pLog->Log("REMOVING entity classname %s classid=%02d id=%3id ",pClass->strClassName.c_str(),(int)pClass->ClassId,pEnt->GetId());
#endif
pEntitySystem->RemoveEntity(pEnt->GetId());
}
pConsole->TickProgressBar(); // advance progress
pEntitySystem->Update();
SoftReset();
m_pEntitySystem->Reset();
pConsole->TickProgressBar(); // advance progress
}
// PETAR lets delete all guys since they will be spawned anyway
m_pSystem->GetAISystem()->Reset();
IAISystem *pAISystem = m_pSystem->GetAISystem();
// adaptive balancing
if (pAISystem)
{
if (cv_game_Difficulty->GetIVal())
{
float fAcc,fAgg,fHealth;
if (pAISystem->GetAutoBalanceInterface())
{
pAISystem->GetAutoBalanceInterface()->GetMultipliers(fAcc,fAgg,fHealth);
cv_game_Aggression->Set(fAgg);
cv_game_Accuracy->Set(fAcc);
cv_game_Health->Set(fHealth);
}
}
}
//[petar] load autobalancing stuff
int nAllowedDeaths;
stm.Read(nAllowedDeaths);
pAISystem->GetAutoBalanceInterface()->SetAllowedDeathCount(nAllowedDeaths);
// apparently these 20 updates are needed to setup everything,
// from hud to netwrok stream etc. do not remove it
// or savegame won't load
for (int i = 0; i<20; i++)
Update();
if (bLoadBar)
{
pConsole->TickProgressBar(); // advance progress
}
VERIFY_COOKIE_NO(stm,0x3c);
// loading reserver IDs for dynacally created saved entities
int dynReservedIDsNumber=0;
stm.Read(dynReservedIDsNumber);
for( ; dynReservedIDsNumber>0; --dynReservedIDsNumber )
{
int reservedId;
stm.Read((int&)reservedId);
pEntitySystem->MarkId( reservedId );
}
// only load this in case of older save
if(nVersion<PATCH2_SAVEVERSION)
{
VERIFY_COOKIE_NO(stm,0x73);
// load hud objectives
if (m_pUIHud)
{
GetScriptSystem()->BeginCall("Hud", "OnLoadOld");
GetScriptSystem()->PushFuncParam(m_pUIHud->GetScript());
GetScriptSystem()->PushFuncParam(scriptStream.GetScriptObject());
GetScriptSystem()->EndCall();
}
}
VERIFY_COOKIE_NO(stm,61);
while (!stm.EOS())
{
BYTE cChunk=0;
stm.Read(cChunk);
switch(cChunk)
{
case CHUNK_ENTITY:
{
EntityId id;
EntityClassId ClassID;
pBitStream->ReadBitStream(stm,ClassID,eEntityClassId);
EntityClass *pClass=pECR->GetByClassId(ClassID);
ASSERT(pClass);
ed.className=pClass->strClassName;
ed.ClassId=pClass->ClassId;
stm.Read(id);
ed.id=id;
// [kirill] if this entity was dynamicly created - ID was marked when dynReservedIDsNumber loaded, to prevent
// from being taked by some other dynamically spawned non-saved entity. So now we load it and let's free the id
if (pEntitySystem->IsDynamicEntityId( id ))
pEntitySystem->ClearId(id);
// position and angles are read later -
// with pEnt->Load(
//////////////////////////////////////////////////////////////////////////
//[marco] position and angles must be read before spawining the entity - must
// be consistent with load level!
Vec3d vPos,vAngles;
if (!stm.Read(vPos))
CryError("Error while reading position for entity id=%d",id);
if (!stm.Read(vAngles))
CryError("Error while reading position for entity id=%d",id);
ed.pos=vPos;
ed.angles=vAngles;
//////////////////////////////////////////////////////////////////////////
// renderer stuff
float fScale;
stm.Read(fScale);
int dwRendFlags;
stm.Read(dwRendFlags);
unsigned char uViewDistRatio;
stm.Read(uViewDistRatio);
unsigned char uLodRatio;
stm.Read(uLodRatio);
string MatName;
stm.Read(MatName);
bool bHidden=false;
stm.Read(bHidden);
//////////////////////////////////////////////////////////////////////////
VERIFY_COOKIE_NO(stm,76);
string name;
stm.Read(name);
if (m_pLog->GetVerbosityLevel()>=5)
m_pLog->Log("LOADED entity classname %s classid=%02d id=%3id ",pClass->strClassName.c_str(),(int)pClass->ClassId,id);
//////////////////////////////////////////////////////////////////////////
VERIFY_COOKIE_NO(stm,77);
stm.AlignRead();
_SmartScriptObject props(m_pScriptSystem);
LoadProperties(props, stm, m_pScriptSystem, "_root");
_SmartScriptObject propsi(m_pScriptSystem);
LoadProperties(propsi, stm, m_pScriptSystem, "_root");
_SmartScriptObject events(m_pScriptSystem);
LoadProperties(events, stm, m_pScriptSystem, "_root");
VERIFY_COOKIE_NO(stm,78);
//m_pScriptSystem->BeginCall("dump");
//m_pScriptSystem->PushFuncParam(props);
//m_pScriptSystem->EndCall();
//ASSERT(!pEntitySystem->GetEntity(id)); // entity already exists in map
//IEntity* dbgEnt=pEntitySystem->GetEntity(id);
if (pEntitySystem->GetEntity(id))
{
CryError("entity classname %s classid=%d id=%d ALREADY EXISTING IN THE MAP",pClass->strClassName.c_str(),(int)pClass->ClassId,id);
}
ed.pProperties=props;
ed.pPropertiesInstance=propsi;
ed.name = name;
pEnt=pEntitySystem->SpawnEntity(ed);
//ASSERT(pEnt);
if (!pEnt)
CryError("entity classname %s classid=%02d id=%3id CANNOT BE SPAWNED",pClass->strClassName.c_str(),(int)pClass->ClassId,id);
if(id==0) // it's a local player
localPlayerId = ed.id;
//////////////////////////////////////////////////////////////////////////
// render flags
pEnt->SetRndFlags(dwRendFlags);
pEnt->SetScale(fScale);
pEnt->SetViewDistRatio(uViewDistRatio);
pEnt->SetLodRatio(uLodRatio);
if (!MatName.empty())
{
IMatInfo *pMtl = GetSystem()->GetI3DEngine()->FindMaterial(MatName.c_str());
if (pMtl)
pEnt->SetMaterial(pMtl);
}
pEnt->Hide(bHidden);
//////////////////////////////////////////////////////////////////////////
///pEnt->SetName(name.c_str());
IScriptObject *so = pEnt->GetScriptObject();
ASSERT(so);
//so->SetValue("Properties", props);
//so->SetValue("PropertiesInstance", propsi);
so->SetValue("Events", events);
CPlayer *pPlayer=NULL;
if(pEnt->GetContainer()) pEnt->GetContainer()->QueryContainerInterface(CIT_IPLAYER,(void**) &pPlayer);
VERIFY_COOKIE_NO(stm,45);
int health = 0;
stm.Read(health);
VERIFY_COOKIE_NO(stm,46);
if(pPlayer)
{
if(health<=0)
{
pEnt->GetCharInterface()->KillCharacter(0);
#ifdef _DEBUG
m_pLog->Log("DEAD entity classname %s classid=%02d id=%3id ",pClass->strClassName.c_str(),(int)pClass->ClassId,pEnt->GetId());
#endif
//pEntitySystem->RemoveEntity(pEnt->GetId());
};
};
bool b = pEnt->Load(stm,scriptStream.GetScriptObject());
if (!b)
CryError("entity classname %s classid=%02d id=%3id CANNOT BE LOADED",pClass->strClassName.c_str(),(int)pClass->ClassId,id);
//ASSERT(b);
// [anton] proper state serialization was absent BasicEntity.lua,
// we'll have to at least make sure that activated rigid bodies that were initially not active
// don't load velocity from active state
IPhysicalEntity *pPhys = pEnt->GetPhysics();
pe_status_dynamics sd;
if (nVersion<=PATCH1_SAVEVERSION && pPhys && pPhys->GetType()==PE_RIGID && pPhys->GetStatus(&sd) && sd.mass==0)
{
pe_action_set_velocity asv;
asv.v.Set(0,0,0); asv.w.Set(0,0,0);
pPhys->Action(&asv);
}
// update position if ended up under terrain and outdoors
if(pPlayer)
{
if(id==0)
{
I3DEngine *pEngine = m_pSystem->GetI3DEngine();
if (pEngine && pAISystem)
{
Vec3d pos = pEnt->GetPos();
IVisArea *pArea;
int nBuildingid;
if (!pAISystem->CheckInside(pos,nBuildingid,pArea))
{
float newz = pEngine->GetTerrainElevation(pos.x,pos.y);
if (newz>pos.z)
pos.z=newz+0.1f;
pEnt->SetPos(pos);
}
}
}
//[kirill] the OnReset called from OnInit which is called when entity is spawned
// if(health>0)
// {
// // call on reset for the spawned entity
// m_pScriptSystem->BeginCall(pEnt->GetEntityClassName(),"OnReset");
// m_pScriptSystem->PushFuncParam(pEnt->GetScriptObject());
// m_pScriptSystem->EndCall();
// };
pPlayer->LoadGame(stm);
if (pEnt->GetAI())
{
char sName[255];
stm.Read(sName,255);
pEnt->GetAI()->SetName(sName);
}
};
break;
}
case CHUNK_PLAYER:
{
EntityId wPlayerID = 0;
bool b = stm.Read(wPlayerID);
ASSERT(b);
if(wPlayerID==0) // we write 0 for localplayer on save
wPlayerID = localPlayerId;
m_pClient->SetPlayerID(wPlayerID);
pEnt=pEntitySystem->GetEntity(wPlayerID);
// if( wPlayerID==0 )
// pEnt=m_pClient->m_pISystem->GetLocalPlayer();
if (!pEnt)
CryError("player id=%3id NOT FOUND",wPlayerID);
//ASSERT(pEnt);
CPlayer *pPlayer=NULL;
if(pEnt->GetContainer()) pEnt->GetContainer()->QueryContainerInterface(CIT_IPLAYER,(void**) &pPlayer);
ASSERT(pPlayer);
stm.Read(pPlayer->m_bFirstPerson);
SetViewMode(!pPlayer->m_bFirstPerson); // hack, but works
/* [kirill] moved this to int CScriptObjectGame::TouchCheckPoint(IFunctionHandler *pH)
needed to fix quickLoad restoreHealth problem
// do we want to overwrite health with half of maximum
if(p_restorehalfhealth->GetIVal())
{
pPlayer->m_stats.health = 255; // [marco] danger! this should be set by
// gamerules but it gets overwritten by the save checkpoint
//m_pSystem->GetILog()->Log("player health=%d",pPlayer->m_stats.health);
// [kirill]
// this was requested by UBI. It's expected here that current health value is the maximum
//Everytime Jack dies he should respawn with half of his hit points instead of full health.
//Same mechanics for Val, she should get half her hit points everytime Jack respawns.
pPlayer->m_stats.health/=2;
}
if (pPlayer->m_stats.health<128)
pPlayer->m_stats.health = 128;
*/
IEntityCamera *pCam=pEnt->GetCamera();
ASSERT(pCam);
Vec3d vPos;
stm.Read(vPos);
pCam->SetPos(vPos);
//pEnt->SetPos(vPos);
m_pLog->Log("PLAYER %d (%f %f %f) ", wPlayerID, vPos.x, vPos.y, vPos.z);
Vec3d vAngles;
stm.Read(vAngles);
pCam->SetAngles(vAngles);
//[kirill]
//why do we do it here? entity angles are set when entity is loaded
//pEnt->SetAngles(vAngles);
m_pClient->m_PlayerProcessingCmd.SetDeltaAngles(vAngles);
GetSystem()->SetViewCamera( pCam->GetCamera() );
GetSystem()->GetISoundSystem()->SetListener(pCam->GetCamera(),Vec3(0,0,0));
if(!isdemo)
{
CXServer::XSlotMap::iterator itor;
ASSERT(m_pServer->GetSlotsMap().size()==1);
itor = m_pServer->GetSlotsMap().begin();
CXServerSlot *pSSlot=itor->second; // serverslot associated with the player
pSSlot->SetPlayerID(wPlayerID);
pSSlot->SetGameState(CGS_INPROGRESS); // start game imediatly
};
break;
}
case CHUNK_AI:
{
// find for which entity this chunk is
int nID;
stm.Read(nID);
IEntity *pEntity = m_pEntitySystem->GetEntity(nID);
if ( nID == 0 ) // it's local player
pEntity = m_pClient->m_pISystem->GetLocalPlayer();
if (pEntity)
{
m_pLog->Log("Now loading AICHUNK for entity %s", pEntity->GetName());
CPlayer *pPlayer=0;
CVehicle *pVehicle=0;
if (pEntity->GetContainer())
{
if (pEntity->GetContainer()->QueryContainerInterface(CIT_IPLAYER,(void**)&pPlayer))
pPlayer->LoadAIState(stm, scriptStream);
if (pEntity->GetContainer()->QueryContainerInterface(CIT_IVEHICLE,(void**)&pVehicle))
pVehicle->LoadAIState(stm, scriptStream);
}
else if( pEntity->GetAI() )
{
pEntity->GetAI()->Load(stm);
IScriptSystem *pScriptSystem = GetSystem()->GetIScriptSystem();
HSCRIPTFUNCTION loadOverallFunction=NULL;
if( pEntity->GetScriptObject() && pEntity->GetScriptObject()->GetValue("OnLoadOverall", loadOverallFunction) )
{
pScriptSystem->BeginCall(loadOverallFunction);
pScriptSystem->PushFuncParam(pEntity->GetScriptObject());
pScriptSystem->PushFuncParam(scriptStream.GetScriptObject());
pScriptSystem->EndCall();
}
}
}
VERIFY_COOKIE_NO(stm,13);
}
break;
case CHUNK_INGAME_SEQUENCE:
{
#if !defined(LINUX)
IMovieSystem *pMovies = m_pSystem->GetIMovieSystem();
char szName[1024];
stm.Read(szName,1024);
float fTime;
stm.Read(fTime);
IAnimSequence *pSeq = pMovies->FindSequence(szName);
pMovies->PlaySequence(pSeq,false);
pMovies->SetPlayingTime(pSeq,fTime);
#endif
}
break;
case CHUNK_HUD:
{
// load hud/clientstuff viewlayers data
if (m_pUIHud)
{
GetScriptSystem()->BeginCall("Hud", "OnLoad");
GetScriptSystem()->PushFuncParam(m_pUIHud->GetScript());
GetScriptSystem()->PushFuncParam(scriptStream.GetScriptObject());
GetScriptSystem()->EndCall();
}
_SmartScriptObject pClientStuff(m_pScriptSystem,true);
if(m_pScriptSystem->GetGlobalValue("ClientStuff",pClientStuff))
{
m_pScriptSystem->BeginCall("ClientStuff","OnLoad");
m_pScriptSystem->PushFuncParam(pClientStuff);
m_pScriptSystem->PushFuncParam(scriptStream.GetScriptObject());
m_pScriptSystem->EndCall();
}
}
break;
default:
ASSERT(0);
};
if (bLoadBar)
{
pConsole->TickProgressBar(); // advance progress
}
}
{ // [Anton] - allow entities to restore pointer links between them during post load step
// [kirill] restore all the bindings
IEntityItPtr pEntities=pEntitySystem->GetEntityIterator();
pEntities->MoveFirst();
IEntity *pEnt=NULL;
while((pEnt=pEntities->Next())!=NULL)
pEnt->PostLoad();
}
pEntitySystem->Update();
m_pSystem->GetIMovieSystem()->PlayOnLoadSequences(); // yes, we reset this twice, the first time to remove all entity-pointers and now to restore them
m_pClient->Reset();
m_bIsLoadingLevelFromFile = false;
m_pSystem->GetISoundSystem()->Mute(false);
m_bMapLoadedFromCheckpoint=true;
m_pEntitySystem->PauseTimers(false,true);
// m_pLog->Log("HIDE CONSOLE");
m_pRenderer->ClearColorBuffer(Vec3(0,0,0));
m_pSystem->GetIConsole()->ResetProgressBar(0);
m_pSystem->GetIConsole()->ShowConsole(false);
m_pSystem->GetIConsole()->SetScrollMax(600/2);
if (nPreset!=-1)
m_pSystem->GetISoundSystem()->SetEaxListenerEnvironment(nPreset,NULL);
else
m_pSystem->GetISoundSystem()->SetEaxListenerEnvironment(nPreset,&tProps);
GotoGame(1);
m_nDEBUG_TIMING = 0;
return true;
};
//////////////////////////////////////////////////////////////////////////
bool CXGame::Load(string sFileName)
{
m_pSystem->VTuneResume();
assert(g_playerprofile);
string tmp( g_playerprofile->GetString() );
SaveName(sFileName, tmp);
CDefaultStreamAllocator sa;
CStream stm(300, &sa);
int bitslen=m_pSystem->GetCompressedFileSize((char *)sFileName.c_str());
if(bitslen==0)
{
return false;
}
IInput *pInput=GetSystem()->GetIInput();
if(pInput)
pInput->SetMouseExclusive(false);
stm.Resize(bitslen);
int bitsread = m_pSystem->ReadCompressedFile((char *)sFileName.c_str(), stm.GetPtr(), stm.GetAllocatedSize());
if(!bitsread)
{
m_pLog->Log("No such savegame: %s", sFileName.c_str());
if(pInput)
pInput->SetMouseExclusive(true);
return false;
};
stm.SetSize(bitsread);
if (m_pUISystem)
{
m_pUISystem->StopAllVideo();
}
//////////////////////////////////////////////////////////////////////////
// Lock resources before loading checkpoint.
// Speed ups loading a lot.
m_pSystem->GetI3DEngine()->LockCGFResources();
m_pSystem->GetIAnimationSystem()->LockResources();
m_pSystem->GetISoundSystem()->LockResources();
//////////////////////////////////////////////////////////////////////////
bool ok = LoadFromStream(stm, false);
//////////////////////////////////////////////////////////////////////////
// Unlock resources after loading checkpoint.
// Some uneeded resources that were locked before may get released here.
m_pSystem->GetISoundSystem()->UnlockResources();
m_pSystem->GetIAnimationSystem()->UnlockResources();
m_pSystem->GetI3DEngine()->UnlockCGFResources();
//////////////////////////////////////////////////////////////////////////
if(ok)
m_strLastSaveGame = sFileName;
if(pInput)
pInput->SetMouseExclusive(true);
m_pSystem->VTunePause();
//pInput->GetIKeyboard()->ClearKeyState();
if (!IsMultiplayer())
{
_SmartScriptObject pMissionScript(m_pScriptSystem);
m_pScriptSystem->GetGlobalValue("Mission", pMissionScript);
if (((IScriptObject *)pMissionScript) != 0)
{
HSCRIPTFUNCTION pfnOnCheckpointLoaded = 0;
pMissionScript->GetValue("OnCheckpointLoaded", pfnOnCheckpointLoaded);
if (pfnOnCheckpointLoaded)
{
m_pScriptSystem->BeginCall(pfnOnCheckpointLoaded);
m_pScriptSystem->PushFuncParam((IScriptObject*)pMissionScript);
m_pScriptSystem->EndCall();
m_pScriptSystem->ReleaseFunc(pfnOnCheckpointLoaded);
}
}
}
AllowQuicksave(true);
return ok;
}
//////////////////////////////////////////////////////////////////////////
void CXGame::LoadLatest()
{
if(!m_strLastSaveGame.empty())
{
Load(m_strLastSaveGame);
m_pServer->m_pISystem->BindChildren();
//m_pSystem->GetIInput()->GetIKeyboard()->ClearKeyState();
}
};
//////////////////////////////////////////////////////////////////////////
class CCVarSaveDump : public ICVarDumpSink
{
private:
FILE *m_pFile;
public:
CCVarSaveDump(FILE *pFile)
{
m_pFile=pFile;
}
virtual void OnElementFound(ICVar *pCVar)
{
if (pCVar && (pCVar->GetFlags() & VF_DUMPTODISK))
{
string szValue = pCVar->GetString();
int pos;
// replace \ with \\
pos = 1;
for(;;)
{
pos = szValue.find_first_of("\\", pos);
if (pos == string::npos)
{
break;
}
szValue.replace(pos, 1, "\\\\", 2);
pos+=2;
}
// replace " with \"
pos = 1;
for(;;)
{
pos = szValue.find_first_of("\"", pos);
if (pos == string::npos)
{
break;
}
szValue.replace(pos, 1, "\\\"", 2);
pos+=2;
}
string szLine = pCVar->GetName();
szLine += " = \"";
szLine += szValue;
szLine += "\"\r\n";
fputs(szLine.c_str(), m_pFile);
}
}
};
//////////////////////////////////////////////////////////////////////////
class CActionMapDumpSink : public IActionMapDumpSink
{
private:
CXGame *m_pGame;
FILE *m_pFile;
public:
CActionMapDumpSink(CXGame *pGame, FILE *pFile)
{
m_pGame=pGame;
m_pFile=pFile;
fputs("Input:ResetAllBindings();\r\n", m_pFile);
}
virtual void OnElementFound(const char *pszActionMapName, IActionMap *pActionMap)
{
char pszKey[256];
char pszMod[256];
ActionsEnumMap &ActionsMap=m_pGame->GetActionsEnumMap();
ActionsEnumMapItor It=ActionsMap.begin();
while (It!=ActionsMap.end())
{
ActionInfo &Info=It->second;
for (int i=0;i<MAX_BINDS_PER_ACTION;i++)
{
pActionMap->GetBinding(Info.nId, i, pszKey, pszMod);
if (strlen(pszKey))
{
if (strcmp(pszKey, "\\") == 0)
{
strcpy(pszKey, "\\\\");
}
else if (strcmp(pszKey, "\"") == 0)
{
strcpy(pszKey, "\\\"");
}
char szLine[1024] = {0};
sprintf(szLine, "Input:BindAction(\"%s\", \"%s\", \"%s\", %d);\r\n", It->first.c_str(), pszKey, pszActionMapName, i);
fputs(szLine, m_pFile);
}
}
++It;
}
}
};
//////////////////////////////////////////////////////////////////////////
void CXGame::SaveConfiguration( const char *pszSystemCfg,const char *pszGameCfg,const char *sProfileName)
{
if(m_pSystem->IsQuitting())
{
// shutdown the client if there is one (SERVERSYNC vars get restored and SaveConfiguration() can save the right config)
ShutdownClient();
}
else
{
if(IsClient() && !IsServer()) // only a client
assert(0); // shouldn't be - saving config while connected to a client means you save the server synced variables of the server
}
string sSystemCfg = pszSystemCfg;
string sGameCfg = pszGameCfg;
if (sProfileName)
{
sSystemCfg=string("Profiles/Player/")+sProfileName+"_"+sSystemCfg;
sGameCfg=string("Profiles/Player/")+sProfileName+"_"+sGameCfg;
}
FILE *pFile=fxopen(sSystemCfg.c_str(), "wb");
if (pFile)
{
fputs("-- [System-Configuration]\r\n", pFile);
fputs("-- Attention: This file is generated by the system, do not modify! Editing is not recommended! \r\n\r\n", pFile);
CCVarSaveDump SaveDump(pFile);
m_pSystem->GetIConsole()->DumpCVars(&SaveDump);
//m_pConsole->DumpCVars(&SaveDump);
fclose(pFile);
}
if (m_pIActionMapManager)
{
pFile=fxopen(sGameCfg.c_str(), "wb");
if (pFile)
{
fputs("-- [Game-Configuration]\r\n", pFile);
fputs("-- Attention: This file will be overwritten when updated, so dont add lines ! Editing is not recommended !\r\n\r\n", pFile);
CActionMapDumpSink SaveActionMaps(this, pFile);
m_pIActionMapManager->GetActionMaps(&SaveActionMaps);
char sValue[32];
sprintf(sValue, "%4.4f", m_pSystem->GetIInput()->GetIMouse()->GetSensitvity());
fputs(string(string("Input:SetMouseSensitivity(")+string(sValue)+string(");\r\n")).c_str(), pFile);
m_pIActionMapManager->GetInvertedMouse() ? strcpy(sValue, "1") : strcpy(sValue, "nil");
fputs(string(string("Input:SetInvertedMouse(")+string(sValue)+string(");\r\n")).c_str(), pFile);
//Input:BindCommandToKey("#System:ShowDebugger();", "f8", 1);
//fputs("Input:BindCommandToKey(\"#System:ShowDebugger();\", \"f8\", 1);\r\n" ,pFile);
//Input:BindCommandToKey("\\SkipCutScene","F7",1);
fputs("Input:BindCommandToKey(\"\\\\SkipCutScene\",\"F7\",1);\r\n",pFile);
fputs("Input:BindCommandToKey(\"\\\\SkipCutScene\",\"spacebar\",1);\r\n",pFile);
fclose(pFile);
}
}
}
//////////////////////////////////////////////////////////////////////////
void CXGame::LoadConfiguration(const string &sSystemCfg,const string &sGameCfg)
{
m_pSystem->LoadConfiguration(sSystemCfg);
FILE *pFile=fxopen(sGameCfg.c_str(), "rb");
if (!pFile)
{
// if for some reason the game config is not found
// (first time, new installation etc.)
char szBuffer[512];
strcpy(szBuffer,"Input:BindCommandToKey(\"\\\\SkipCutScene\",\"F7\",1);");
m_pSystem->GetIScriptSystem()->ExecuteBuffer(szBuffer,strlen(szBuffer));
strcpy(szBuffer,"Input:BindCommandToKey(\"\\\\SkipCutScene\",\"spacebar\",1);");
m_pSystem->GetIScriptSystem()->ExecuteBuffer(szBuffer,strlen(szBuffer));
return;
}
char szLine[512];
char szBuffer[512];
while (fgets(szLine,512,pFile))
{
// skip comments
if (szLine[0]=='-')
continue;
// extract command
if (!strstr(szLine,";"))
continue;
// check for malicious commands
bool bValid=false;
if (strstr(szLine,"#"))
{
// someone is trying to bind script code
// to a key - silently skip this line
continue;
}
else
if (strstr(szLine,"Input:ResetAllBindings"))
{
// valid command
bValid=true;
}
else
if (strstr(szLine,"Input:BindAction"))
{
// valid command
bValid=true;
}
else
if (strstr(szLine,"Input:SetMouseSensitivity"))
{
// valid command
bValid=true;
}
else
if (strstr(szLine,"Input:SetInvertedMouse"))
{
// valid command
bValid=true;
}
else
if (strstr(szLine,"Input:BindCommandToKey"))
{
//if (strstr(szLine,"SkipCutScene"))
// valid command
bValid=true;
}
if (bValid)
{
strcpy(szBuffer,szLine);
m_pSystem->GetIScriptSystem()->ExecuteBuffer(szBuffer,strlen(szBuffer));
}
else
{
m_pSystem->GetILog()->Log("Invalid game cfg:%s",szLine);
}
}
fclose(pFile);
//m_pScriptSystem->ExecuteFile(sGameCfg.c_str(), false);
//m_pScriptSystem->ExecuteFile("GameCfgOverride.Lua",false); //?
}
//////////////////////////////////////////////////////////////////////////
void CXGame::RemoveConfiguration(string &sSystemCfg,string &sGameCfg,const char *sProfileName)
{
if (sProfileName)
{
sSystemCfg=string("Profiles/Player/")+sProfileName+"_"+sSystemCfg;
sGameCfg=string("Profiles/Player/")+sProfileName+"_"+sGameCfg;
}
#if defined(LINUX)
remove( sSystemCfg.c_str() );
remove( sGameCfg.c_str() );
#else
DeleteFile(sSystemCfg.c_str());
DeleteFile(sGameCfg.c_str());
#endif
// remove the folder
string path = "profiles/player/";
path += sProfileName;
path += "/";
m_pSystem->Deltree(path.c_str(), 1);
}
//////////////////////////////////////////////////////////////////////////
//[PETAR]
// DO NOT UPDATE THIS FUNCTION ITS HERE TO ENABLE THAT ALL NEWER VERSIONS
// SUPPORT LOADING OLDER SAVEFILES
bool CXGame::LoadFromStream_RELEASEVERSION(CStream &stm, bool isdemo, CScriptObjectStream &scriptStream)
{
IBitStream *pBitStream=GetIBitStream();
IEntitySystem *pEntitySystem=m_pSystem->GetIEntitySystem();
IEntityClassRegistry *pECR=GetClassRegistry();
IEntity *pEnt=NULL;
CEntityDesc ed;
int localPlayerId=0;
string sLevelName;
string sMissionName;
stm.Read(sLevelName);
stm.Read(sMissionName);
// read dummy save date and time
unsigned char bDummy;
unsigned short wDummy;
stm.Read(bDummy); // hour
stm.Read(bDummy); // minute
stm.Read(bDummy); // second
stm.Read(bDummy); // day
stm.Read(bDummy); // month
stm.Read(wDummy); // year
// load savegame name
string sFilename;
stm.Read(sFilename);
// load the number of entities, for loading screen bar
int nEnt;
stm.Read(nEnt);
bool bS=IsServer();
bool bC=IsClient();
assert(!IsMultiplayer());
VERIFY_COOKIE_NO(stm,0x22);
// Load EAX preset
int nPreset;
CS_REVERB_PROPERTIES tProps;
m_pSystem->GetISoundSystem()->GetCurrentEaxEnvironment(nPreset,tProps);
stm.Read(nPreset);
if (nPreset==-1)
{
stm.ReadBits((BYTE *)&tProps,sizeof(CS_REVERB_PROPERTIES));
}
VERIFY_COOKIE_NO(stm,0x12);
// load saved cvars
string varname,val;
int nCount,i;
stm.Read(nCount);
IConsole *pCon=m_pSystem->GetIConsole();
for (i=0;i<nCount;i++)
{
if(stm.Read(varname))
if(stm.Read(val))
{
ICVar *pCVar=m_pSystem->GetIConsole()->GetCVar(varname.c_str());
if (!pCVar)
{
m_pSystem->GetILog()->Log("\001 WARNING, CVar %s(%s) was saved but is not present",varname.c_str(),val.c_str());
}
else
pCVar->Set(val.c_str());
}
else
{
m_pSystem->GetILog()->LogError("CXGame::LoadFromStream %d/%d critical error",i,nCount);
stm.Debug();
m_bIsLoadingLevelFromFile = false;
return false;
}
} //i
if(m_pSystem->GetISoundSystem())
m_pSystem->GetISoundSystem()->Silence();
m_pSystem->GetISoundSystem()->Mute(true);
bool bLoadBar = false;
IConsole *pConsole = m_pSystem->GetIConsole();
assert(pConsole);
if(stricmp(g_LevelName->GetString(),sLevelName.c_str()) != 0 || !m_pServer || (stricmp(m_pServer->m_GameContext.strMission.c_str(),sMissionName.c_str()) != 0))
{
m_pLog->LogToConsole("Loading %s / %s", sLevelName.c_str(), sMissionName.c_str());
if (m_pServer)
ShutdownServer();
if (isdemo)
{
//StartupClient();
m_pClient->DemoConnect();
LoadLevelCS( false,sLevelName.c_str(), sMissionName.c_str(), false);
}
else
{
GetISystem()->GetIInput()->EnableEventPosting(false);
m_pSystem->GetIInput()->GetIKeyboard()->ClearKeyState();
LoadLevelCS(false,sLevelName.c_str(), sMissionName.c_str(), false);
//m_pClient->Connect("localhost");
m_pSystem->GetIInput()->GetIKeyboard()->ClearKeyState();
GetISystem()->GetIInput()->EnableEventPosting(true);
};
}
else
{
bLoadBar = 1;
string sLoadingScreenTexture = m_currentLevelFolder + "/loadscreen_" + m_currentLevel + ".dds";
pConsole->SetLoadingImage( sLoadingScreenTexture.c_str() );
pConsole->Clear();
pConsole->ResetProgressBar(nEnt + 3);
pConsole->SetScrollMax(600);
pConsole->ShowConsole(1);
DeleteMessage("Switch"); // no switching during loading
// local player has to exit all areas before starting to delete entities
IEntity *pIMyPlayer = GetMyPlayer();
if( pIMyPlayer )
{
IEntityContainer *pIContainer = pIMyPlayer->GetContainer();
if (pIContainer)
{
CPlayer *pPlayer = NULL;
if (pIContainer->QueryContainerInterface(CIT_IPLAYER, (void**)&pPlayer))
m_XAreaMgr.ExitAllAreas( pPlayer->m_AreaUser );
}
}
m_pSystem->GetI3DEngine()->RestoreTerrainFromDisk();
m_pSystem->GetIMovieSystem()->Reset( false );
m_pLog->Log("REMOVING entities:");
IEntityItPtr pEntities=pEntitySystem->GetEntityIterator();
pEntities->MoveFirst();
IEntity *pEnt=NULL;
while((pEnt=pEntities->Next())!=NULL)
{
EntityClass *pClass=pECR->GetByClass(pEnt->GetEntityClassName());
if (m_pWeaponSystemEx->IsProjectileClass(pClass->ClassId)) continue;
#ifdef _DEBUG
m_pLog->Log("REMOVING entity classname %s classid=%02d id=%3id ",pClass->strClassName.c_str(),(int)pClass->ClassId,pEnt->GetId());
#endif
pEntitySystem->RemoveEntity(pEnt->GetId());
}
pConsole->TickProgressBar(); // advance progress
pEntitySystem->Update();
SoftReset();
m_pEntitySystem->Reset();
pConsole->TickProgressBar(); // advance progress
}
// PETAR lets delete all guys since they will be spawned anyway
m_pSystem->GetAISystem()->Reset();
IAISystem *pAISystem = m_pSystem->GetAISystem();
// adaptive balancing
if (pAISystem)
{
if (cv_game_Difficulty->GetIVal())
{
float fAcc,fAgg,fHealth;
if (pAISystem->GetAutoBalanceInterface())
{
pAISystem->GetAutoBalanceInterface()->GetMultipliers(fAcc,fAgg,fHealth);
cv_game_Aggression->Set(fAgg);
cv_game_Accuracy->Set(fAcc);
cv_game_Health->Set(fHealth);
}
}
}
//[petar] load autobalancing stuff
int nAllowedDeaths;
stm.Read(nAllowedDeaths);
pAISystem->GetAutoBalanceInterface()->SetAllowedDeathCount(nAllowedDeaths);
// apparently these 20 updates are needed to setup everything,
// from hud to netwrok stream etc. do not remove it
// or savegame won't load
for (int i = 0; i<20; i++)
Update();
if (bLoadBar)
{
pConsole->TickProgressBar(); // advance progress
}
VERIFY_COOKIE_NO(stm,0x3c);
// loading reserver IDs for dynacally created saved entities
int dynReservedIDsNumber=0;
stm.Read(dynReservedIDsNumber);
for( ; dynReservedIDsNumber>0; --dynReservedIDsNumber )
{
int reservedId;
stm.Read((int&)reservedId);
pEntitySystem->MarkId( reservedId );
}
VERIFY_COOKIE_NO(stm,0x73);
// load hud objectives
if (m_pUIHud)
{
GetScriptSystem()->BeginCall("Hud", "OnLoadOld");
GetScriptSystem()->PushFuncParam(m_pUIHud->GetScript());
GetScriptSystem()->PushFuncParam(scriptStream.GetScriptObject());
GetScriptSystem()->EndCall();
}
VERIFY_COOKIE_NO(stm,61);
while (!stm.EOS())
{
BYTE cChunk=0;
stm.Read(cChunk);
switch(cChunk)
{
case CHUNK_ENTITY:
{
EntityId id;
EntityClassId ClassID;
pBitStream->ReadBitStream(stm,ClassID,eEntityClassId);
EntityClass *pClass=pECR->GetByClassId(ClassID);
ASSERT(pClass);
ed.className=pClass->strClassName;
ed.ClassId=pClass->ClassId;
stm.Read(id);
ed.id=id;
// [kirill] if this entity was dynamicly created - ID was marked when dynReservedIDsNumber loaded, to prevent
// from being taked by some other dynamically spawned non-saved entity. So now we load it and let's free the id
if (pEntitySystem->IsDynamicEntityId( id ))
pEntitySystem->ClearId(id);
// position and angles are read later -
// with pEnt->Load(
//////////////////////////////////////////////////////////////////////////
//[marco] position and angles must be read before spawining the entity - must
// be consistent with load level!
Vec3d vPos,vAngles;
if (!stm.Read(vPos))
CryError("Error while reading position for entity id=%d",id);
if (!stm.Read(vAngles))
CryError("Error while reading position for entity id=%d",id);
ed.pos=vPos;
ed.angles=vAngles;
//////////////////////////////////////////////////////////////////////////
// renderer stuff
float fScale;
stm.Read(fScale);
int dwRendFlags;
stm.Read(dwRendFlags);
unsigned char uViewDistRatio;
stm.Read(uViewDistRatio);
unsigned char uLodRatio;
stm.Read(uLodRatio);
string MatName;
stm.Read(MatName);
bool bHidden=false;
stm.Read(bHidden);
//////////////////////////////////////////////////////////////////////////
VERIFY_COOKIE_NO(stm,76);
string name;
stm.Read(name);
if (m_pLog->GetVerbosityLevel()>=5)
m_pLog->Log("LOADED entity classname %s classid=%02d id=%3id ",pClass->strClassName.c_str(),(int)pClass->ClassId,id);
//////////////////////////////////////////////////////////////////////////
VERIFY_COOKIE_NO(stm,77);
stm.AlignRead();
_SmartScriptObject props(m_pScriptSystem);
LoadProperties(props, stm, m_pScriptSystem, "_root");
_SmartScriptObject propsi(m_pScriptSystem);
LoadProperties(propsi, stm, m_pScriptSystem, "_root");
_SmartScriptObject events(m_pScriptSystem);
LoadProperties(events, stm, m_pScriptSystem, "_root");
VERIFY_COOKIE_NO(stm,78);
//m_pScriptSystem->BeginCall("dump");
//m_pScriptSystem->PushFuncParam(props);
//m_pScriptSystem->EndCall();
//ASSERT(!pEntitySystem->GetEntity(id)); // entity already exists in map
//IEntity* dbgEnt=pEntitySystem->GetEntity(id);
if (pEntitySystem->GetEntity(id))
{
CryError("entity classname %s classid=%d id=%d ALREADY EXISTING IN THE MAP",pClass->strClassName.c_str(),(int)pClass->ClassId,id);
}
ed.pProperties=props;
ed.pPropertiesInstance=propsi;
ed.name = name;
pEnt=pEntitySystem->SpawnEntity(ed);
//ASSERT(pEnt);
if (!pEnt)
CryError("entity classname %s classid=%02d id=%3id CANNOT BE SPAWNED",pClass->strClassName.c_str(),(int)pClass->ClassId,id);
if(id==0) // it's a local player
localPlayerId = ed.id;
//////////////////////////////////////////////////////////////////////////
// render flags
pEnt->SetRndFlags(dwRendFlags);
pEnt->SetScale(fScale);
pEnt->SetViewDistRatio(uViewDistRatio);
pEnt->SetLodRatio(uLodRatio);
if (!MatName.empty())
{
IMatInfo *pMtl = GetSystem()->GetI3DEngine()->FindMaterial(MatName.c_str());
if (pMtl)
pEnt->SetMaterial(pMtl);
}
pEnt->Hide(bHidden);
//////////////////////////////////////////////////////////////////////////
///pEnt->SetName(name.c_str());
IScriptObject *so = pEnt->GetScriptObject();
ASSERT(so);
//so->SetValue("Properties", props);
//so->SetValue("PropertiesInstance", propsi);
so->SetValue("Events", events);
CPlayer *pPlayer=NULL;
if(pEnt->GetContainer()) pEnt->GetContainer()->QueryContainerInterface(CIT_IPLAYER,(void**) &pPlayer);
VERIFY_COOKIE_NO(stm,45);
int health = 0;
stm.Read(health);
VERIFY_COOKIE_NO(stm,46);
if(pPlayer)
{
if(health<=0)
{
pEnt->GetCharInterface()->KillCharacter(0);
#ifdef _DEBUG
m_pLog->Log("DEAD entity classname %s classid=%02d id=%3id ",pClass->strClassName.c_str(),(int)pClass->ClassId,pEnt->GetId());
#endif
//pEntitySystem->RemoveEntity(pEnt->GetId());
};
};
bool b = pEnt->LoadRELEASE(stm,scriptStream.GetScriptObject());
if (!b)
CryError("entity classname %s classid=%02d id=%3id CANNOT BE LOADED",pClass->strClassName.c_str(),(int)pClass->ClassId,id);
//ASSERT(b);
// [anton] proper state serialization was absent BasicEntity.lua,
// we'll have to at least make sure that activated rigid bodies that were initially not active
// don't load velocity from active state
IPhysicalEntity *pPhys = pEnt->GetPhysics();
pe_status_dynamics sd;
if (pPhys && pPhys->GetType()==PE_RIGID && pPhys->GetStatus(&sd) && sd.mass==0)
{
pe_action_set_velocity asv;
asv.v.Set(0,0,0); asv.w.Set(0,0,0);
pPhys->Action(&asv);
}
if(pPlayer)
{
//[kirill] the OnReset called from OnInit which is called when entity is spawned
// if(health>0)
// {
// // call on reset for the spawned entity
// m_pScriptSystem->BeginCall(pEnt->GetEntityClassName(),"OnReset");
// m_pScriptSystem->PushFuncParam(pEnt->GetScriptObject());
// m_pScriptSystem->EndCall();
// };
pPlayer->LoadGame_PATCH_1(stm);
};
break;
}
case CHUNK_PLAYER:
{
EntityId wPlayerID = 0;
bool b = stm.Read(wPlayerID);
ASSERT(b);
if(wPlayerID==0) // we write 0 for localplayer on save
wPlayerID = localPlayerId;
m_pClient->SetPlayerID(wPlayerID);
pEnt=pEntitySystem->GetEntity(wPlayerID);
// if( wPlayerID==0 )
// pEnt=m_pClient->m_pISystem->GetLocalPlayer();
if (!pEnt)
CryError("player id=%3id NOT FOUND",wPlayerID);
//ASSERT(pEnt);
CPlayer *pPlayer=NULL;
if(pEnt->GetContainer()) pEnt->GetContainer()->QueryContainerInterface(CIT_IPLAYER,(void**) &pPlayer);
ASSERT(pPlayer);
stm.Read(pPlayer->m_bFirstPerson);
SetViewMode(!pPlayer->m_bFirstPerson); // hack, but works
// do we want to overwrite health with half of maximum
if(p_restorehalfhealth->GetIVal())
{
pPlayer->m_stats.health = 255; // [marco] danger! this should be set by
// gamerules but it gets overwritten by the save checkpoint
//m_pSystem->GetILog()->Log("player health=%d",pPlayer->m_stats.health);
// [kirill]
// this was requested by UBI. It's expected here that current health value is the maximum
//Everytime Jack dies he should respawn with half of his hit points instead of full health.
//Same mechanics for Val, she should get half her hit points everytime Jack respawns.
pPlayer->m_stats.health/=2;
}
if (pPlayer->m_stats.health<128)
pPlayer->m_stats.health = 128;
IEntityCamera *pCam=pEnt->GetCamera();
ASSERT(pCam);
Vec3d vPos;
stm.Read(vPos);
pCam->SetPos(vPos);
pEnt->SetPos(vPos);
m_pLog->Log("PLAYER %d (%f %f %f) ", wPlayerID, vPos.x, vPos.y, vPos.z);
Vec3d vAngles;
stm.Read(vAngles);
pCam->SetAngles(vAngles);
pEnt->SetAngles(vAngles);
m_pClient->m_PlayerProcessingCmd.SetDeltaAngles(vAngles);
GetSystem()->SetViewCamera( pCam->GetCamera() );
GetSystem()->GetISoundSystem()->SetListener(pCam->GetCamera(),Vec3(0,0,0));
if(!isdemo)
{
CXServer::XSlotMap::iterator itor;
ASSERT(m_pServer->GetSlotsMap().size()==1);
itor = m_pServer->GetSlotsMap().begin();
CXServerSlot *pSSlot=itor->second; // serverslot associated with the player
pSSlot->SetPlayerID(wPlayerID);
pSSlot->SetGameState(CGS_INPROGRESS); // start game imediatly
};
break;
}
case CHUNK_AI:
{
// find for which entity this chunk is
int nID;
stm.Read(nID);
IEntity *pEntity = m_pEntitySystem->GetEntity(nID);
if (pEntity)
{
CPlayer *pPlayer=0;
if (pEntity->GetContainer())
{
if (pEntity->GetContainer()->QueryContainerInterface(CIT_IPLAYER,(void**)&pPlayer))
pPlayer->LoadAIState_RELEASE(stm);
}
}
VERIFY_COOKIE_NO(stm,13);
}
break;
default:
ASSERT(0);
};
if (bLoadBar)
{
pConsole->TickProgressBar(); // advance progress
}
}
{ // [Anton] - allow entities to restore pointer links between them during post load step
// [kirill] restore all the bindings
IEntityItPtr pEntities=pEntitySystem->GetEntityIterator();
pEntities->MoveFirst();
IEntity *pEnt=NULL;
while((pEnt=pEntities->Next())!=NULL)
pEnt->PostLoad();
}
pEntitySystem->Update();
m_pSystem->GetIMovieSystem()->PlayOnLoadSequences(); // yes, we reset this twice, the first time to remove all entity-pointers and now to restore them
m_pClient->Reset();
m_bIsLoadingLevelFromFile = false;
m_pSystem->GetISoundSystem()->Mute(false);
m_bMapLoadedFromCheckpoint=true;
m_pEntitySystem->PauseTimers(false,true);
// m_pLog->Log("HIDE CONSOLE");
m_pRenderer->ClearColorBuffer(Vec3(0,0,0));
m_pSystem->GetIConsole()->ResetProgressBar(0);
m_pSystem->GetIConsole()->ShowConsole(false);
m_pSystem->GetIConsole()->SetScrollMax(600/2);
if (nPreset!=-1)
m_pSystem->GetISoundSystem()->SetEaxListenerEnvironment(nPreset,NULL);
else
m_pSystem->GetISoundSystem()->SetEaxListenerEnvironment(nPreset,&tProps);
GotoGame(1);
return true;
}
//////////////////////////////////////////////////////////////////////////
//[KIRILL]
// DO NOT UPDATE THIS FUNCTION ITS HERE TO ENABLE THAT ALL NEWER VERSIONS
// SUPPORT LOADING OLDER SAVEFILES - from PATCH 1
bool CXGame::LoadFromStream_PATCH_1(CStream &stm, bool isdemo, CScriptObjectStream &scriptStream)
{
IBitStream *pBitStream=GetIBitStream();
IEntitySystem *pEntitySystem=m_pSystem->GetIEntitySystem();
IEntityClassRegistry *pECR=GetClassRegistry();
IEntity *pEnt=NULL;
CEntityDesc ed;
int localPlayerId=0;
string sLevelName;
string sMissionName;
stm.Read(sLevelName);
stm.Read(sMissionName);
// read dummy save date and time
unsigned char bDummy;
unsigned short wDummy;
stm.Read(bDummy); // hour
stm.Read(bDummy); // minute
stm.Read(bDummy); // second
stm.Read(bDummy); // day
stm.Read(bDummy); // month
stm.Read(wDummy); // year
// load savegame name
string sFilename;
stm.Read(sFilename);
// load the number of entities, for loading screen bar
int nEnt;
stm.Read(nEnt);
bool bS=IsServer();
bool bC=IsClient();
assert(!IsMultiplayer());
VERIFY_COOKIE_NO(stm,0x22);
// Load EAX preset
int nPreset;
CS_REVERB_PROPERTIES tProps;
m_pSystem->GetISoundSystem()->GetCurrentEaxEnvironment(nPreset,tProps);
stm.Read(nPreset);
if (nPreset==-1)
{
stm.ReadBits((BYTE *)&tProps,sizeof(CS_REVERB_PROPERTIES));
}
VERIFY_COOKIE_NO(stm,0x12);
// load saved cvars
string varname,val;
int nCount,i;
stm.Read(nCount);
IConsole *pCon=m_pSystem->GetIConsole();
for (i=0;i<nCount;i++)
{
if(stm.Read(varname))
if(stm.Read(val))
{
ICVar *pCVar=m_pSystem->GetIConsole()->GetCVar(varname.c_str());
if (!pCVar)
{
m_pSystem->GetILog()->Log("\001 WARNING, CVar %s(%s) was saved but is not present",varname.c_str(),val.c_str());
}
else
pCVar->Set(val.c_str());
}
else
{
m_pSystem->GetILog()->LogError("CXGame::LoadFromStream %d/%d critical error",i,nCount);
stm.Debug();
m_bIsLoadingLevelFromFile = false;
return false;
}
} //i
if(m_pSystem->GetISoundSystem())
m_pSystem->GetISoundSystem()->Silence();
m_pSystem->GetISoundSystem()->Mute(true);
bool bLoadBar = false;
IConsole *pConsole = m_pSystem->GetIConsole();
assert(pConsole);
if(stricmp(g_LevelName->GetString(),sLevelName.c_str()) != 0 || !m_pServer || (stricmp(m_pServer->m_GameContext.strMission.c_str(),sMissionName.c_str()) != 0))
{
m_pLog->LogToConsole("Loading %s / %s", sLevelName.c_str(), sMissionName.c_str());
if (m_pServer)
ShutdownServer();
if (isdemo)
{
//StartupClient();
m_pClient->DemoConnect();
LoadLevelCS( false,sLevelName.c_str(), sMissionName.c_str(), false);
}
else
{
GetISystem()->GetIInput()->EnableEventPosting(false);
//m_pSystem->GetIInput()->GetIKeyboard()->ClearKeyState();
LoadLevelCS(false,sLevelName.c_str(), sMissionName.c_str(), false);
//m_pClient->Connect("localhost");
//m_pSystem->GetIInput()->GetIKeyboard()->ClearKeyState();
GetISystem()->GetIInput()->EnableEventPosting(true);
};
}
else
{
bLoadBar = 1;
string sLoadingScreenTexture = m_currentLevelFolder + "/loadscreen_" + m_currentLevel + ".dds";
pConsole->SetLoadingImage( sLoadingScreenTexture.c_str() );
pConsole->Clear();
pConsole->ResetProgressBar(nEnt + 3);
pConsole->SetScrollMax(600);
pConsole->ShowConsole(1);
DeleteMessage("Switch"); // no switching during loading
// local player has to exit all areas before starting to delete entities
IEntity *pIMyPlayer = GetMyPlayer();
if( pIMyPlayer )
{
IEntityContainer *pIContainer = pIMyPlayer->GetContainer();
if (pIContainer)
{
CPlayer *pPlayer = NULL;
if (pIContainer->QueryContainerInterface(CIT_IPLAYER, (void**)&pPlayer))
m_XAreaMgr.ExitAllAreas( pPlayer->m_AreaUser );
}
}
m_pSystem->GetI3DEngine()->RestoreTerrainFromDisk();
m_pSystem->GetIMovieSystem()->Reset( false );
m_pLog->Log("REMOVING entities:");
IEntityItPtr pEntities=pEntitySystem->GetEntityIterator();
pEntities->MoveFirst();
IEntity *pEnt=NULL;
while((pEnt=pEntities->Next())!=NULL)
{
EntityClass *pClass=pECR->GetByClass(pEnt->GetEntityClassName());
if (m_pWeaponSystemEx->IsProjectileClass(pClass->ClassId)) continue;
#ifdef _DEBUG
m_pLog->Log("REMOVING entity classname %s classid=%02d id=%3id ",pClass->strClassName.c_str(),(int)pClass->ClassId,pEnt->GetId());
#endif
pEntitySystem->RemoveEntity(pEnt->GetId());
}
pConsole->TickProgressBar(); // advance progress
pEntitySystem->Update();
SoftReset();
m_pEntitySystem->Reset();
pConsole->TickProgressBar(); // advance progress
}
// PETAR lets delete all guys since they will be spawned anyway
m_pSystem->GetAISystem()->Reset();
IAISystem *pAISystem = m_pSystem->GetAISystem();
// adaptive balancing
if (pAISystem)
{
if (cv_game_Difficulty->GetIVal())
{
float fAcc,fAgg,fHealth;
if (pAISystem->GetAutoBalanceInterface())
{
pAISystem->GetAutoBalanceInterface()->GetMultipliers(fAcc,fAgg,fHealth);
cv_game_Aggression->Set(fAgg);
cv_game_Accuracy->Set(fAcc);
cv_game_Health->Set(fHealth);
}
}
}
//[petar] load autobalancing stuff
int nAllowedDeaths;
stm.Read(nAllowedDeaths);
pAISystem->GetAutoBalanceInterface()->SetAllowedDeathCount(nAllowedDeaths);
// apparently these 20 updates are needed to setup everything,
// from hud to netwrok stream etc. do not remove it
// or savegame won't load
for (int i = 0; i<20; i++)
Update();
if (bLoadBar)
{
pConsole->TickProgressBar(); // advance progress
}
VERIFY_COOKIE_NO(stm,0x3c);
// loading reserver IDs for dynacally created saved entities
int dynReservedIDsNumber=0;
stm.Read(dynReservedIDsNumber);
for( ; dynReservedIDsNumber>0; --dynReservedIDsNumber )
{
int reservedId;
stm.Read((int&)reservedId);
pEntitySystem->MarkId( reservedId );
}
// only load this in case of older save
VERIFY_COOKIE_NO(stm,0x73);
// load hud objectives
if (m_pUIHud)
{
GetScriptSystem()->BeginCall("Hud", "OnLoadOld");
GetScriptSystem()->PushFuncParam(m_pUIHud->GetScript());
GetScriptSystem()->PushFuncParam(scriptStream.GetScriptObject());
GetScriptSystem()->EndCall();
}
VERIFY_COOKIE_NO(stm,61);
while (!stm.EOS())
{
BYTE cChunk=0;
stm.Read(cChunk);
switch(cChunk)
{
case CHUNK_ENTITY:
{
EntityId id;
EntityClassId ClassID;
pBitStream->ReadBitStream(stm,ClassID,eEntityClassId);
EntityClass *pClass=pECR->GetByClassId(ClassID);
ASSERT(pClass);
ed.className=pClass->strClassName;
ed.ClassId=pClass->ClassId;
stm.Read(id);
ed.id=id;
// [kirill] if this entity was dynamicly created - ID was marked when dynReservedIDsNumber loaded, to prevent
// from being taked by some other dynamically spawned non-saved entity. So now we load it and let's free the id
if (pEntitySystem->IsDynamicEntityId( id ))
pEntitySystem->ClearId(id);
// position and angles are read later -
// with pEnt->Load(
//////////////////////////////////////////////////////////////////////////
//[marco] position and angles must be read before spawining the entity - must
// be consistent with load level!
Vec3d vPos,vAngles;
if (!stm.Read(vPos))
CryError("Error while reading position for entity id=%d",id);
if (!stm.Read(vAngles))
CryError("Error while reading position for entity id=%d",id);
ed.pos=vPos;
ed.angles=vAngles;
//////////////////////////////////////////////////////////////////////////
// renderer stuff
float fScale;
stm.Read(fScale);
int dwRendFlags;
stm.Read(dwRendFlags);
unsigned char uViewDistRatio;
stm.Read(uViewDistRatio);
unsigned char uLodRatio;
stm.Read(uLodRatio);
string MatName;
stm.Read(MatName);
bool bHidden=false;
stm.Read(bHidden);
//////////////////////////////////////////////////////////////////////////
VERIFY_COOKIE_NO(stm,76);
string name;
stm.Read(name);
if (m_pLog->GetVerbosityLevel()>=5)
m_pLog->Log("LOADED entity classname %s classid=%02d id=%3id ",pClass->strClassName.c_str(),(int)pClass->ClassId,id);
//////////////////////////////////////////////////////////////////////////
VERIFY_COOKIE_NO(stm,77);
stm.AlignRead();
_SmartScriptObject props(m_pScriptSystem);
LoadProperties(props, stm, m_pScriptSystem, "_root");
_SmartScriptObject propsi(m_pScriptSystem);
LoadProperties(propsi, stm, m_pScriptSystem, "_root");
_SmartScriptObject events(m_pScriptSystem);
LoadProperties(events, stm, m_pScriptSystem, "_root");
VERIFY_COOKIE_NO(stm,78);
//m_pScriptSystem->BeginCall("dump");
//m_pScriptSystem->PushFuncParam(props);
//m_pScriptSystem->EndCall();
//ASSERT(!pEntitySystem->GetEntity(id)); // entity already exists in map
//IEntity* dbgEnt=pEntitySystem->GetEntity(id);
if (pEntitySystem->GetEntity(id))
{
CryError("entity classname %s classid=%d id=%d ALREADY EXISTING IN THE MAP",pClass->strClassName.c_str(),(int)pClass->ClassId,id);
}
ed.pProperties=props;
ed.pPropertiesInstance=propsi;
ed.name = name;
pEnt=pEntitySystem->SpawnEntity(ed);
//ASSERT(pEnt);
if (!pEnt)
CryError("entity classname %s classid=%02d id=%3id CANNOT BE SPAWNED",pClass->strClassName.c_str(),(int)pClass->ClassId,id);
if(id==0) // it's a local player
localPlayerId = ed.id;
//////////////////////////////////////////////////////////////////////////
// render flags
pEnt->SetRndFlags(dwRendFlags);
pEnt->SetScale(fScale);
pEnt->SetViewDistRatio(uViewDistRatio);
pEnt->SetLodRatio(uLodRatio);
if (!MatName.empty())
{
IMatInfo *pMtl = GetSystem()->GetI3DEngine()->FindMaterial(MatName.c_str());
if (pMtl)
pEnt->SetMaterial(pMtl);
}
pEnt->Hide(bHidden);
//////////////////////////////////////////////////////////////////////////
///pEnt->SetName(name.c_str());
IScriptObject *so = pEnt->GetScriptObject();
ASSERT(so);
//so->SetValue("Properties", props);
//so->SetValue("PropertiesInstance", propsi);
so->SetValue("Events", events);
CPlayer *pPlayer=NULL;
if(pEnt->GetContainer()) pEnt->GetContainer()->QueryContainerInterface(CIT_IPLAYER,(void**) &pPlayer);
VERIFY_COOKIE_NO(stm,45);
int health = 0;
stm.Read(health);
VERIFY_COOKIE_NO(stm,46);
if(pPlayer)
{
if(health<=0)
{
pEnt->GetCharInterface()->KillCharacter(0);
#ifdef _DEBUG
m_pLog->Log("DEAD entity classname %s classid=%02d id=%3id ",pClass->strClassName.c_str(),(int)pClass->ClassId,pEnt->GetId());
#endif
//pEntitySystem->RemoveEntity(pEnt->GetId());
};
};
bool b = pEnt->LoadPATCH1(stm,scriptStream.GetScriptObject());
if (!b)
CryError("entity classname %s classid=%02d id=%3id CANNOT BE LOADED",pClass->strClassName.c_str(),(int)pClass->ClassId,id);
//ASSERT(b);
// [anton] proper state serialization was absent BasicEntity.lua,
// we'll have to at least make sure that activated rigid bodies that were initially not active
// don't load velocity from active state
IPhysicalEntity *pPhys = pEnt->GetPhysics();
pe_status_dynamics sd;
if (pPhys && pPhys->GetType()==PE_RIGID && pPhys->GetStatus(&sd) && sd.mass==0)
{
pe_action_set_velocity asv;
asv.v.Set(0,0,0); asv.w.Set(0,0,0);
pPhys->Action(&asv);
}
// update position if ended up under terrain and outdoors
if(pPlayer)
{
if(id==0)
{
I3DEngine *pEngine = m_pSystem->GetI3DEngine();
if (pEngine && pAISystem)
{
Vec3d pos = pEnt->GetPos();
IVisArea *pArea;
int nBuildingid;
if (!pAISystem->CheckInside(pos,nBuildingid,pArea))
{
float newz = pEngine->GetTerrainElevation(pos.x,pos.y);
if (newz>pos.z)
pos.z=newz+0.1f;
pEnt->SetPos(pos);
}
}
}
//[kirill] the OnReset called from OnInit which is called when entity is spawned
// if(health>0)
// {
// // call on reset for the spawned entity
// m_pScriptSystem->BeginCall(pEnt->GetEntityClassName(),"OnReset");
// m_pScriptSystem->PushFuncParam(pEnt->GetScriptObject());
// m_pScriptSystem->EndCall();
// };
pPlayer->LoadGame_PATCH_1(stm);
if (pEnt->GetAI())
{
char sName[255];
stm.Read(sName,255);
pEnt->GetAI()->SetName(sName);
}
};
break;
}
case CHUNK_PLAYER:
{
EntityId wPlayerID = 0;
bool b = stm.Read(wPlayerID);
ASSERT(b);
if(wPlayerID==0) // we write 0 for localplayer on save
wPlayerID = localPlayerId;
m_pClient->SetPlayerID(wPlayerID);
pEnt=pEntitySystem->GetEntity(wPlayerID);
// if( wPlayerID==0 )
// pEnt=m_pClient->m_pISystem->GetLocalPlayer();
if (!pEnt)
CryError("player id=%3id NOT FOUND",wPlayerID);
//ASSERT(pEnt);
CPlayer *pPlayer=NULL;
if(pEnt->GetContainer()) pEnt->GetContainer()->QueryContainerInterface(CIT_IPLAYER,(void**) &pPlayer);
ASSERT(pPlayer);
stm.Read(pPlayer->m_bFirstPerson);
SetViewMode(!pPlayer->m_bFirstPerson); // hack, but works
/* [kirill] moved this to int CScriptObjectGame::TouchCheckPoint(IFunctionHandler *pH)
needed to fix quickLoad restoreHealth problem
// do we want to overwrite health with half of maximum
if(p_restorehalfhealth->GetIVal())
{
pPlayer->m_stats.health = 255; // [marco] danger! this should be set by
// gamerules but it gets overwritten by the save checkpoint
//m_pSystem->GetILog()->Log("player health=%d",pPlayer->m_stats.health);
// [kirill]
// this was requested by UBI. It's expected here that current health value is the maximum
//Everytime Jack dies he should respawn with half of his hit points instead of full health.
//Same mechanics for Val, she should get half her hit points everytime Jack respawns.
pPlayer->m_stats.health/=2;
}
if (pPlayer->m_stats.health<128)
pPlayer->m_stats.health = 128;
*/
IEntityCamera *pCam=pEnt->GetCamera();
ASSERT(pCam);
Vec3d vPos;
stm.Read(vPos);
pCam->SetPos(vPos);
//pEnt->SetPos(vPos);
m_pLog->Log("PLAYER %d (%f %f %f) ", wPlayerID, vPos.x, vPos.y, vPos.z);
Vec3d vAngles;
stm.Read(vAngles);
pCam->SetAngles(vAngles);
//[kirill]
//why do we do it here? entity angles are set when entity is loaded
//pEnt->SetAngles(vAngles);
m_pClient->m_PlayerProcessingCmd.SetDeltaAngles(vAngles);
GetSystem()->SetViewCamera( pCam->GetCamera() );
GetSystem()->GetISoundSystem()->SetListener(pCam->GetCamera(),Vec3(0,0,0));
if(!isdemo)
{
CXServer::XSlotMap::iterator itor;
ASSERT(m_pServer->GetSlotsMap().size()==1);
itor = m_pServer->GetSlotsMap().begin();
CXServerSlot *pSSlot=itor->second; // serverslot associated with the player
pSSlot->SetPlayerID(wPlayerID);
pSSlot->SetGameState(CGS_INPROGRESS); // start game imediatly
};
break;
}
case CHUNK_AI:
{
// find for which entity this chunk is
int nID;
stm.Read(nID);
IEntity *pEntity = m_pEntitySystem->GetEntity(nID);
if ( nID == 0 ) // it's local player
pEntity = m_pClient->m_pISystem->GetLocalPlayer();
if (pEntity)
{
m_pLog->Log("Now loading AICHUNK <PATCH_1> for entity %s", pEntity->GetName());
CPlayer *pPlayer=0;
CVehicle *pVehicle=0;
if (pEntity->GetContainer())
{
if (pEntity->GetContainer()->QueryContainerInterface(CIT_IPLAYER,(void**)&pPlayer))
pPlayer->LoadAIState_PATCH_1(stm);
if (pEntity->GetContainer()->QueryContainerInterface(CIT_IVEHICLE,(void**)&pVehicle))
pVehicle->LoadAIState(stm, scriptStream);
}
else if( pEntity->GetAI() )
{
pEntity->GetAI()->Load(stm);
IScriptSystem *pScriptSystem = GetSystem()->GetIScriptSystem();
HSCRIPTFUNCTION loadOverallFunction=NULL;
// if( pEntity->GetScriptObject() && pEntity->GetScriptObject()->GetValue("OnLoadOverall", loadOverallFunction) )
// {
// pScriptSystem->BeginCall(loadOverallFunction);
// pScriptSystem->PushFuncParam(pEntity->GetScriptObject());
// pScriptSystem->PushFuncParam(scriptStream.GetScriptObject());
// pScriptSystem->EndCall();
// }
}
}
VERIFY_COOKIE_NO(stm,13);
}
break;
case CHUNK_INGAME_SEQUENCE:
{
#if !defined(LINUX)
IMovieSystem *pMovies = m_pSystem->GetIMovieSystem();
char szName[1024];
stm.Read(szName,1024);
float fTime;
stm.Read(fTime);
IAnimSequence *pSeq = pMovies->FindSequence(szName);
pMovies->PlaySequence(pSeq,false);
pMovies->SetPlayingTime(pSeq,fTime);
#endif
}
break;
case CHUNK_HUD:
{
// load hud/clientstuff viewlayers data
if (m_pUIHud)
{
GetScriptSystem()->BeginCall("Hud", "OnLoad");
GetScriptSystem()->PushFuncParam(m_pUIHud->GetScript());
GetScriptSystem()->PushFuncParam(scriptStream.GetScriptObject());
GetScriptSystem()->EndCall();
}
_SmartScriptObject pClientStuff(m_pScriptSystem,true);
if(m_pScriptSystem->GetGlobalValue("ClientStuff",pClientStuff))
{
m_pScriptSystem->BeginCall("ClientStuff","OnLoad");
m_pScriptSystem->PushFuncParam(pClientStuff);
m_pScriptSystem->PushFuncParam(scriptStream.GetScriptObject());
m_pScriptSystem->EndCall();
}
}
break;
default:
ASSERT(0);
};
if (bLoadBar)
{
pConsole->TickProgressBar(); // advance progress
}
}
{ // [Anton] - allow entities to restore pointer links between them during post load step
// [kirill] restore all the bindings
IEntityItPtr pEntities=pEntitySystem->GetEntityIterator();
pEntities->MoveFirst();
IEntity *pEnt=NULL;
while((pEnt=pEntities->Next())!=NULL)
pEnt->PostLoad();
}
pEntitySystem->Update();
m_pSystem->GetIMovieSystem()->PlayOnLoadSequences(); // yes, we reset this twice, the first time to remove all entity-pointers and now to restore them
m_pClient->Reset();
m_bIsLoadingLevelFromFile = false;
m_pSystem->GetISoundSystem()->Mute(false);
m_bMapLoadedFromCheckpoint=true;
m_pEntitySystem->PauseTimers(false,true);
// m_pLog->Log("HIDE CONSOLE");
m_pRenderer->ClearColorBuffer(Vec3(0,0,0));
m_pSystem->GetIConsole()->ResetProgressBar(0);
m_pSystem->GetIConsole()->ShowConsole(false);
m_pSystem->GetIConsole()->SetScrollMax(600/2);
if (nPreset!=-1)
m_pSystem->GetISoundSystem()->SetEaxListenerEnvironment(nPreset,NULL);
else
m_pSystem->GetISoundSystem()->SetEaxListenerEnvironment(nPreset,&tProps);
GotoGame(1);
m_nDEBUG_TIMING = 0;
return true;
};