Serious-Engine/Sources/Engine/Entities/EntityProperties.cpp

674 lines
21 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */
#include <Engine/StdH.h>
#include <Engine/Entities/EntityProperties.h>
#include <Engine/Entities/Precaching.h>
#include <Engine/Base/Stream.h>
#include <Engine/Base/CRCTable.h>
#include <Engine/Base/Console.h>
#include <Engine/World/World.h>
#include <Engine/Base/ReplaceFile.h>
#include <Engine/Sound/SoundObject.h>
#include <Engine/Math/Quaternion.h>
#include <Engine/Templates/Stock_CAnimData.h>
#include <Engine/Templates/Stock_CTextureData.h>
#include <Engine/Templates/Stock_CModelData.h>
#include <Engine/Templates/Stock_CSoundData.h>
#include <Engine/Templates/Stock_CEntityClass.h>
#include <Engine/Templates/StaticArray.cpp>
#define FILTER_ALL "All files (*.*)\0*.*\0"
#define FILTER_END "\0"
#define PROPERTY(offset, type) ENTITYPROPERTY(this, offset, type)
/////////////////////////////////////////////////////////////////////
// Property management functions
/*
* Set all properties to default values.
*/
void CEntity::SetDefaultProperties(void)
{
// no properties to set in base class
}
/*
* Helpers for writing/reading entity pointers.
*/
void CEntity::ReadEntityPointer_t(CTStream *istrm, CEntityPointer &pen)
{
CEntity *penPointed;
// read index
INDEX iPointedEntity;
*istrm>>iPointedEntity;
// if there is no entity pointed to
if (iPointedEntity == -1) {
// set NULL pointer
penPointed = NULL;
// if there is some entity
} else {
// get the entity in this world with that index
extern BOOL _bReadEntitiesByID;
if (_bReadEntitiesByID) {
penPointed = en_pwoWorld->EntityFromID(iPointedEntity);
} else {
penPointed = en_pwoWorld->wo_cenAllEntities.Pointer(iPointedEntity);
}
}
// return the entity pointer
pen = penPointed;
}
void CEntity::WriteEntityPointer_t(CTStream *ostrm, CEntityPointer pen)
{
// if there is no entity pointed to
if (pen==NULL) {
// write -1 index
*ostrm<<(INDEX)-1;
// if there is some entity
} else {
// the entity must be in the same world as this one
ASSERT(pen->en_pwoWorld == en_pwoWorld);
// write index of the entity in this world
*ostrm<<(pen->en_ulID);
}
}
/*
* Read all properties from a stream.
*/
void CEntity::ReadProperties_t(CTStream &istrm) // throw char *
{
istrm.ExpectID_t("PRPS"); // 'properties'
CDLLEntityClass *pdecDLLClass = en_pecClass->ec_pdecDLLClass;
INDEX ctProperties;
// read number of properties (note that this doesn't have to be same as number
// of properties in the class (class might have changed))
istrm>>ctProperties;
// for all saved properties
for(INDEX iProperty=0; iProperty<ctProperties; iProperty++) {
pdecDLLClass->dec_ctProperties;
// read packed identifier
ULONG ulIDAndType;
istrm>>ulIDAndType;
// unpack property ID and property type from the identifier
ULONG ulID;
CEntityProperty::PropertyType eptType;
ulID = ulIDAndType>>8;
eptType = (CEntityProperty::PropertyType )(ulIDAndType&0x000000FFUL);
// get the property with that ID and type
CEntityProperty *pepProperty = PropertyForTypeAndID(eptType, ulID);
// if not found, but it is a string
if (pepProperty == NULL && eptType==CEntityProperty::EPT_STRING) {
// maybe that became translatable string try that
pepProperty = PropertyForTypeAndID(CEntityProperty::EPT_STRINGTRANS, ulID);
// NOTE: it is still loaded as string, without translation chunk,
// we just find it in properties table as a translatable string.
}
// if it was not found
if (pepProperty == NULL) {
// depending on the property type
switch (eptType) {
// if it is BOOL
case CEntityProperty::EPT_BOOL: {
// skip BOOL
BOOL bDummy;
istrm>>(INDEX &)bDummy;
break;
}
// if it is INDEX
case CEntityProperty::EPT_INDEX:
case CEntityProperty::EPT_ENUM:
case CEntityProperty::EPT_FLAGS:
case CEntityProperty::EPT_ANIMATION:
case CEntityProperty::EPT_ILLUMINATIONTYPE:
case CEntityProperty::EPT_COLOR:
case CEntityProperty::EPT_ANGLE: {
// skip INDEX
INDEX iDummy;
istrm>>iDummy;
} break;
// if it is FLOAT
case CEntityProperty::EPT_FLOAT:
case CEntityProperty::EPT_RANGE: {
// skip FLOAT
FLOAT fDummy;
istrm>>fDummy;
}
break;
// if it is STRING
case CEntityProperty::EPT_STRING: {
// skip STRING
CTString strDummy;
istrm>>strDummy;
break;
}
// if it is STRINGTRANS
case CEntityProperty::EPT_STRINGTRANS: {
// skip STRINGTRANS
istrm.ExpectID_t("DTRS");
CTString strDummy;
istrm>>strDummy;
break;
}
// if it is FILENAME
case CEntityProperty::EPT_FILENAME: {
// skip FILENAME
CTFileName fnmDummy;
istrm>>fnmDummy;
break;
}
// if it is FILENAMENODEP
case CEntityProperty::EPT_FILENAMENODEP: {
// skip FILENAMENODEP
CTFileNameNoDep fnmDummy;
istrm>>fnmDummy;
break;
}
// if it is ENTITYPTR
case CEntityProperty::EPT_ENTITYPTR: {
// skip index
INDEX iDummy;
istrm>>iDummy;
}
break;
// if it is FLOATAABBOX3D
case CEntityProperty::EPT_FLOATAABBOX3D: {
// skip FLOATAABBOX3D
FLOATaabbox3D boxDummy;
istrm>>boxDummy;
}
break;
// if it is FLOATMATRIX3D
case CEntityProperty::EPT_FLOATMATRIX3D: {
// skip FLOATMATRIX3D
FLOATmatrix3D boxDummy;
istrm>>boxDummy;
}
break;
// if it is EPT_FLOATQUAT3D
case CEntityProperty::EPT_FLOATQUAT3D: {
// skip EPT_FLOATQUAT3D
FLOATquat3D qDummy;
istrm>>qDummy;
}
break;
// if it is FLOAT3D
case CEntityProperty::EPT_FLOAT3D: {
// skip FLOAT3D
FLOAT3D vDummy;
istrm>>vDummy;
}
break;
// if it is ANGLE3D
case CEntityProperty::EPT_ANGLE3D: {
// skip ANGLE3D
ANGLE3D vDummy;
istrm>>vDummy;
}
break;
// if it is FLOATplane3D
case CEntityProperty::EPT_FLOATplane3D: {
// skip FLOATplane3D
FLOATplane3D plDummy;
istrm>>plDummy;
}
break;
// if it is MODELOBJECT
case CEntityProperty::EPT_MODELOBJECT:
// skip CModelObject
SkipModelObject_t(istrm);
break;
// if it is MODELINSTANCE
case CEntityProperty::EPT_MODELINSTANCE:
SkipModelInstance_t(istrm);
break;
// if it is ANIMOBJECT
case CEntityProperty::EPT_ANIMOBJECT:
// skip CAnimObject
SkipAnimObject_t(istrm);
break;
// if it is SOUNDOBJECT
case CEntityProperty::EPT_SOUNDOBJECT:
// skip CSoundObject
SkipSoundObject_t(istrm);
break;
default:
ASSERTALWAYS("Unknown property type");
}
// if it was found
} else {
// fixup for loading old strings as translatable strings
CEntityProperty::PropertyType eptLoad = pepProperty->ep_eptType;
if (eptType==CEntityProperty::EPT_STRING &&
eptLoad==CEntityProperty::EPT_STRINGTRANS) {
eptLoad = CEntityProperty::EPT_STRING;
}
// depending on the property type
switch (eptLoad) {
// if it is BOOL
case CEntityProperty::EPT_BOOL:
// read BOOL
istrm>>(INDEX &)PROPERTY(pepProperty->ep_slOffset, BOOL);
break;
// if it is INDEX
case CEntityProperty::EPT_INDEX:
case CEntityProperty::EPT_ENUM:
case CEntityProperty::EPT_FLAGS:
case CEntityProperty::EPT_ANIMATION:
case CEntityProperty::EPT_ILLUMINATIONTYPE:
case CEntityProperty::EPT_COLOR:
case CEntityProperty::EPT_ANGLE:
// read INDEX
istrm>>PROPERTY(pepProperty->ep_slOffset, INDEX);
break;
// if it is FLOAT
case CEntityProperty::EPT_FLOAT:
case CEntityProperty::EPT_RANGE:
// read FLOAT
istrm>>PROPERTY(pepProperty->ep_slOffset, FLOAT);
break;
// if it is STRING
case CEntityProperty::EPT_STRING:
// read STRING
istrm>>PROPERTY(pepProperty->ep_slOffset, CTString);
break;
// if it is STRINGTRANS
case CEntityProperty::EPT_STRINGTRANS:
// read STRINGTRANS
istrm.ExpectID_t("DTRS");
istrm>>PROPERTY(pepProperty->ep_slOffset, CTString);
break;
// if it is FILENAME
case CEntityProperty::EPT_FILENAME:
// read FILENAME
istrm>>PROPERTY(pepProperty->ep_slOffset, CTFileName);
if (PROPERTY(pepProperty->ep_slOffset, CTFileName)=="") {
break;
}
// try to replace file name if it doesn't exist
for(;;)
{
if( !FileExists( PROPERTY(pepProperty->ep_slOffset, CTFileName)))
{
// if file was not found, ask for replacing file
CTFileName fnReplacingFile;
if( GetReplacingFile( PROPERTY(pepProperty->ep_slOffset, CTFileName),
fnReplacingFile, FILTER_ALL FILTER_END))
{
// replacing file was provided
PROPERTY(pepProperty->ep_slOffset, CTFileName) = fnReplacingFile;
} else {
ThrowF_t(TRANS("File '%s' does not exist"), (const char*)PROPERTY(pepProperty->ep_slOffset, CTFileName));
}
}
else
{
break;
}
}
break;
// if it is FILENAMENODEP
case CEntityProperty::EPT_FILENAMENODEP:
// read FILENAMENODEP
istrm>>PROPERTY(pepProperty->ep_slOffset, CTFileNameNoDep);
break;
// if it is ENTITYPTR
case CEntityProperty::EPT_ENTITYPTR:
// read the entity pointer
ReadEntityPointer_t(&istrm, PROPERTY(pepProperty->ep_slOffset, CEntityPointer));
break;
// if it is FLOATAABBOX3D
case CEntityProperty::EPT_FLOATAABBOX3D:
// read FLOATAABBOX3D
istrm>>(PROPERTY(pepProperty->ep_slOffset, FLOATaabbox3D));
break;
// if it is FLOATMATRIX3D
case CEntityProperty::EPT_FLOATMATRIX3D:
// read FLOATMATRIX3D
istrm>>(PROPERTY(pepProperty->ep_slOffset, FLOATmatrix3D));
break;
// if it is FLOATQUAT3D
case CEntityProperty::EPT_FLOATQUAT3D:
// read FLOATQUAT3D
istrm>>(PROPERTY(pepProperty->ep_slOffset, FLOATquat3D));
break;
// if it is FLOAT3D
case CEntityProperty::EPT_FLOAT3D:
// read FLOAT3D
istrm>>(PROPERTY(pepProperty->ep_slOffset, FLOAT3D));
break;
// if it is ANGLE3D
case CEntityProperty::EPT_ANGLE3D:
// read ANGLE3D
istrm>>(PROPERTY(pepProperty->ep_slOffset, ANGLE3D));
break;
// if it is FLOATplane3D
case CEntityProperty::EPT_FLOATplane3D:
// read FLOATplane3D
istrm>>(PROPERTY(pepProperty->ep_slOffset, FLOATplane3D));
break;
// if it is MODELOBJECT
case CEntityProperty::EPT_MODELOBJECT:
// read CModelObject
ReadModelObject_t(istrm, PROPERTY(pepProperty->ep_slOffset, CModelObject));
break;
// if it is MODELINSTANCE
case CEntityProperty::EPT_MODELINSTANCE:
// read CModelObject
ReadModelInstance_t(istrm, PROPERTY(pepProperty->ep_slOffset, CModelInstance));
break;
// if it is ANIMOBJECT
case CEntityProperty::EPT_ANIMOBJECT:
// read CAnimObject
ReadAnimObject_t(istrm, PROPERTY(pepProperty->ep_slOffset, CAnimObject));
break;
// if it is SOUNDOBJECT
case CEntityProperty::EPT_SOUNDOBJECT:
// read CSoundObject
{
CSoundObject &so = PROPERTY(pepProperty->ep_slOffset, CSoundObject);
ReadSoundObject_t(istrm, so);
so.so_penEntity = this;
}
break;
// if it is CPlacement3D
case CEntityProperty::EPT_PLACEMENT3D:
// read CPlacement3D
istrm>>(PROPERTY(pepProperty->ep_slOffset, CPlacement3D));
break;
default:
ASSERTALWAYS("Unknown property type");
}
}
}
}
/*
* Write all properties to a stream.
*/
void CEntity::WriteProperties_t(CTStream &ostrm) // throw char *
{
INDEX ctProperties = 0;
// for all classes in hierarchy of this entity
{for(CDLLEntityClass *pdecDLLClass = en_pecClass->ec_pdecDLLClass;
pdecDLLClass!=NULL;
pdecDLLClass = pdecDLLClass->dec_pdecBase) {
// count the properties
ctProperties+=pdecDLLClass->dec_ctProperties;
}}
ostrm.WriteID_t("PRPS"); // 'properties'
// write number of properties
ostrm<<ctProperties;
// for all classes in hierarchy of this entity
{for(CDLLEntityClass *pdecDLLClass = en_pecClass->ec_pdecDLLClass;
pdecDLLClass!=NULL;
pdecDLLClass = pdecDLLClass->dec_pdecBase) {
// for all properties
for(INDEX iProperty=0; iProperty<pdecDLLClass->dec_ctProperties; iProperty++) {
CEntityProperty &epProperty = pdecDLLClass->dec_aepProperties[iProperty];
// pack property ID and property type together
ULONG ulID = epProperty.ep_ulID;
ULONG ulType = (ULONG)epProperty.ep_eptType;
ULONG ulIDAndType = (ulID<<8)|ulType;
// write the packed identifier
ostrm<<ulIDAndType;
// depending on the property type
switch (epProperty.ep_eptType) {
// if it is BOOL
case CEntityProperty::EPT_BOOL:
// write BOOL
ostrm<<(INDEX &)PROPERTY(epProperty.ep_slOffset, BOOL);
break;
// if it is INDEX
case CEntityProperty::EPT_INDEX:
case CEntityProperty::EPT_ENUM:
case CEntityProperty::EPT_FLAGS:
case CEntityProperty::EPT_ANIMATION:
case CEntityProperty::EPT_ILLUMINATIONTYPE:
case CEntityProperty::EPT_COLOR:
case CEntityProperty::EPT_ANGLE:
// write INDEX
ostrm<<PROPERTY(epProperty.ep_slOffset, INDEX);
break;
// if it is FLOAT
case CEntityProperty::EPT_FLOAT:
case CEntityProperty::EPT_RANGE:
// write FLOAT
ostrm<<PROPERTY(epProperty.ep_slOffset, FLOAT);
break;
// if it is STRING
case CEntityProperty::EPT_STRING:
// write STRING
ostrm<<PROPERTY(epProperty.ep_slOffset, CTString);
break;
// if it is STRINGTRANS
case CEntityProperty::EPT_STRINGTRANS:
// write STRINGTRANS
ostrm.WriteID_t("DTRS");
ostrm<<PROPERTY(epProperty.ep_slOffset, CTString);
break;
// if it is FILENAME
case CEntityProperty::EPT_FILENAME:
// write FILENAME
ostrm<<PROPERTY(epProperty.ep_slOffset, CTFileName);
break;
// if it is FILENAMENODEP
case CEntityProperty::EPT_FILENAMENODEP:
// write FILENAMENODEP
ostrm<<PROPERTY(epProperty.ep_slOffset, CTFileNameNoDep);
break;
// if it is FLOATAABBOX3D
case CEntityProperty::EPT_FLOATAABBOX3D:
// write FLOATAABBOX3D
ostrm<<PROPERTY(epProperty.ep_slOffset, FLOATaabbox3D);
break;
// if it is FLOATMATRIX3D
case CEntityProperty::EPT_FLOATMATRIX3D:
// write FLOATMATRIX3D
ostrm<<PROPERTY(epProperty.ep_slOffset, FLOATmatrix3D);
break;
// if it is FLOATQUAT3D
case CEntityProperty::EPT_FLOATQUAT3D:
// write FLOATQUAT3D
ostrm<<PROPERTY(epProperty.ep_slOffset, FLOATquat3D);
break;
// if it is ANGLE3D
case CEntityProperty::EPT_ANGLE3D:
// write ANGLE3D
ostrm<<PROPERTY(epProperty.ep_slOffset, ANGLE3D);
break;
// if it is FLOAT3D
case CEntityProperty::EPT_FLOAT3D:
// write FLOAT3D
ostrm<<PROPERTY(epProperty.ep_slOffset, FLOAT3D);
break;
// if it is FLOATplane3D
case CEntityProperty::EPT_FLOATplane3D:
// write FLOATplane3D
ostrm<<PROPERTY(epProperty.ep_slOffset, FLOATplane3D);
break;
// if it is ENTITYPTR
case CEntityProperty::EPT_ENTITYPTR:
// write entity pointer
WriteEntityPointer_t(&ostrm, PROPERTY(epProperty.ep_slOffset, CEntityPointer));
break;
// if it is MODELOBJECT
case CEntityProperty::EPT_MODELOBJECT:
// write CModelObject
WriteModelObject_t(ostrm, PROPERTY(epProperty.ep_slOffset, CModelObject));
break;
// if it is MODELINSTANCE
case CEntityProperty::EPT_MODELINSTANCE:
// write CModelInstance
WriteModelInstance_t(ostrm, PROPERTY(epProperty.ep_slOffset, CModelInstance));
break;
// if it is ANIMOBJECT
case CEntityProperty::EPT_ANIMOBJECT:
// write CAnimObject
WriteAnimObject_t(ostrm, PROPERTY(epProperty.ep_slOffset, CAnimObject));
break;
// if it is SOUNDOBJECT
case CEntityProperty::EPT_SOUNDOBJECT:
// write CSoundObject
WriteSoundObject_t(ostrm, PROPERTY(epProperty.ep_slOffset, CSoundObject));
break;
// if it is CPlacement3D
case CEntityProperty::EPT_PLACEMENT3D:
// write CPlacement3D
ostrm<<PROPERTY(epProperty.ep_slOffset, CPlacement3D);
break;
default:
ASSERTALWAYS("Unknown property type");
}
}
}}
}
/////////////////////////////////////////////////////////////////////
// Component management functions
/*
* Obtain the component.
*/
void CEntityComponent::Obtain_t(void) // throw char *
{
// if obtained
if (ec_pvPointer!=NULL) {
// just add to CRC
AddToCRCTable();
// do not obtain again
return;
}
INDEX ctUsed = 0;
// check the component type
switch(ec_ectType) {
// if texture
case ECT_TEXTURE:
// obtain texture data
ec_ptdTexture = _pTextureStock->Obtain_t(ec_fnmComponent);
ctUsed = ec_ptdTexture->GetUsedCount();
break;
// if model
case ECT_MODEL:
// obtain model data
ec_pmdModel = _pModelStock->Obtain_t(ec_fnmComponent);
ctUsed = ec_pmdModel->GetUsedCount();
break;
// if sound
case ECT_SOUND:
// obtain sound data
ec_psdSound = _pSoundStock->Obtain_t(ec_fnmComponent);
ctUsed = ec_psdSound->GetUsedCount();
break;
// if class
case ECT_CLASS:
// obtain entity class
ec_pecEntityClass = _pEntityClassStock->Obtain_t(ec_fnmComponent);
ctUsed = ec_pecEntityClass->GetUsedCount();
break;
// if something else
default:
// error
ThrowF_t(TRANS("Component '%s'(%d) is of unknown type!"), (const char *) (CTString&)ec_fnmComponent, ec_slID);
}
// if not already loaded and should not be precaching now
if( ctUsed<=1 && !_precache_bNowPrecaching) {
// report warning
CPrintF(TRANSV("Not precached: (0x%08X)'%s'\n"), this->ec_slID, (const char *) ec_fnmComponent);
}
//CPrintF(TRANSV("Precaching NOW: (0x%08X)'%s'\n"), this->ec_slID, (const char *) ec_fnmComponent);
// add to CRC
AddToCRCTable();
}
void CEntityComponent::ObtainWithCheck(void)
{
try {
Obtain_t();
} catch(char *strError) {
FatalError("%s", strError);
}
}
// add component to crc table
void CEntityComponent::AddToCRCTable(void)
{
// if not obtained
if (ec_pvPointer==NULL) {
// do nothing
return;
}
// add it
switch(ec_ectType) {
case ECT_TEXTURE: ec_ptdTexture->AddToCRCTable(); break;
case ECT_MODEL: ec_pmdModel->AddToCRCTable(); break;
case ECT_SOUND: ec_psdSound->AddToCRCTable(); break;
case ECT_CLASS: ec_pecEntityClass->AddToCRCTable(); break;
}
}
/*
* Release the component.
*/
void CEntityComponent::Release(void)
{
// if the component is not obtained
if (ec_pvPointer==NULL) {
// don't release it
return;
}
// check the component type
switch(ec_ectType) {
// if texture
case ECT_TEXTURE:
// release texture data
_pTextureStock->Release(ec_ptdTexture);
break;
// if model
case ECT_MODEL:
// release model data
_pModelStock->Release(ec_pmdModel);
break;
// if sound
case ECT_SOUND:
// release sound data
_pSoundStock->Release(ec_psdSound);
break;
// if class
case ECT_CLASS:
// release entity class
_pEntityClassStock->Release(ec_pecEntityClass);
break;
// if something else
default:
// error
ThrowF_t(TRANS("Component '%s'(%d) is of unknown type!"), (const char *) (CTString&)ec_fnmComponent, ec_slID);
}
// released
ec_pvPointer=NULL;
}
// these entity classes are bases, here stop all recursive searches
ENTITY_CLASSDEFINITION_BASE(CEntity, 32000);
ENTITY_CLASSDEFINITION_BASE(CLiveEntity, 32001);
ENTITY_CLASSDEFINITION_BASE(CRationalEntity, 32002);