scummvm/engines/hpl1/penumbra-overture/GameEntity.cpp
2024-08-10 22:51:56 +03:00

1069 lines
33 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Copyright (C) 2006-2010 - Frictional Games
*
* This file is part of Penumbra Overture.
*/
#include "hpl1/penumbra-overture/GameEntity.h"
#include "hpl1/penumbra-overture/EffectHandler.h"
#include "hpl1/penumbra-overture/GameMessageHandler.h"
#include "hpl1/penumbra-overture/GameStickArea.h"
#include "hpl1/penumbra-overture/Init.h"
#include "hpl1/penumbra-overture/MapHandler.h"
#include "hpl1/penumbra-overture/Player.h"
#include "hpl1/penumbra-overture/PlayerHelper.h"
#include "hpl1/penumbra-overture/PlayerState.h"
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
iGameEntity::iGameEntity(cInit *apInit, const tString &asName) {
mpInit = apInit;
msName = asName;
msFileName = "";
mbActive = true;
mbIsSaved = true;
mfHealth = 0;
mlToughness = 0;
mbDestroyMe = false;
mbBreakMe = false;
mpMeshEntity = NULL;
mpCharBody = NULL;
mType = eGameEntityType_Unknown;
msDescription = _W("");
msGameName = _W("");
mbShowDescritionOnce = false;
mfMaxExamineDist = 6.0f;
mfMaxInteractDist = 1.5f;
mbHasInteraction = false;
mbHasBeenExamined = false;
mbPauseControllers = false;
mbPauseGravity = false;
for (int i = 0; i < eGameEntityScriptType_LastEnum; ++i) {
mvCallbackScripts[i] = NULL;
}
mbSaveLights = true;
mbUpdatingCollisionCallbacks = false;
mbTransActive = false;
mvLastImpulse = 0;
}
//-----------------------------------------------------------------------
iGameEntity::~iGameEntity() {
cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
//////////////////////////////////////////////
// Remove all references to from all otter
// Log("Deleting '%s'\n",msName.c_str());
// Player
mpInit->mpPlayer->RemoveCollideScriptWithChildEntity(this);
// Other entities (check so not all entities are being destroyed,
// in that case it is not needed and might be bad).
if (mpInit->mpMapHandler->IsDestroyingAll() == false) {
tGameEntityIterator it = mpInit->mpMapHandler->GetGameEntityIterator();
while (it.HasNext()) {
iGameEntity *pEntity = it.Next();
pEntity->RemoveCollideScriptWithChildEntity(this);
}
}
//////////////////////////////////////////////
// Destroy all graphics in the entity!
if (pWorld && mpInit->mbDestroyGraphics) {
for (size_t i = 0; i < mvBodies.size(); ++i) {
// Make sure that this body is not picked!
if (mpInit->mpPlayer->GetPickedBody() == mvBodies[i])
mpInit->mpPlayer->GetPickRay()->mpPickedBody = NULL;
if (mpInit->mpPlayer->GetPushBody() == mvBodies[i]) {
ePlayerState state = mpInit->mpPlayer->GetState();
if (state == ePlayerState_Move ||
state == ePlayerState_Grab ||
state == ePlayerState_Push) {
ePlayerState prevState = mpInit->mpPlayer->GetStateData(state)->mPreviuosState;
if (prevState == ePlayerState_InteractMode)
mpInit->mpPlayer->ChangeState(ePlayerState_InteractMode);
else
mpInit->mpPlayer->ChangeState(ePlayerState_Normal);
}
mpInit->mpPlayer->SetPushBody(NULL);
}
pWorld->GetPhysicsWorld()->DestroyBody(mvBodies[i]);
}
if (mpMeshEntity)
pWorld->DestroyMeshEntity(mpMeshEntity);
for (size_t i = 0; i < mvLights.size(); ++i)
pWorld->DestroyLight(mvLights[i]);
for (size_t i = 0; i < mvParticleSystems.size(); ++i)
if (mvParticleSystems[i])
mvParticleSystems[i]->Kill();
for (size_t i = 0; i < mvBillboards.size(); ++i)
pWorld->DestroyBillboard(mvBillboards[i]);
for (size_t i = 0; i < mvBeams.size(); ++i)
pWorld->DestroyBeam(mvBeams[i]);
for (size_t i = 0; i < mvSoundEntities.size(); ++i) {
pWorld->DestroySoundEntity(mvSoundEntities[i]);
}
if (mpCharBody)
pWorld->GetPhysicsWorld()->DestroyCharacterBody(mpCharBody);
}
// Delete callbacks
for (int i = 0; i < eGameEntityScriptType_LastEnum; ++i) {
if (mvCallbackScripts[i])
hplDelete(mvCallbackScripts[i]);
}
STLMapDeleteAll(m_mapCollideCallbacks);
STLDeleteAll(mvTransMaterials);
for (size_t i = 0; i < mvPreloadedBreakMeshes.size(); ++i) {
mpInit->mpGame->GetResources()->GetMeshManager()->Destroy(mvPreloadedBreakMeshes[i]);
}
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
void iGameEntity::SetActive(bool abX) {
if (mbActive == abX)
return;
mbActive = abX;
for (size_t i = 0; i < mvBodies.size(); ++i) {
mvBodies[i]->SetActive(mbActive);
mvBodies[i]->SetTransformUpdated(true);
}
if (mpMeshEntity) {
mpMeshEntity->SetVisible(mbActive);
mpMeshEntity->SetActive(mbActive);
if (mbActive)
mpMeshEntity->UpdateLogic(0.01f);
}
if (mpCharBody) {
mpCharBody->SetActive(mbActive);
if (mbActive) {
// Pre update the character body to make sure it is on ground.
// Seems to mess up stuff
/*for(int i=0; i<120; ++i)
{
//mpCharBody->Update(1.0f / 60.0f);
}*/
}
}
for (size_t i = 0; i < mvParticleSystems.size(); ++i) {
if (mvParticleSystems[i])
mvParticleSystems[i]->SetVisible(mbActive);
if (mvParticleSystems[i])
mvParticleSystems[i]->SetActive(mbActive);
}
for (size_t i = 0; i < mvLights.size(); ++i)
if (mvLights[i])
mvLights[i]->SetVisible(mbActive);
for (size_t i = 0; i < mvBillboards.size(); ++i)
if (mvBillboards[i])
mvBillboards[i]->SetVisible(mbActive);
OnSetActive(mbActive);
}
//-----------------------------------------------------------------------
float iGameEntity::GetPickedDistance() {
return mpInit->mpPlayer->GetPickedDist();
}
//-----------------------------------------------------------------------
eCrossHairState iGameEntity::GetPickCrossHairState(iPhysicsBody *apBody) {
float fDistance = GetPickedDistance();
//////////////////////////////////////////
// Interaction available
cGameStickArea *pStickArea = mpInit->mpMapHandler->GetBodyStickArea(apBody);
if (apBody->GetMass() != 0 || mType == eGameEntityType_Area || (pStickArea && pStickArea->GetCanDeatch())) {
if (mvCallbackScripts[eGameEntityScriptType_PlayerInteract] || mbHasInteraction) {
if (fDistance <= mfMaxInteractDist)
return eCrossHairState_Active;
}
}
//////////////////////////////////////////
// Examine available
if (mvCallbackScripts[eGameEntityScriptType_PlayerExamine] || msDescription != _W("")) {
if (fDistance <= mfMaxExamineDist)
return eCrossHairState_Examine;
}
//////////////////////////////////////////
// Too far
if (mvCallbackScripts[eGameEntityScriptType_PlayerInteract] || mbHasInteraction) {
// if(fDistance <= mfMaxExamineDist) return eCrossHairState_Invalid;
if (apBody->GetMass() == 0 && mvCallbackScripts[eGameEntityScriptType_PlayerInteract] == NULL) {
return eCrossHairState_None;
} else {
return eCrossHairState_Invalid;
}
}
return eCrossHairState_None;
}
//-----------------------------------------------------------------------
void iGameEntity::DestroyLight(iLight3D *apLight) {
STLFindAndRemove(mvLights, apLight);
mpInit->mpGame->GetScene()->GetWorld3D()->DestroyLight(apLight);
}
void iGameEntity::DestroyParticleSystem(cParticleSystem3D *apPS) {
STLFindAndRemove(mvParticleSystems, apPS);
mpInit->mpGame->GetScene()->GetWorld3D()->DestroyParticleSystem(apPS);
}
void iGameEntity::DestroyBillboard(cBillboard *apBillboard) {
STLFindAndRemove(mvBillboards, apBillboard);
mpInit->mpGame->GetScene()->GetWorld3D()->DestroyBillboard(apBillboard);
}
void iGameEntity::SetSoundEntity(cSoundEntity *apSound) {
STLFindAndRemove(mvSoundEntities, apSound);
mpInit->mpGame->GetScene()->GetWorld3D()->DestroySoundEntity(apSound);
}
//-----------------------------------------------------------------------
void iGameEntity::PlayerPick() {
//////////////////////
// Script stuff
/*cWorld3D *pWorld = */ mpInit->mpGame->GetScene()->GetWorld3D();
if (mvCallbackScripts[eGameEntityScriptType_PlayerPick]) {
tString sCommand = GetScriptCommand(eGameEntityScriptType_PlayerPick);
mpInit->RunScriptCommand(sCommand);
}
OnPlayerPick();
}
void iGameEntity::PlayerInteract() {
//////////////////////
// Script stuff
if (GetPickedDistance() <= mfMaxInteractDist) {
/*cWorld3D *pWorld = */ mpInit->mpGame->GetScene()->GetWorld3D();
if (mvCallbackScripts[eGameEntityScriptType_PlayerInteract]) {
tString sCommand = GetScriptCommand(eGameEntityScriptType_PlayerInteract);
mpInit->RunScriptCommand(sCommand);
}
}
OnPlayerInteract();
}
void iGameEntity::PlayerExamine() {
//////////////////////
// Script stuff
if (GetPickedDistance() <= mfMaxExamineDist) {
/*cWorld3D *pWorld = */ mpInit->mpGame->GetScene()->GetWorld3D();
if (mvCallbackScripts[eGameEntityScriptType_PlayerExamine]) {
tString sCommand = GetScriptCommand(eGameEntityScriptType_PlayerExamine);
mpInit->RunScriptCommand(sCommand);
}
}
OnPlayerExamine();
}
//-----------------------------------------------------------------------
void iGameEntity::OnPlayerExamine() {
if (mfMaxExamineDist >= mpInit->mpPlayer->GetPickedDist() && msDescription != _W("")) {
mpInit->mpGameMessageHandler->Add(msDescription);
// if(mbShowDescritionOnce) msDescription = _W("");
mbHasBeenExamined = true;
//////////////////////////////
// Set focus on the object
mpInit->mpEffectHandler->GetDepthOfField()->FocusOnBody(mpInit->mpPlayer->GetPickedBody());
mpInit->mpEffectHandler->GetDepthOfField()->SetFocusBody(NULL);
mpInit->mpEffectHandler->GetDepthOfField()->SetActive(true, 1.2f);
mpInit->mpGameMessageHandler->SetFocusIsedUsed(true);
}
}
//-----------------------------------------------------------------------
void iGameEntity::Damage(float afDamage, int alStrength) {
if (mfHealth > 0) {
if (mType == eGameEntityType_Enemy) {
// if(mpInit->mDifficulty== eGameDifficulty_Easy) afDamage *= 2.0f;
if (mpInit->mDifficulty == eGameDifficulty_Hard)
afDamage /= 2.0f;
}
int lDiff = mlToughness - alStrength;
if (alStrength >= 0) {
float fDamageMul = 1 - (0.25f * (float)lDiff);
if (fDamageMul < 0)
fDamageMul = 0;
// Could be 2 here, depends on what you wanna do. This way the damage is never increased.
if (fDamageMul > 1)
fDamageMul = 1;
afDamage *= fDamageMul;
}
mfHealth -= ABS(afDamage);
if (mfHealth <= 0) {
OnDeath(afDamage);
} else {
OnDamage(afDamage);
}
}
}
void iGameEntity::SetHealth(float afHealth) {
if (afHealth <= 0 && mfHealth > 0) {
mfHealth = afHealth;
OnDeath(0);
} else {
mfHealth = afHealth;
}
}
//-----------------------------------------------------------------------
void iGameEntity::SetUpTransMaterials() {
mvNormalMaterials.resize(mpMeshEntity->GetSubMeshEntityNum());
mvTransMaterials.resize(mpMeshEntity->GetSubMeshEntityNum());
mbTransShadow = mpMeshEntity->IsShadowCaster();
for (int i = 0; i < mpMeshEntity->GetSubMeshEntityNum(); ++i) {
cSubMeshEntity *pSubEntity = mpMeshEntity->GetSubMeshEntity(i);
cSubMesh *pSubMesh = mpMeshEntity->GetMesh()->GetSubMesh(i);
iMaterial *pNormalMaterial = pSubEntity->GetMaterial();
mvNormalMaterials[i] = pSubEntity->GetCustomMaterial();
// create material for the transperancy
iMaterial *pTransMaterial = mpInit->mpGame->GetGraphics()->GetMaterialHandler()->Create(
"Trans", "Modulative", eMaterialPicture_Texture);
// Set texture for the trans material
iTexture *pDiffTex = pNormalMaterial->GetTexture(eMaterialTexture_Diffuse);
if (pDiffTex) {
pDiffTex->IncUserCount();
pTransMaterial->SetTexture(pDiffTex, eMaterialTexture_Diffuse);
mvTransMaterials[i] = pTransMaterial;
} else {
Log("Sub mesh '%s' material '%s' does not have diffuse!\n", pSubMesh->GetName().c_str(),
pNormalMaterial->GetName().c_str());
}
}
}
void iGameEntity::SetTransActive(bool abX) {
if (mbTransActive == abX)
return;
mbTransActive = abX;
if (mbTransShadow) {
// mpMeshEntity->SetForceShadow(mbTransActive);
}
for (int i = 0; i < mpMeshEntity->GetSubMeshEntityNum(); ++i) {
cSubMeshEntity *pSubEntity = mpMeshEntity->GetSubMeshEntity(i);
if (mbTransActive) {
pSubEntity->SetCustomMaterial(mvTransMaterials[i], false);
} else {
pSubEntity->SetCustomMaterial(mvNormalMaterials[i], false);
}
}
}
//-----------------------------------------------------------------------
static inline tString GetCollideCommand(const tString &asFuncName, const tString &asParent,
const tString &asChild) {
return asFuncName + "(\"" + asParent + "\", \"" + asChild + "\")";
}
////////////////////////////
void iGameEntity::OnUpdate(float afTimeStep) {
if (mbActive == false)
return;
////////////////////////////////////////////
/// Script Collide test stuff
iPhysicsWorld *pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();
/*cWorld3D *pWorld = */ mpInit->mpGame->GetScene()->GetWorld3D();
////////////////
// If entity has character body add it to the array and then remove.
Common::Array<iPhysicsBody *> vTempBodies;
if (mpCharBody) {
for (size_t i = 0; i < mvBodies.size(); ++i) {
vTempBodies.push_back(mvBodies[i]);
}
mvBodies.clear();
mvBodies.push_back(mpCharBody->GetBody());
}
cCollideData collideData;
collideData.SetMaxSize(1);
// if(msName == "liftclose") Log("--- Start collision test\n");
mbUpdatingCollisionCallbacks = true;
tGameCollideScriptMapIt CollideIt = m_mapCollideCallbacks.begin();
for (; CollideIt != m_mapCollideCallbacks.end(); ++CollideIt) {
cGameCollideScript *pCallback = CollideIt->second;
iGameEntity *pEntity = pCallback->mpEntity;
if (pEntity->IsActive() == false)
continue;
bool bCollide = false;
for (size_t i = 0; i < mvBodies.size(); ++i)
for (size_t j = 0; j < pEntity->mvBodies.size(); ++j) {
iPhysicsBody *pParentBody = mvBodies[i];
iPhysicsBody *pChildBody = pEntity->mvBodies[j];
// if(msName == "liftclose") Log("Start shape collision....");
if (cMath::CheckCollisionBV(*pParentBody->GetBV(), *pChildBody->GetBV())) {
bCollide = pPhysicsWorld->CheckShapeCollision(pParentBody->GetShape(),
pParentBody->GetLocalMatrix(),
pChildBody->GetShape(),
pChildBody->GetLocalMatrix(),
collideData, 1);
}
// if(msName == "liftclose") Log("end it\n");
if (bCollide)
break;
}
// Run Collide scripts
if (bCollide) {
// if(msName == "liftclose") Log("entity %s collided!\n",msName.c_str());
if (pCallback->mbCollides) {
if (pCallback->msFuncName[eGameCollideScriptType_During] != "") {
tString sCommand = GetCollideCommand(
pCallback->msFuncName[eGameCollideScriptType_During],
msName, CollideIt->first);
mpInit->RunScriptCommand(sCommand);
}
} else {
if (pCallback->msFuncName[eGameCollideScriptType_Enter] != "") {
tString sCommand = GetCollideCommand(
pCallback->msFuncName[eGameCollideScriptType_Enter],
msName, CollideIt->first);
mpInit->RunScriptCommand(sCommand);
}
pCallback->mbCollides = true;
}
} else {
if (pCallback->mbCollides) {
if (pCallback->msFuncName[eGameCollideScriptType_Leave] != "") {
tString sCommand = GetCollideCommand(
pCallback->msFuncName[eGameCollideScriptType_Leave],
msName, CollideIt->first);
mpInit->RunScriptCommand(sCommand);
}
pCallback->mbCollides = false;
}
}
}
mbUpdatingCollisionCallbacks = false;
////////////////
// If entity has character body remove the previuously added.
if (mpCharBody) {
mvBodies.clear();
for (size_t i = 0; i < vTempBodies.size(); ++i)
mvBodies.push_back(vTempBodies[i]);
}
// if(msName == "liftclose") Log("--- End collision test\n");
//////////////////////////////////////////////////
// Check if any callback should be deleted
CollideIt = m_mapCollideCallbacks.begin();
for (; CollideIt != m_mapCollideCallbacks.end();) {
cGameCollideScript *pCallback = CollideIt->second;
tGameCollideScriptMapIt currentIt = CollideIt;
++CollideIt;
if (pCallback->mbDeleteMe) {
hplDelete(pCallback);
m_mapCollideCallbacks.erase(currentIt);
}
}
////////////////////////////////////////////
/// Script Update
if (mvCallbackScripts[eGameEntityScriptType_OnUpdate]) {
tString sCommand = GetScriptCommand(eGameEntityScriptType_OnUpdate);
mpInit->RunScriptCommand(sCommand);
}
///////////////////////////////////////////
// update entity specific stuff.
Update(afTimeStep);
}
//-----------------------------------------------------------------------
void iGameEntity::AddCollideScript(eGameCollideScriptType aType, const tString &asFunc, const tString &asEntity) {
cGameCollideScript *pCallback;
// Check if the function already exist
tGameCollideScriptMapIt it = m_mapCollideCallbacks.find(asEntity);
if (it != m_mapCollideCallbacks.end()) {
pCallback = it->second;
} else {
pCallback = hplNew(cGameCollideScript, ());
// Get the entity
iGameEntity *pEntity = mpInit->mpMapHandler->GetGameEntity(asEntity);
if (pEntity == NULL) {
Warning("Couldn't find entity '%s'\n", asEntity.c_str());
hplDelete(pCallback);
return;
}
// Set the entity
pCallback->mpEntity = pEntity;
// Add to container
m_mapCollideCallbacks.insert(tGameCollideScriptMap::value_type(asEntity, pCallback));
}
pCallback->msFuncName[aType] = asFunc;
}
//-----------------------------------------------------------------------
void iGameEntity::RemoveCollideScriptWithChildEntity(iGameEntity *apEntity) {
tGameCollideScriptMapIt it = m_mapCollideCallbacks.begin();
for (; it != m_mapCollideCallbacks.end();) {
cGameCollideScript *pCallback = it->second;
tGameCollideScriptMapIt currentIt = it;
++it;
if (pCallback && pCallback->mpEntity == apEntity) {
if (mbUpdatingCollisionCallbacks) {
pCallback->mbDeleteMe = true;
} else {
hplDelete(pCallback);
m_mapCollideCallbacks.erase(currentIt);
}
}
}
}
//-----------------------------------------------------------------------
void iGameEntity::RemoveCollideScript(eGameCollideScriptType aType, const tString &asEntity) {
tGameCollideScriptMapIt it = m_mapCollideCallbacks.find(asEntity);
if (it != m_mapCollideCallbacks.end()) {
cGameCollideScript *pCallback = it->second;
pCallback->msFuncName[aType] = "";
// if there are no functions left, erase
if (pCallback->msFuncName[0] == "" && pCallback->msFuncName[1] == "" && pCallback->msFuncName[2] == "") {
if (mbUpdatingCollisionCallbacks) {
pCallback->mbDeleteMe = true;
} else {
hplDelete(pCallback);
m_mapCollideCallbacks.erase(it);
}
}
} else {
Warning("Entity '%s' callback doesn't exist in '%s'\n", asEntity.c_str(), msName.c_str());
}
}
//-----------------------------------------------------------------------
void iGameEntity::AddScript(eGameEntityScriptType aType, const tString &asFunc) {
cGameEntityScript *pScript = mvCallbackScripts[aType];
if (pScript == NULL) {
pScript = hplNew(cGameEntityScript, ());
mvCallbackScripts[aType] = pScript;
}
pScript->msScriptFunc = asFunc;
}
//-----------------------------------------------------------------------
void iGameEntity::RemoveScript(eGameEntityScriptType aType) {
if (mvCallbackScripts[aType]) {
hplDelete(mvCallbackScripts[aType]);
mvCallbackScripts[aType] = NULL;
}
}
//-----------------------------------------------------------------------
void iGameEntity::CreateVar(const tString &asName, int alVal) {
tGameEntityVarMapIt it = m_mapVars.find(asName);
if (it == m_mapVars.end()) {
m_mapVars.insert(tGameEntityVarMap::value_type(asName, alVal));
}
}
void iGameEntity::SetVar(const tString &asName, int alVal) {
tGameEntityVarMapIt it = m_mapVars.find(asName);
if (it == m_mapVars.end()) {
Warning("Entity '%s' var '%s' not found!\n", msName.c_str(), asName.c_str());
return;
}
it->second = alVal;
}
void iGameEntity::AddVar(const tString &asName, int alVal) {
tGameEntityVarMapIt it = m_mapVars.find(asName);
if (it == m_mapVars.end()) {
Warning("Entity '%s' var '%s' not found!\n", msName.c_str(), asName.c_str());
return;
}
it->second += alVal;
}
int iGameEntity::GetVar(const tString &asName) {
tGameEntityVarMapIt it = m_mapVars.find(asName);
if (it == m_mapVars.end()) {
Warning("Entity '%s' var '%s' not found!\n", msName.c_str(), asName.c_str());
return 0;
}
return it->second;
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PROTECTED METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
tString iGameEntity::GetScriptCommand(eGameEntityScriptType aType) {
return mvCallbackScripts[aType]->msScriptFunc + "(\"" + msName + "\")";
}
//-----------------------------------------------------------------------
void iGameEntity::PreloadModel(const tString &asFile) {
tString sFileName = cString::SetFileExt(asFile, "ent");
tString sPath = mpInit->mpGame->GetResources()->GetFileSearcher()->GetFilePath(sFileName);
if (sPath != "") {
TiXmlDocument *pEntityDoc = hplNew(TiXmlDocument, ());
if (pEntityDoc->LoadFile(sPath.c_str()) == false) {
Error("Couldn't load '%s'!\n", sPath.c_str());
} else {
TiXmlElement *pRootElem = pEntityDoc->FirstChildElement();
TiXmlElement *pGraphicsElem = pRootElem->FirstChildElement("GRAPHICS");
tString sModelFile = cString::ToString(pGraphicsElem->Attribute("ModelFile"), "");
cMesh *pMesh = mpInit->mpGame->GetResources()->GetMeshManager()->CreateMesh(sModelFile);
mvPreloadedBreakMeshes.push_back(pMesh);
for (int i = 0; i < pMesh->GetReferenceNum(); ++i) {
cMeshReference *pRef = pMesh->GetReference(i);
PreloadModel(pRef->msFile);
}
}
hplDelete(pEntityDoc);
} else {
Error("Entity file '%s' was not found!\n", sFileName.c_str());
}
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// SAVE DATA STUFF
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cEnginePS_SaveData *iGameEntity_SaveData::GetParticleSystem(cParticleSystem3D *apPS) {
for (size_t i = 0; i < mvPS.Size(); ++i) {
if (mvPS[i].msName == apPS->GetName())
return &mvPS[i];
}
return NULL;
}
cEngineSound_SaveData *iGameEntity_SaveData::GetSoundEntity(cSoundEntity *apSound) {
for (size_t i = 0; i < mvSounds.Size(); ++i) {
if (mvSounds[i].msName == apSound->GetName())
return &mvSounds[i];
}
return NULL;
}
//-----------------------------------------------------------------------
kBeginSerializeBase(cGameEntityScript)
kSerializeVar(mlNum, eSerializeType_Int32)
kSerializeVar(msScriptFunc, eSerializeType_String)
kEndSerialize()
//-----------------------------------------------------------------------
kBeginSerializeBase(cGameEntityAnimation_SaveData)
kSerializeVar(mbActive, eSerializeType_Bool)
kSerializeVar(mbLoop, eSerializeType_Bool)
kSerializeVar(mfWeight, eSerializeType_Float32)
kSerializeVar(mfFadeStep, eSerializeType_Float32)
kSerializeVar(mfTimePos, eSerializeType_Float32)
kSerializeVar(mfSpeed, eSerializeType_Float32)
kEndSerialize()
//-----------------------------------------------------------------------
kBeginSerializeBaseVirtual(iGameEntity_SaveData)
kSerializeVar(mbActive, eSerializeType_Bool)
kSerializeVar(msFileName, eSerializeType_String)
kSerializeVar(msName, eSerializeType_String)
kSerializeVar(mfHealth, eSerializeType_Float32)
kSerializeVar(mfMaxExamineDist, eSerializeType_Float32)
kSerializeVar(mfMaxInteractDist, eSerializeType_Float32)
kSerializeVar(msGameName, eSerializeType_WString)
kSerializeVar(msDescription, eSerializeType_WString)
kSerializeVar(mbHasBeenExamined, eSerializeType_Bool)
kSerializeVar(mbShowDescritionOnce, eSerializeType_Bool)
kSerializeVar(mType, eSerializeType_Int32)
kSerializeVar(m_mtxTransform, eSerializeType_Matrixf)
kSerializeClassContainer(mlstCollideCallbacks, cSaveGame_cGameCollideScript, eSerializeType_Class)
kSerializeClassContainer(mlstCallbackScripts, cGameEntityScript, eSerializeType_Class)
kSerializeClassContainer(mlstVars, cScriptVar, eSerializeType_Class)
kSerializeClassContainer(mvBodies, cEngineBody_SaveData, eSerializeType_Class)
kSerializeClassContainer(mvPS, cEnginePS_SaveData, eSerializeType_Class)
kSerializeClassContainer(mvLights, cEngineLight_SaveData, eSerializeType_Class)
kSerializeClassContainer(mvSounds, cEngineSound_SaveData, eSerializeType_Class)
kSerializeClassContainer(mvAnimations, cGameEntityAnimation_SaveData, eSerializeType_Class)
kEndSerialize()
//-----------------------------------------------------------------------
void iGameEntity::SaveToSaveData(iGameEntity_SaveData *apSaveData) {
// Properties
kCopyToVar(apSaveData, mbActive);
kCopyToVar(apSaveData, msName);
kCopyToVar(apSaveData, msFileName);
kCopyToVar(apSaveData, mfHealth);
kCopyToVar(apSaveData, mfMaxInteractDist);
kCopyToVar(apSaveData, mfMaxExamineDist);
kCopyToVar(apSaveData, msGameName);
kCopyToVar(apSaveData, msDescription);
kCopyToVar(apSaveData, mbShowDescritionOnce);
kCopyToVar(apSaveData, mbHasBeenExamined);
kCopyToVar(apSaveData, mType);
apSaveData->m_mtxTransform = m_mtxOnLoadTransform;
cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
// Collide scripts
tGameCollideScriptMapIt colIt = m_mapCollideCallbacks.begin();
for (; colIt != m_mapCollideCallbacks.end(); ++colIt) {
cGameCollideScript *pScript = colIt->second;
cSaveGame_cGameCollideScript savedScript;
savedScript.LoadFrom(pScript);
apSaveData->mlstCollideCallbacks.Add(savedScript);
}
// Script functions
for (int i = 0; i < eGameEntityScriptType_LastEnum; ++i) {
if (mvCallbackScripts[i]) {
cGameEntityScript script;
script.mlNum = i;
script.msScriptFunc = mvCallbackScripts[i]->msScriptFunc;
apSaveData->mlstCallbackScripts.Add(script);
}
}
// Script variables
tGameEntityVarMapIt varIt = m_mapVars.begin();
for (; varIt != m_mapVars.end(); ++varIt) {
cScriptVar scriptVar;
scriptVar.mlVal = varIt->second;
scriptVar.msName = varIt->first;
apSaveData->mlstVars.Add(scriptVar);
}
// Bodies
apSaveData->mvBodies.Resize(mvBodies.size());
for (size_t i = 0; i < mvBodies.size(); ++i) {
apSaveData->mvBodies[i].FromBody(mvBodies[i]);
}
// Log("Saving particles for %s\n",GetName().c_str());
// Particle Systems
apSaveData->mvPS.Resize(mvParticleSystems.size());
for (size_t i = 0; i < mvParticleSystems.size(); ++i) {
// Log("%d, ",i);
if (pWorld->ParticleSystemExists(mvParticleSystems[i]) == false) {
mvParticleSystems[i] = NULL;
Warning("particle system %d in %s does not exist anymore!\n", i, GetName().c_str());
}
apSaveData->mvPS[i].FromPS(mvParticleSystems[i]);
}
// Log("Done\n");
// Lights
if (mbSaveLights) {
apSaveData->mvLights.Resize(mvLights.size());
for (size_t i = 0; i < mvLights.size(); ++i) {
apSaveData->mvLights[i].FromLight(mvLights[i]);
}
}
// Sounds
apSaveData->mvSounds.Resize(mvSoundEntities.size());
for (size_t i = 0; i < mvSoundEntities.size(); ++i) {
apSaveData->mvSounds[i].FromSound(mvSoundEntities[i]);
}
// Animations
if (mpMeshEntity) {
apSaveData->mvAnimations.Resize(mpMeshEntity->GetAnimationStateNum());
for (int i = 0; i < mpMeshEntity->GetAnimationStateNum(); ++i) {
cAnimationState *pAnim = mpMeshEntity->GetAnimationState(i);
cGameEntityAnimation_SaveData &saveAnim = apSaveData->mvAnimations[i];
saveAnim.mbActive = pAnim->IsActive();
saveAnim.mbLoop = pAnim->IsLooping();
saveAnim.mfWeight = pAnim->GetWeight();
saveAnim.mfFadeStep = pAnim->GetFadeStep();
saveAnim.mfTimePos = pAnim->GetTimePosition();
saveAnim.mfSpeed = pAnim->GetSpeed();
}
}
}
//-----------------------------------------------------------------------
void iGameEntity::LoadFromSaveData(iGameEntity_SaveData *apSaveData) {
// Properties
kCopyFromVar(apSaveData, msName);
kCopyFromVar(apSaveData, msFileName);
kCopyFromVar(apSaveData, mfHealth);
kCopyFromVar(apSaveData, mfMaxInteractDist);
kCopyFromVar(apSaveData, mfMaxExamineDist);
kCopyFromVar(apSaveData, msGameName);
kCopyFromVar(apSaveData, msDescription);
kCopyFromVar(apSaveData, mbShowDescritionOnce);
kCopyFromVar(apSaveData, mbHasBeenExamined);
kCopyFromVar(apSaveData, mType);
SetActive(apSaveData->mbActive);
// Script functions
cContainerListIterator<cGameEntityScript> scriptIt = apSaveData->mlstCallbackScripts.GetIterator();
while (scriptIt.HasNext()) {
cGameEntityScript &script = scriptIt.Next();
mvCallbackScripts[script.mlNum] = hplNew(cGameEntityScript, ());
mvCallbackScripts[script.mlNum]->msScriptFunc = script.msScriptFunc;
}
// Script variables
cContainerListIterator<cScriptVar> scriptVar = apSaveData->mlstVars.GetIterator();
while (scriptVar.HasNext()) {
cScriptVar &var = scriptVar.Next();
CreateVar(var.msName, var.mlVal);
}
// Bodies
for (size_t i = 0; i < mvBodies.size(); ++i) {
apSaveData->mvBodies[i].ToBody(mvBodies[i]);
}
// Lights
if (mbSaveLights) {
for (size_t i = 0; i < mvLights.size(); ++i) {
apSaveData->mvLights[i].ToLight(mvLights[i]);
}
}
// Particle Systems
int lCount = 0;
for (Common::Array<cParticleSystem3D *>::iterator it = mvParticleSystems.begin();
it != mvParticleSystems.end();) {
cParticleSystem3D *pPS = *it;
/*if(pPS)
Log("Loading particle system %d, %s\n",i,pPS->GetName().c_str());
else
Log("Loading particle system %d, NULL\n",i);*/
cEnginePS_SaveData *pSavePS = apSaveData->GetParticleSystem(pPS);
if (pSavePS) {
pSavePS->ToPS(pPS);
++it;
} else {
// check if a null was previously saved
if (apSaveData->mvPS.Size() == mvParticleSystems.size() &&
apSaveData->mvPS[lCount].msType == "") {
++it;
}
// a particle system has been removed.
else {
mpInit->mpGame->GetScene()->GetWorld3D()->DestroyParticleSystem(pPS);
it = mvParticleSystems.erase(it);
}
}
++lCount;
}
// Sounds
for (Common::Array<cSoundEntity *>::iterator it = mvSoundEntities.begin();
it != mvSoundEntities.end();) {
cSoundEntity *pSound = *it;
cEngineSound_SaveData *pSaveSound = apSaveData->GetSoundEntity(pSound);
if (pSaveSound) {
pSaveSound->ToSound(pSound);
++it;
} else {
mpInit->mpGame->GetScene()->GetWorld3D()->DestroySoundEntity(pSound);
it = mvSoundEntities.erase(it);
}
}
// Animations
if (mpMeshEntity) {
if (mpMeshEntity->GetAnimationStateNum() == (int)apSaveData->mvAnimations.Size()) {
for (int i = 0; i < mpMeshEntity->GetAnimationStateNum(); ++i) {
cAnimationState *pAnim = mpMeshEntity->GetAnimationState(i);
cGameEntityAnimation_SaveData &saveAnim = apSaveData->mvAnimations[i];
pAnim->SetActive(saveAnim.mbActive);
pAnim->SetLoop(saveAnim.mbLoop);
pAnim->SetWeight(saveAnim.mfWeight);
pAnim->SetFadeStep(saveAnim.mfFadeStep);
pAnim->SetTimePosition(saveAnim.mfTimePos);
pAnim->SetSpeed(saveAnim.mfSpeed);
}
} else {
Error("Number of animations in saved entity '%s' of type '%s' does not match!\n",
GetName().c_str(), mpMeshEntity->GetName().c_str());
}
}
}
//-----------------------------------------------------------------------
void iGameEntity::SetupSaveData(iGameEntity_SaveData *apSaveData) {
// Collide scripts
cContainerListIterator<cSaveGame_cGameCollideScript> colIt = apSaveData->mlstCollideCallbacks.GetIterator();
while (colIt.HasNext()) {
cSaveGame_cGameCollideScript &savedScript = colIt.Next();
cGameCollideScript *pCallback = hplNew(cGameCollideScript, ());
pCallback->mpEntity = mpInit->mpMapHandler->GetGameEntity(savedScript.msEntity);
if (pCallback->mpEntity == NULL) {
Warning("Couldn't find entity '%s'\n", savedScript.msEntity.c_str());
hplDelete(pCallback);
continue;
}
savedScript.SaveTo(pCallback);
m_mapCollideCallbacks.insert(tGameCollideScriptMap::value_type(savedScript.msEntity, pCallback));
}
// Log("Setup save data!\n");
}
//-----------------------------------------------------------------------