1292 lines
34 KiB
C++
1292 lines
34 KiB
C++
//=======================================================================
|
|
// Copyright (C) XashXT Group 2006
|
|
//=======================================================================
|
|
|
|
#include "extdll.h"
|
|
#include "utils.h"
|
|
#include "cbase.h"
|
|
#include "defaults.h"
|
|
#include "saverestore.h"
|
|
#include <time.h>
|
|
#include "shake.h"
|
|
#include "decals.h"
|
|
#include "player.h"
|
|
#include "baseweapon.h"
|
|
#include "gamerules.h"
|
|
#include "client.h"
|
|
|
|
#define ENTVARS_COUNT ( sizeof(gEntvarsDescription) / sizeof(gEntvarsDescription[0]) )
|
|
CGlobalState gGlobalState;
|
|
|
|
TYPEDESCRIPTION gEntvarsDescription[] =
|
|
{
|
|
DEFINE_ENTITY_FIELD( classname, FIELD_STRING ),
|
|
DEFINE_ENTITY_GLOBAL_FIELD( globalname, FIELD_STRING ),
|
|
|
|
DEFINE_ENTITY_FIELD( origin, FIELD_POSITION_VECTOR ),
|
|
DEFINE_ENTITY_FIELD( oldorigin, FIELD_POSITION_VECTOR ),
|
|
DEFINE_ENTITY_FIELD( velocity, FIELD_VECTOR ),
|
|
DEFINE_ENTITY_FIELD( movedir, FIELD_VECTOR ),
|
|
|
|
DEFINE_ENTITY_FIELD( angles, FIELD_VECTOR ),
|
|
DEFINE_ENTITY_FIELD( avelocity, FIELD_VECTOR ),
|
|
DEFINE_ENTITY_FIELD( punchangle, FIELD_VECTOR ),
|
|
DEFINE_ENTITY_FIELD( viewangles, FIELD_VECTOR ),
|
|
DEFINE_ENTITY_FIELD( fixangle, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( ideal_pitch, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( pitch_speed, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( ideal_yaw, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( yaw_speed, FIELD_FLOAT ),
|
|
|
|
DEFINE_ENTITY_FIELD( modelindex, FIELD_INTEGER ),
|
|
DEFINE_ENTITY_GLOBAL_FIELD( model, FIELD_MODELNAME ),
|
|
|
|
DEFINE_ENTITY_FIELD( viewmodel, FIELD_MODELNAME ),
|
|
DEFINE_ENTITY_FIELD( weaponmodel, FIELD_MODELNAME ),
|
|
|
|
DEFINE_ENTITY_FIELD( absmin, FIELD_POSITION_VECTOR ),
|
|
DEFINE_ENTITY_FIELD( absmax, FIELD_POSITION_VECTOR ),
|
|
DEFINE_ENTITY_GLOBAL_FIELD( mins, FIELD_VECTOR ),
|
|
DEFINE_ENTITY_GLOBAL_FIELD( maxs, FIELD_VECTOR ),
|
|
DEFINE_ENTITY_GLOBAL_FIELD( size, FIELD_VECTOR ),
|
|
|
|
DEFINE_ENTITY_FIELD( ltime, FIELD_TIME ),
|
|
DEFINE_ENTITY_FIELD( nextthink, FIELD_TIME ),
|
|
|
|
DEFINE_ENTITY_FIELD( solid, FIELD_INTEGER ),
|
|
DEFINE_ENTITY_FIELD( movetype, FIELD_INTEGER ),
|
|
|
|
DEFINE_ENTITY_FIELD( skin, FIELD_INTEGER ),
|
|
DEFINE_ENTITY_FIELD( body, FIELD_INTEGER ),
|
|
DEFINE_ENTITY_FIELD( effects, FIELD_INTEGER ),
|
|
|
|
DEFINE_ENTITY_FIELD( gravity, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( friction, FIELD_FLOAT ),
|
|
|
|
DEFINE_ENTITY_FIELD( frame, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( scale, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( sequence, FIELD_INTEGER ),
|
|
DEFINE_ENTITY_FIELD( animtime, FIELD_TIME ),
|
|
DEFINE_ENTITY_FIELD( framerate, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( controller, FIELD_INTEGER ),
|
|
DEFINE_ENTITY_FIELD( blending, FIELD_INTEGER ),
|
|
|
|
DEFINE_ENTITY_FIELD( rendermode, FIELD_INTEGER ),
|
|
DEFINE_ENTITY_FIELD( renderamt, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( rendercolor, FIELD_VECTOR ),
|
|
DEFINE_ENTITY_FIELD( renderfx, FIELD_INTEGER ),
|
|
|
|
DEFINE_ENTITY_FIELD( health, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( frags, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( weapons, FIELD_INTEGER64 ),
|
|
DEFINE_ENTITY_FIELD( takedamage, FIELD_FLOAT ),
|
|
|
|
DEFINE_ENTITY_FIELD( deadflag, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( view_ofs, FIELD_VECTOR ),
|
|
DEFINE_ENTITY_FIELD( button, FIELD_INTEGER ),
|
|
DEFINE_ENTITY_FIELD( impulse, FIELD_INTEGER ),
|
|
|
|
DEFINE_ENTITY_FIELD( chain, FIELD_EDICT ),
|
|
DEFINE_ENTITY_FIELD( dmg_inflictor, FIELD_EDICT ),
|
|
DEFINE_ENTITY_FIELD( enemy, FIELD_EDICT ),
|
|
DEFINE_ENTITY_FIELD( aiment, FIELD_EDICT ),
|
|
DEFINE_ENTITY_FIELD( owner, FIELD_EDICT ),
|
|
DEFINE_ENTITY_FIELD( groundentity, FIELD_EDICT ),
|
|
|
|
DEFINE_ENTITY_FIELD( spawnflags, FIELD_INTEGER ),
|
|
DEFINE_ENTITY_FIELD( flags, FIELD_INTEGER64 ),
|
|
#if 0
|
|
DEFINE_ENTITY_FIELD_ARRAY( m_pmatrix, FIELD_VECTOR, 3 ),
|
|
DEFINE_ENTITY_FIELD_ARRAY( m_pcentre, FIELD_VECTOR, 3 ),
|
|
#else
|
|
DEFINE_ENTITY_FIELD( m_pmatrix[0], FIELD_VECTOR ),
|
|
DEFINE_ENTITY_FIELD( m_pmatrix[1], FIELD_VECTOR ),
|
|
DEFINE_ENTITY_FIELD( m_pmatrix[2], FIELD_VECTOR ),
|
|
DEFINE_ENTITY_FIELD( m_pcentre[0], FIELD_VECTOR ),
|
|
DEFINE_ENTITY_FIELD( m_pcentre[1], FIELD_VECTOR ),
|
|
DEFINE_ENTITY_FIELD( m_pcentre[2], FIELD_VECTOR ),
|
|
#endif
|
|
DEFINE_ENTITY_FIELD( colormap, FIELD_INTEGER ),
|
|
DEFINE_ENTITY_FIELD( team, FIELD_INTEGER ),
|
|
|
|
DEFINE_ENTITY_FIELD( max_health, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( teleport_time, FIELD_TIME ),
|
|
DEFINE_ENTITY_FIELD( armortype, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( armorvalue, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( waterlevel, FIELD_INTEGER ),
|
|
DEFINE_ENTITY_FIELD( watertype, FIELD_INTEGER ),
|
|
|
|
// Having these fields be local to the individual levels makes it easier to test those levels individually.
|
|
DEFINE_ENTITY_GLOBAL_FIELD( target, FIELD_STRING ),
|
|
DEFINE_ENTITY_GLOBAL_FIELD( targetname, FIELD_STRING ),
|
|
DEFINE_ENTITY_FIELD( netname, FIELD_STRING ),
|
|
DEFINE_ENTITY_FIELD( message, FIELD_STRING ),
|
|
|
|
DEFINE_ENTITY_FIELD( dmg_take, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( dmg_save, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( dmg, FIELD_FLOAT ),
|
|
DEFINE_ENTITY_FIELD( dmgtime, FIELD_TIME ),
|
|
DEFINE_ENTITY_FIELD( fov, FIELD_FLOAT ),
|
|
|
|
DEFINE_ENTITY_FIELD( noise, FIELD_SOUNDNAME ),
|
|
DEFINE_ENTITY_FIELD( noise1, FIELD_SOUNDNAME ),
|
|
DEFINE_ENTITY_FIELD( noise2, FIELD_SOUNDNAME ),
|
|
DEFINE_ENTITY_FIELD( noise3, FIELD_SOUNDNAME ),
|
|
DEFINE_ENTITY_FIELD( speed, FIELD_FLOAT ),
|
|
};
|
|
|
|
|
|
// --------------------------------------------------------------
|
|
//
|
|
// CSave
|
|
//
|
|
// --------------------------------------------------------------
|
|
static int gSizes[FIELD_TYPECOUNT] =
|
|
{
|
|
sizeof(float), // FIELD_FLOAT
|
|
sizeof(int), // FIELD_STRING
|
|
sizeof(int), // FIELD_ENTITY
|
|
sizeof(int), // FIELD_CLASSPTR
|
|
sizeof(int), // FIELD_EHANDLE
|
|
sizeof(int), // FIELD_entvars_t
|
|
sizeof(int), // FIELD_EDICT
|
|
sizeof(float)*3, // FIELD_VECTOR
|
|
sizeof(float)*3, // FIELD_POSITION_VECTOR
|
|
sizeof(int *), // FIELD_POINTER
|
|
sizeof(int), // FIELD_INTEGER
|
|
sizeof(int *), // FIELD_FUNCTION
|
|
sizeof(int), // FIELD_BOOLEAN
|
|
sizeof(short), // FIELD_SHORT
|
|
sizeof(char), // FIELD_CHARACTER
|
|
sizeof(float), // FIELD_TIME
|
|
sizeof(int), // FIELD_MODELNAME
|
|
sizeof(int), // FIELD_SOUNDNAME
|
|
sizeof(float)*2, // FIELD_RANGE
|
|
sizeof(int64), // FIELD_INTEGER64
|
|
sizeof(double), // FIELD_DOUBLE
|
|
};
|
|
|
|
static const char *gNames[FIELD_TYPECOUNT] =
|
|
{
|
|
"float", // FIELD_FLOAT
|
|
"string", // FIELD_STRING
|
|
"entity", // FIELD_ENTITY
|
|
"classptr", // FIELD_CLASSPTR
|
|
"ehandle", // FIELD_EHANDLE
|
|
"entvars", // FIELD_entvars_t
|
|
"edict", // FIELD_EDICT
|
|
"vector", // FIELD_VECTOR
|
|
"position vector", // FIELD_POSITION_VECTOR
|
|
"pointer", // FIELD_POINTER
|
|
"integer", // FIELD_INTEGER
|
|
"function", // FIELD_FUNCTION
|
|
"boolean", // FIELD_BOOLEAN
|
|
"short", // FIELD_SHORT
|
|
"char", // FIELD_CHARACTER
|
|
"time", // FIELD_TIME
|
|
"modelname", // FIELD_MODELNAME
|
|
"soundname", // FIELD_SOUNDNAME
|
|
"random range", // FIELD_RANGE
|
|
"integer 64", // FIELD_INTEGER64
|
|
"double", // FIELD_DOUBLE
|
|
};
|
|
|
|
// Base class includes common SAVERESTOREDATA pointer, and manages the entity table
|
|
CSaveRestoreBuffer :: CSaveRestoreBuffer( void )
|
|
{
|
|
m_pdata = NULL;
|
|
}
|
|
|
|
|
|
CSaveRestoreBuffer :: CSaveRestoreBuffer( SAVERESTOREDATA *pdata )
|
|
{
|
|
m_pdata = pdata;
|
|
}
|
|
|
|
|
|
CSaveRestoreBuffer :: ~CSaveRestoreBuffer( void )
|
|
{
|
|
}
|
|
|
|
int CSaveRestoreBuffer :: EntityIndex( CBaseEntity *pEntity )
|
|
{
|
|
if ( pEntity == NULL )
|
|
return -1;
|
|
return EntityIndex( pEntity->pev );
|
|
}
|
|
|
|
|
|
int CSaveRestoreBuffer :: EntityIndex( entvars_t *pevLookup )
|
|
{
|
|
if ( pevLookup == NULL )
|
|
return -1;
|
|
return EntityIndex( ENT( pevLookup ) );
|
|
}
|
|
|
|
int CSaveRestoreBuffer :: EntityIndex( EOFFSET eoLookup )
|
|
{
|
|
return EntityIndex( ENT( eoLookup ) );
|
|
}
|
|
|
|
|
|
int CSaveRestoreBuffer :: EntityIndex( edict_t *pentLookup )
|
|
{
|
|
if ( !m_pdata || pentLookup == NULL )
|
|
return -1;
|
|
|
|
int i;
|
|
ENTITYTABLE *pTable;
|
|
|
|
for ( i = 0; i < m_pdata->tableCount; i++ )
|
|
{
|
|
pTable = m_pdata->pTable + i;
|
|
if ( pTable->pent == pentLookup )
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
edict_t *CSaveRestoreBuffer :: EntityFromIndex( int entityIndex )
|
|
{
|
|
if ( !m_pdata || entityIndex < 0 )
|
|
return NULL;
|
|
|
|
int i;
|
|
ENTITYTABLE *pTable;
|
|
|
|
for ( i = 0; i < m_pdata->tableCount; i++ )
|
|
{
|
|
pTable = m_pdata->pTable + i;
|
|
if ( pTable->id == entityIndex )
|
|
return pTable->pent;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int CSaveRestoreBuffer :: EntityFlagsSet( int entityIndex, int flags )
|
|
{
|
|
if ( !m_pdata || entityIndex < 0 )
|
|
return 0;
|
|
if ( entityIndex > m_pdata->tableCount )
|
|
return 0;
|
|
|
|
m_pdata->pTable[ entityIndex ].flags |= flags;
|
|
|
|
return m_pdata->pTable[ entityIndex ].flags;
|
|
}
|
|
|
|
|
|
void CSaveRestoreBuffer :: BufferRewind( int size )
|
|
{
|
|
if ( !m_pdata )
|
|
return;
|
|
|
|
if ( m_pdata->size < size )
|
|
size = m_pdata->size;
|
|
|
|
m_pdata->pCurrentData -= size;
|
|
m_pdata->size -= size;
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
extern "C" {
|
|
unsigned _rotr ( unsigned val, int shift)
|
|
{
|
|
register unsigned lobit; /* non-zero means lo bit set */
|
|
register unsigned num = val; /* number to rotate */
|
|
|
|
shift &= 0x1f; /* modulo 32 -- this will also make
|
|
negative shifts work */
|
|
|
|
while (shift--) {
|
|
lobit = num & 1; /* get high bit */
|
|
num >>= 1; /* shift right one bit */
|
|
if (lobit)
|
|
num |= 0x80000000; /* set hi bit if lo bit was set */
|
|
}
|
|
|
|
return num;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
unsigned int CSaveRestoreBuffer :: HashString( const char *pszToken )
|
|
{
|
|
unsigned int hash = 0;
|
|
|
|
while ( *pszToken )
|
|
hash = _rotr( hash, 4 ) ^ *pszToken++;
|
|
|
|
return hash;
|
|
}
|
|
|
|
unsigned short CSaveRestoreBuffer :: TokenHash( const char *pszToken )
|
|
{
|
|
unsigned short hash = (unsigned short)(HashString( pszToken ) % (unsigned)m_pdata->tokenCount );
|
|
|
|
#if _DEBUG
|
|
static int tokensparsed = 0;
|
|
tokensparsed++;
|
|
if ( !m_pdata->tokenCount || !m_pdata->pTokens )
|
|
ALERT( at_error, "No token table array in TokenHash()!" );
|
|
#endif
|
|
|
|
for ( int i=0; i<m_pdata->tokenCount; i++ )
|
|
{
|
|
#if _DEBUG
|
|
static BOOL beentheredonethat = FALSE;
|
|
if ( i > 50 && !beentheredonethat )
|
|
{
|
|
beentheredonethat = TRUE;
|
|
ALERT( at_error, "CSaveRestoreBuffer :: TokenHash() is getting too full!" );
|
|
}
|
|
#endif
|
|
|
|
int index = hash + i;
|
|
if ( index >= m_pdata->tokenCount )
|
|
index -= m_pdata->tokenCount;
|
|
|
|
if ( !m_pdata->pTokens[index] || strcmp( pszToken, m_pdata->pTokens[index] ) == 0 )
|
|
{
|
|
m_pdata->pTokens[index] = (char *)pszToken;
|
|
return index;
|
|
}
|
|
}
|
|
|
|
// Token hash table full!!!
|
|
// [Consider doing overflow table(s) after the main table & limiting linear hash table search]
|
|
ALERT( at_error, "CSaveRestoreBuffer :: TokenHash() is COMPLETELY FULL!" );
|
|
return 0;
|
|
}
|
|
|
|
void CSave :: WriteData( const char *pname, int size, const char *pdata )
|
|
{
|
|
BufferField( pname, size, pdata );
|
|
}
|
|
|
|
|
|
void CSave :: WriteShort( const char *pname, const short *data, int count )
|
|
{
|
|
BufferField( pname, sizeof(short) * count, (const char *)data );
|
|
}
|
|
|
|
|
|
void CSave :: WriteInt( const char *pname, const int *data, int count )
|
|
{
|
|
BufferField( pname, sizeof(int) * count, (const char *)data );
|
|
}
|
|
|
|
void CSave :: WriteInt64( const char *pname, const int64 *data, int count )
|
|
{
|
|
BufferField( pname, sizeof(int64) * count, (const char *)data );
|
|
}
|
|
|
|
void CSave :: WriteFloat( const char *pname, const float *data, int count )
|
|
{
|
|
BufferField( pname, sizeof(float) * count, (const char *)data );
|
|
}
|
|
|
|
void CSave :: WriteDouble( const char *pname, const double *data, int count )
|
|
{
|
|
BufferField( pname, sizeof( double ) * count, (const char *)data );
|
|
}
|
|
|
|
void CSave :: WriteTime( const char *pname, const float *data, int count )
|
|
{
|
|
int i;
|
|
Vector tmp, input;
|
|
|
|
BufferHeader( pname, sizeof(float) * count );
|
|
for ( i = 0; i < count; i++ )
|
|
{
|
|
float tmp = data[0];
|
|
|
|
// Always encode time as a delta from the current time so it can be re-based if loaded in a new level
|
|
// Times of 0 are never written to the file, so they will be restored as 0, not a relative time
|
|
if( m_pdata ) tmp -= m_pdata->time;
|
|
|
|
BufferData( (const char *)&tmp, sizeof( float ));
|
|
data++;
|
|
}
|
|
}
|
|
|
|
|
|
void CSave :: WriteString( const char *pname, const char *pdata )
|
|
{
|
|
#ifdef TOKENIZE
|
|
short token = (short)TokenHash( pdata );
|
|
WriteShort( pname, &token, 1 );
|
|
#else
|
|
BufferField( pname, strlen(pdata) + 1, pdata );
|
|
#endif
|
|
}
|
|
|
|
|
|
void CSave :: WriteString( const char *pname, const int *stringId, int count )
|
|
{
|
|
int i, size;
|
|
|
|
#ifdef TOKENIZE
|
|
short token = (short)TokenHash( STRING( *stringId ) );
|
|
WriteShort( pname, &token, 1 );
|
|
#else
|
|
#if 0
|
|
if ( count != 1 )
|
|
ALERT( at_error, "No string arrays!\n" );
|
|
WriteString( pname, (char *)STRING(*stringId) );
|
|
#endif
|
|
|
|
size = 0;
|
|
for ( i = 0; i < count; i++ )
|
|
size += strlen( STRING( stringId[i] ) ) + 1;
|
|
|
|
BufferHeader( pname, size );
|
|
for ( i = 0; i < count; i++ )
|
|
{
|
|
const char *pString = STRING(stringId[i]);
|
|
BufferData( pString, strlen(pString)+1 );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
void CSave :: WriteVector( const char *pname, const Vector &value )
|
|
{
|
|
WriteVector( pname, &value.x, 1 );
|
|
}
|
|
|
|
void CSave :: WriteRange( const char *pname, const RandomRange &value )
|
|
{
|
|
WriteRange( pname, &value.m_flMin, 1 );
|
|
}
|
|
|
|
void CSave :: WriteRange( const char *pname, const float *value, int count )
|
|
{
|
|
BufferHeader( pname, sizeof(float) * 2 * count );
|
|
BufferData( (const char *)value, sizeof(float) * 2 * count );
|
|
}
|
|
|
|
void CSave :: WriteVector( const char *pname, const float *value, int count )
|
|
{
|
|
BufferHeader( pname, sizeof(float) * 3 * count );
|
|
BufferData( (const char *)value, sizeof(float) * 3 * count );
|
|
}
|
|
|
|
|
|
|
|
void CSave :: WritePositionVector( const char *pname, const Vector &value )
|
|
{
|
|
|
|
if ( m_pdata && m_pdata->fUseLandmark )
|
|
{
|
|
Vector tmp = value - m_pdata->vecLandmarkOffset;
|
|
WriteVector( pname, tmp );
|
|
}
|
|
|
|
WriteVector( pname, value );
|
|
}
|
|
|
|
|
|
void CSave :: WritePositionVector( const char *pname, const float *value, int count )
|
|
{
|
|
int i;
|
|
Vector tmp, input;
|
|
|
|
BufferHeader( pname, sizeof(float) * 3 * count );
|
|
for ( i = 0; i < count; i++ )
|
|
{
|
|
Vector tmp( value[0], value[1], value[2] );
|
|
|
|
if ( m_pdata && m_pdata->fUseLandmark )
|
|
tmp = tmp - m_pdata->vecLandmarkOffset;
|
|
|
|
BufferData( (const char *)&tmp.x, sizeof(float) * 3 );
|
|
value += 3;
|
|
}
|
|
}
|
|
|
|
|
|
void CSave :: WriteFunction( const char* cname, const char *pname, const int *data, int count )
|
|
{
|
|
const char *functionName;
|
|
|
|
functionName = NAME_FOR_FUNCTION( *data );
|
|
if ( functionName )
|
|
BufferField( pname, strlen(functionName) + 1, functionName );
|
|
else ALERT( at_error, "Member \"%s\" of \"%s\" contains an invalid function pointer %p!", pname, cname, *data );
|
|
}
|
|
|
|
|
|
void EntvarsKeyvalue( entvars_t *pev, KeyValueData *pkvd )
|
|
{
|
|
int i;
|
|
TYPEDESCRIPTION *pField;
|
|
|
|
for ( i = 0; i < ENTVARS_COUNT; i++ )
|
|
{
|
|
pField = &gEntvarsDescription[i];
|
|
|
|
if ( !stricmp( pField->fieldName, pkvd->szKeyName ) )
|
|
{
|
|
switch( pField->fieldType )
|
|
{
|
|
case FIELD_MODELNAME:
|
|
case FIELD_SOUNDNAME:
|
|
case FIELD_STRING:
|
|
(*(int *)((char *)pev + pField->fieldOffset)) = ALLOC_STRING( pkvd->szValue );
|
|
break;
|
|
|
|
case FIELD_TIME:
|
|
case FIELD_FLOAT:
|
|
(*(float *)((char *)pev + pField->fieldOffset)) = atof( pkvd->szValue );
|
|
break;
|
|
case FIELD_DOUBLE:
|
|
(*(double *)((char *)pev + pField->fieldOffset)) = atof( pkvd->szValue );
|
|
break;
|
|
case FIELD_INTEGER:
|
|
(*(int *)((char *)pev + pField->fieldOffset)) = atoi( pkvd->szValue );
|
|
break;
|
|
case FIELD_INTEGER64:
|
|
(*(int64 *)((char *)pev + pField->fieldOffset)) = _atoi64( pkvd->szValue );
|
|
break;
|
|
case FIELD_POSITION_VECTOR:
|
|
case FIELD_VECTOR:
|
|
UTIL_StringToVector( (float *)((char *)pev + pField->fieldOffset), pkvd->szValue );
|
|
break;
|
|
|
|
default:
|
|
case FIELD_EVARS:
|
|
case FIELD_CLASSPTR:
|
|
case FIELD_EDICT:
|
|
case FIELD_ENTITY:
|
|
case FIELD_POINTER:
|
|
ALERT( at_error, "Bad field in entity!!\n" );
|
|
break;
|
|
}
|
|
pkvd->fHandled = TRUE;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int CSave :: WriteEntVars( const char *pname, entvars_t *pev )
|
|
{
|
|
if (pev->targetname)
|
|
return WriteFields( STRING(pev->targetname), pname, pev, gEntvarsDescription, ENTVARS_COUNT );
|
|
else
|
|
return WriteFields( STRING(pev->classname), pname, pev, gEntvarsDescription, ENTVARS_COUNT );
|
|
}
|
|
|
|
|
|
|
|
int CSave :: WriteFields( const char *cname, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount )
|
|
{
|
|
int i, j, actualCount, emptyCount;
|
|
TYPEDESCRIPTION *pTest;
|
|
int entityArray[MAX_ENTITYARRAY];
|
|
|
|
ALERT( at_console, "CSave::WriteFields( %s [%i fields])\n", pname, fieldCount );
|
|
|
|
if( !strcmp( pname, "Save Header" ) || !strcmp( pname, "ADJACENCY" ) || !strcmp( pname, "Game Header" ))
|
|
{
|
|
for( i = 0; i < fieldCount; i++ )
|
|
ALERT( at_console, "FIELD: %s [%s][0x%x]\n", pFields[i].fieldName, gNames[pFields[i].fieldType], pFields[i].flags );
|
|
}
|
|
|
|
// precalculate the number of empty fields
|
|
emptyCount = 0;
|
|
for ( i = 0; i < fieldCount; i++ )
|
|
{
|
|
void *pOutputData;
|
|
pOutputData = ((char *)pBaseData + pFields[i].fieldOffset );
|
|
if ( DataEmpty( (const char *)pOutputData, pFields[i].fieldSize * gSizes[pFields[i].fieldType] ) )
|
|
emptyCount++;
|
|
}
|
|
|
|
// Empty fields will not be written, write out the actual number of fields to be written
|
|
actualCount = fieldCount - emptyCount;
|
|
WriteInt( pname, &actualCount, 1 );
|
|
|
|
for ( i = 0; i < fieldCount; i++ )
|
|
{
|
|
void *pOutputData;
|
|
pTest = &pFields[ i ];
|
|
pOutputData = ((char *)pBaseData + pTest->fieldOffset );
|
|
|
|
// UNDONE: Must we do this twice?
|
|
if ( DataEmpty( (const char *)pOutputData, pTest->fieldSize * gSizes[pTest->fieldType] ) )
|
|
continue;
|
|
|
|
switch( pTest->fieldType )
|
|
{
|
|
case FIELD_FLOAT:
|
|
WriteFloat( pTest->fieldName, (float *)pOutputData, pTest->fieldSize );
|
|
break;
|
|
case FIELD_DOUBLE:
|
|
WriteDouble( pTest->fieldName, (double *)pOutputData, pTest->fieldSize );
|
|
break;
|
|
case FIELD_TIME:
|
|
WriteTime( pTest->fieldName, (float *)pOutputData, pTest->fieldSize );
|
|
break;
|
|
case FIELD_MODELNAME:
|
|
case FIELD_SOUNDNAME:
|
|
case FIELD_STRING:
|
|
WriteString( pTest->fieldName, (int *)pOutputData, pTest->fieldSize );
|
|
break;
|
|
case FIELD_CLASSPTR:
|
|
case FIELD_EVARS:
|
|
case FIELD_EDICT:
|
|
case FIELD_ENTITY:
|
|
case FIELD_EHANDLE:
|
|
if( pTest->fieldSize > MAX_ENTITYARRAY )
|
|
ALERT( at_error, "Can't save more than %d entities in an array!!!\n", MAX_ENTITYARRAY );
|
|
for( j = 0; j < pTest->fieldSize; j++ )
|
|
{
|
|
switch( pTest->fieldType )
|
|
{
|
|
case FIELD_EVARS:
|
|
entityArray[j] = EntityIndex( ((entvars_t **)pOutputData)[j] );
|
|
break;
|
|
case FIELD_CLASSPTR:
|
|
entityArray[j] = EntityIndex( ((CBaseEntity **)pOutputData)[j] );
|
|
break;
|
|
case FIELD_EDICT:
|
|
entityArray[j] = EntityIndex( ((edict_t **)pOutputData)[j] );
|
|
break;
|
|
case FIELD_ENTITY:
|
|
entityArray[j] = EntityIndex( ((EOFFSET *)pOutputData)[j] );
|
|
break;
|
|
case FIELD_EHANDLE:
|
|
entityArray[j] = EntityIndex( (CBaseEntity *)(((EHANDLE *)pOutputData)[j]) );
|
|
break;
|
|
}
|
|
}
|
|
WriteInt( pTest->fieldName, entityArray, pTest->fieldSize );
|
|
break;
|
|
case FIELD_POSITION_VECTOR:
|
|
WritePositionVector( pTest->fieldName, (float *)pOutputData, pTest->fieldSize );
|
|
break;
|
|
case FIELD_VECTOR:
|
|
WriteVector( pTest->fieldName, (float *)pOutputData, pTest->fieldSize );
|
|
break;
|
|
case FIELD_RANGE:
|
|
WriteRange( pTest->fieldName, (float *)pOutputData, pTest->fieldSize );
|
|
break;
|
|
case FIELD_BOOLEAN:
|
|
case FIELD_INTEGER:
|
|
WriteInt( pTest->fieldName, (int *)pOutputData, pTest->fieldSize );
|
|
break;
|
|
case FIELD_INTEGER64:
|
|
WriteInt64( pTest->fieldName, (int64 *)pOutputData, pTest->fieldSize );
|
|
break;
|
|
case FIELD_SHORT:
|
|
WriteData( pTest->fieldName, 2 * pTest->fieldSize, ((char *)pOutputData) );
|
|
break;
|
|
case FIELD_CHARACTER:
|
|
WriteData( pTest->fieldName, pTest->fieldSize, ((char *)pOutputData) );
|
|
break;
|
|
// For now, just write the address out, we're not going to change memory while doing this yet!
|
|
case FIELD_POINTER:
|
|
WriteInt( pTest->fieldName, (int *)(char *)pOutputData, pTest->fieldSize );
|
|
break;
|
|
case FIELD_FUNCTION:
|
|
WriteFunction( cname, pTest->fieldName, (int *)(char *)pOutputData, pTest->fieldSize );
|
|
break;
|
|
default:
|
|
ALERT( at_error, "Bad field type\n" );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
void CSave :: BufferString( char *pdata, int len )
|
|
{
|
|
char c = 0;
|
|
|
|
BufferData( pdata, len ); // Write the string
|
|
BufferData( &c, 1 ); // Write a null terminator
|
|
}
|
|
|
|
|
|
int CSave :: DataEmpty( const char *pdata, int size )
|
|
{
|
|
for ( int i = 0; i < size; i++ )
|
|
{
|
|
if ( pdata[i] )
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
void CSave :: BufferField( const char *pname, int size, const char *pdata )
|
|
{
|
|
BufferHeader( pname, size );
|
|
BufferData( pdata, size );
|
|
}
|
|
|
|
|
|
void CSave :: BufferHeader( const char *pname, int size )
|
|
{
|
|
short hashvalue = TokenHash( pname );
|
|
if ( size > 1<<(sizeof(short)*8) )
|
|
ALERT( at_error, "CSave :: BufferHeader() size parameter exceeds 'short'!" );
|
|
BufferData( (const char *)&size, sizeof(short) );
|
|
BufferData( (const char *)&hashvalue, sizeof(short) );
|
|
}
|
|
|
|
|
|
void CSave :: BufferData( const char *pdata, int size )
|
|
{
|
|
if ( !m_pdata )
|
|
return;
|
|
|
|
if ( m_pdata->size + size > m_pdata->bufferSize )
|
|
{
|
|
ALERT( at_error, "Save/Restore overflow!" );
|
|
m_pdata->size = m_pdata->bufferSize;
|
|
return;
|
|
}
|
|
|
|
memcpy( m_pdata->pCurrentData, pdata, size );
|
|
m_pdata->pCurrentData += size;
|
|
m_pdata->size += size;
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------
|
|
//
|
|
// CRestore
|
|
//
|
|
// --------------------------------------------------------------
|
|
|
|
int CRestore::ReadField( void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount, int startField, int size, char *pName, void *pData )
|
|
{
|
|
int i, j, stringCount, fieldNumber, entityIndex;
|
|
TYPEDESCRIPTION *pTest;
|
|
float time, timeData;
|
|
Vector position;
|
|
edict_t *pent;
|
|
char *pString;
|
|
|
|
time = 0;
|
|
position = Vector(0,0,0);
|
|
|
|
if( m_pdata )
|
|
{
|
|
time = m_pdata->time;
|
|
if ( m_pdata->fUseLandmark )
|
|
position = m_pdata->vecLandmarkOffset;
|
|
}
|
|
|
|
for( i = 0; i < fieldCount; i++ )
|
|
{
|
|
fieldNumber = (i + startField) % fieldCount;
|
|
pTest = &pFields[fieldNumber];
|
|
if( !stricmp( pTest->fieldName, pName ))
|
|
{
|
|
if ( !m_global || !(pTest->flags & FTYPEDESC_GLOBAL) )
|
|
{
|
|
for ( j = 0; j < pTest->fieldSize; j++ )
|
|
{
|
|
void *pOutputData = ((char *)pBaseData + pTest->fieldOffset + (j*gSizes[pTest->fieldType]) );
|
|
void *pInputData = (char *)pData + j * gSizes[pTest->fieldType];
|
|
|
|
switch( pTest->fieldType )
|
|
{
|
|
case FIELD_TIME:
|
|
timeData = *(float *)pInputData;
|
|
// re-base time variables
|
|
timeData += time;
|
|
*((float *)pOutputData) = timeData;
|
|
break;
|
|
case FIELD_FLOAT:
|
|
*((float *)pOutputData) = *(float *)pInputData;
|
|
break;
|
|
case FIELD_DOUBLE:
|
|
*((double *)pOutputData) = *(double *)pInputData;
|
|
break;
|
|
case FIELD_MODELNAME:
|
|
case FIELD_SOUNDNAME:
|
|
case FIELD_STRING:
|
|
// skip over j strings
|
|
pString = (char *)pData;
|
|
for ( stringCount = 0; stringCount < j; stringCount++ )
|
|
{
|
|
while( *pString )
|
|
pString++;
|
|
pString++;
|
|
}
|
|
pInputData = pString;
|
|
if( strlen((char *)pInputData ) == 0 )
|
|
*((int *)pOutputData) = 0;
|
|
else
|
|
{
|
|
int string;
|
|
|
|
string = ALLOC_STRING( (char *)pInputData );
|
|
|
|
*((int *)pOutputData) = string;
|
|
|
|
if ( !FStringNull( string ) && m_precache )
|
|
{
|
|
if ( pTest->fieldType == FIELD_MODELNAME )
|
|
UTIL_PrecacheModel( string );
|
|
else if ( pTest->fieldType == FIELD_SOUNDNAME )
|
|
UTIL_PrecacheSound( string );
|
|
}
|
|
}
|
|
break;
|
|
case FIELD_EVARS:
|
|
entityIndex = *( int *)pInputData;
|
|
pent = EntityFromIndex( entityIndex );
|
|
if ( pent ) *((entvars_t **)pOutputData) = VARS(pent);
|
|
else *((entvars_t **)pOutputData) = NULL;
|
|
break;
|
|
case FIELD_CLASSPTR:
|
|
entityIndex = *( int *)pInputData;
|
|
pent = EntityFromIndex( entityIndex );
|
|
if ( pent )
|
|
*((CBaseEntity **)pOutputData) = CBaseEntity::Instance(pent);
|
|
else
|
|
{
|
|
*((CBaseEntity **)pOutputData) = NULL;
|
|
if (entityIndex != -1) ALERT(at_console, "## Restore: invalid entitynum %d\n", entityIndex);
|
|
}
|
|
break;
|
|
case FIELD_EDICT:
|
|
entityIndex = *( int *)pInputData;
|
|
pent = EntityFromIndex( entityIndex );
|
|
*((edict_t **)pOutputData) = pent;
|
|
break;
|
|
case FIELD_EHANDLE:
|
|
// Input and Output sizes are different!
|
|
pOutputData = (char *)pOutputData + j*(sizeof(EHANDLE) - gSizes[pTest->fieldType]);
|
|
entityIndex = *( int *)pInputData;
|
|
pent = EntityFromIndex( entityIndex );
|
|
if ( pent )
|
|
*((EHANDLE *)pOutputData) = CBaseEntity::Instance(pent);
|
|
else
|
|
*((EHANDLE *)pOutputData) = NULL;
|
|
break;
|
|
case FIELD_ENTITY:
|
|
entityIndex = *( int *)pInputData;
|
|
pent = EntityFromIndex( entityIndex );
|
|
if ( pent )
|
|
*((EOFFSET *)pOutputData) = OFFSET(pent);
|
|
else
|
|
*((EOFFSET *)pOutputData) = 0;
|
|
break;
|
|
case FIELD_RANGE:
|
|
((float *)pOutputData)[0] = ((float *)pInputData)[0];
|
|
((float *)pOutputData)[1] = ((float *)pInputData)[1];
|
|
break;
|
|
case FIELD_VECTOR:
|
|
((float *)pOutputData)[0] = ((float *)pInputData)[0];
|
|
((float *)pOutputData)[1] = ((float *)pInputData)[1];
|
|
((float *)pOutputData)[2] = ((float *)pInputData)[2];
|
|
break;
|
|
case FIELD_POSITION_VECTOR:
|
|
((float *)pOutputData)[0] = ((float *)pInputData)[0] + position.x;
|
|
((float *)pOutputData)[1] = ((float *)pInputData)[1] + position.y;
|
|
((float *)pOutputData)[2] = ((float *)pInputData)[2] + position.z;
|
|
break;
|
|
case FIELD_BOOLEAN:
|
|
case FIELD_INTEGER:
|
|
*((int *)pOutputData) = *( int *)pInputData;
|
|
break;
|
|
case FIELD_INTEGER64:
|
|
*((int64 *)pOutputData) = *( int64 *)pInputData;
|
|
break;
|
|
case FIELD_SHORT:
|
|
*((short *)pOutputData) = *( short *)pInputData;
|
|
break;
|
|
case FIELD_CHARACTER:
|
|
*((char *)pOutputData) = *( char *)pInputData;
|
|
break;
|
|
case FIELD_POINTER:
|
|
*((int *)pOutputData) = *( int *)pInputData;
|
|
break;
|
|
case FIELD_FUNCTION:
|
|
if ( strlen( (char *)pInputData ) == 0 )
|
|
*((int *)pOutputData) = 0;
|
|
else
|
|
*((int *)pOutputData) = FUNCTION_FROM_NAME( (char *)pInputData );
|
|
break;
|
|
default:
|
|
ALERT( at_error, "Bad field type\n" );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#if 0
|
|
else
|
|
{
|
|
ALERT( at_debug, "Skipping global field %s\n", pName );
|
|
}
|
|
#endif
|
|
return fieldNumber;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
int CRestore::ReadEntVars( const char *pname, entvars_t *pev )
|
|
{
|
|
return ReadFields( pname, pev, gEntvarsDescription, ENTVARS_COUNT );
|
|
}
|
|
|
|
|
|
int CRestore::ReadFields( const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount )
|
|
{
|
|
unsigned short i, token;
|
|
int lastField, fileCount;
|
|
HEADER header;
|
|
|
|
i = ReadShort();
|
|
ASSERT( i == sizeof( int )); // First entry should be an int
|
|
|
|
token = ReadShort();
|
|
|
|
// Check the struct name
|
|
if( token != TokenHash( pname )) // Field Set marker
|
|
{
|
|
ALERT( at_error, "Expected %s found %s!\n", pname, BufferPointer() );
|
|
BufferRewind( 2 * sizeof( short ));
|
|
return 0;
|
|
}
|
|
|
|
ALERT( at_console, "CRestore:ReadFields: %s\n", pname );
|
|
|
|
// Skip over the struct name
|
|
fileCount = ReadInt(); // Read field count
|
|
|
|
lastField = 0; // Make searches faster, most data is read/written in the same order
|
|
|
|
// clear out base data
|
|
for( i = 0; i < fieldCount; i++ )
|
|
{
|
|
// Don't clear global fields
|
|
if ( !m_global || !(pFields[i].flags & FTYPEDESC_GLOBAL) )
|
|
memset( ((char *)pBaseData + pFields[i].fieldOffset), 0, pFields[i].fieldSize * gSizes[pFields[i].fieldType] );
|
|
}
|
|
for ( i = 0; i < fileCount; i++ )
|
|
{
|
|
BufferReadHeader( &header );
|
|
lastField = ReadField( pBaseData, pFields, fieldCount, lastField, header.size, m_pdata->pTokens[header.token], header.pData );
|
|
lastField++;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
void CRestore::BufferReadHeader( HEADER *pheader )
|
|
{
|
|
ASSERT( pheader!=NULL );
|
|
pheader->size = ReadShort(); // Read field size
|
|
pheader->token = ReadShort(); // Read field name token
|
|
pheader->pData = BufferPointer(); // Field Data is next
|
|
BufferSkipBytes( pheader->size ); // Advance to next field
|
|
}
|
|
|
|
|
|
short CRestore::ReadShort( void )
|
|
{
|
|
short tmp = 0;
|
|
|
|
BufferReadBytes( (char *)&tmp, sizeof(short) );
|
|
|
|
return tmp;
|
|
}
|
|
|
|
int CRestore::ReadInt( void )
|
|
{
|
|
int tmp = 0;
|
|
|
|
BufferReadBytes( (char *)&tmp, sizeof(int) );
|
|
|
|
return tmp;
|
|
}
|
|
|
|
int CRestore::ReadNamedInt( const char *pName )
|
|
{
|
|
HEADER header;
|
|
|
|
BufferReadHeader( &header );
|
|
return ((int *)header.pData)[0];
|
|
}
|
|
|
|
char *CRestore::ReadNamedString( const char *pName )
|
|
{
|
|
HEADER header;
|
|
|
|
BufferReadHeader( &header );
|
|
#ifdef TOKENIZE
|
|
return (char *)(m_pdata->pTokens[*(short *)header.pData]);
|
|
#else
|
|
return (char *)header.pData;
|
|
#endif
|
|
}
|
|
|
|
|
|
char *CRestore::BufferPointer( void )
|
|
{
|
|
if ( !m_pdata )
|
|
return NULL;
|
|
|
|
return m_pdata->pCurrentData;
|
|
}
|
|
|
|
void CRestore::BufferReadBytes( char *pOutput, int size )
|
|
{
|
|
ASSERT( m_pdata !=NULL );
|
|
|
|
if ( !m_pdata || Empty() )
|
|
return;
|
|
|
|
if ( (m_pdata->size + size) > m_pdata->bufferSize )
|
|
{
|
|
ALERT( at_error, "Restore overflow!" );
|
|
m_pdata->size = m_pdata->bufferSize;
|
|
return;
|
|
}
|
|
|
|
if ( pOutput )
|
|
memcpy( pOutput, m_pdata->pCurrentData, size );
|
|
m_pdata->pCurrentData += size;
|
|
m_pdata->size += size;
|
|
}
|
|
|
|
|
|
void CRestore::BufferSkipBytes( int bytes )
|
|
{
|
|
BufferReadBytes( NULL, bytes );
|
|
}
|
|
|
|
int CRestore::BufferSkipZString( void )
|
|
{
|
|
char *pszSearch;
|
|
int len;
|
|
|
|
if ( !m_pdata )
|
|
return 0;
|
|
|
|
int maxLen = m_pdata->bufferSize - m_pdata->size;
|
|
|
|
len = 0;
|
|
pszSearch = m_pdata->pCurrentData;
|
|
while ( *pszSearch++ && len < maxLen )
|
|
len++;
|
|
|
|
len++;
|
|
|
|
BufferSkipBytes( len );
|
|
|
|
return len;
|
|
}
|
|
|
|
int CRestore::BufferCheckZString( const char *string )
|
|
{
|
|
if ( !m_pdata )
|
|
return 0;
|
|
|
|
int maxLen = m_pdata->bufferSize - m_pdata->size;
|
|
int len = strlen( string );
|
|
if ( len <= maxLen )
|
|
{
|
|
if ( !strncmp( string, m_pdata->pCurrentData, len ) )
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
//=======================================================================
|
|
// global entity states
|
|
//=======================================================================
|
|
CGlobalState::CGlobalState( void )
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
void CGlobalState::Reset( void )
|
|
{
|
|
m_pList = NULL;
|
|
m_listCount = 0;
|
|
}
|
|
|
|
globalentity_t *CGlobalState :: Find( string_t globalname )
|
|
{
|
|
if ( !globalname )
|
|
return NULL;
|
|
|
|
globalentity_t *pTest;
|
|
const char *pEntityName = STRING(globalname);
|
|
|
|
|
|
pTest = m_pList;
|
|
while ( pTest )
|
|
{
|
|
if ( FStrEq( pEntityName, pTest->name ) )
|
|
break;
|
|
|
|
pTest = pTest->pNext;
|
|
}
|
|
|
|
return pTest;
|
|
}
|
|
|
|
void CGlobalState :: DumpGlobals( void )
|
|
{
|
|
static char *estates[] = { "Off", "On", "Dead" };
|
|
globalentity_t *pTest;
|
|
|
|
ALERT( at_debug, "-- Globals --\n" );
|
|
pTest = m_pList;
|
|
while ( pTest )
|
|
{
|
|
Msg( "%s: %s (%s)\n", pTest->name, pTest->levelName, estates[pTest->state] );
|
|
pTest = pTest->pNext;
|
|
}
|
|
}
|
|
|
|
void CGlobalState :: EntityAdd( string_t globalname, string_t mapName, GLOBALESTATE state )
|
|
{
|
|
ASSERT( !Find(globalname) );
|
|
|
|
globalentity_t *pNewEntity = (globalentity_t *)CALLOC( sizeof( globalentity_t ), 1 );
|
|
ASSERT( pNewEntity != NULL );
|
|
pNewEntity->pNext = m_pList;
|
|
m_pList = pNewEntity;
|
|
strcpy( pNewEntity->name, STRING( globalname ) );
|
|
strcpy( pNewEntity->levelName, STRING(mapName) );
|
|
pNewEntity->state = state;
|
|
m_listCount++;
|
|
}
|
|
|
|
|
|
void CGlobalState :: EntitySetState( string_t globalname, GLOBALESTATE state )
|
|
{
|
|
globalentity_t *pEnt = Find( globalname );
|
|
if ( pEnt ) pEnt->state = state;
|
|
}
|
|
|
|
|
|
const globalentity_t *CGlobalState :: EntityFromTable( string_t globalname )
|
|
{
|
|
globalentity_t *pEnt = Find( globalname );
|
|
return pEnt;
|
|
}
|
|
|
|
|
|
GLOBALESTATE CGlobalState :: EntityGetState( string_t globalname )
|
|
{
|
|
globalentity_t *pEnt = Find( globalname );
|
|
if ( pEnt ) return pEnt->state;
|
|
|
|
return GLOBAL_OFF;
|
|
}
|
|
|
|
TYPEDESCRIPTION CGlobalState::m_SaveData[] =
|
|
{
|
|
DEFINE_FIELD( CGlobalState, m_listCount, FIELD_INTEGER ),
|
|
};
|
|
|
|
TYPEDESCRIPTION gGlobalEntitySaveData[] =
|
|
{
|
|
DEFINE_ARRAY( globalentity_t, name, FIELD_CHARACTER, 64 ),
|
|
DEFINE_ARRAY( globalentity_t, levelName, FIELD_CHARACTER, 32 ),
|
|
DEFINE_FIELD( globalentity_t, state, FIELD_INTEGER ),
|
|
};
|
|
|
|
|
|
int CGlobalState::Save( CSave &save )
|
|
{
|
|
int i;
|
|
globalentity_t *pEntity;
|
|
|
|
if ( !save.WriteFields( "cGLOBAL", "GLOBAL", this, m_SaveData, ARRAYSIZE(m_SaveData) ) )
|
|
return 0;
|
|
|
|
pEntity = m_pList;
|
|
for ( i = 0; i < m_listCount && pEntity; i++ )
|
|
{
|
|
if ( !save.WriteFields( "cGENT", "GENT", pEntity, gGlobalEntitySaveData, ARRAYSIZE(gGlobalEntitySaveData) ) )
|
|
return 0;
|
|
|
|
pEntity = pEntity->pNext;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int CGlobalState::Restore( CRestore &restore )
|
|
{
|
|
int i, listCount;
|
|
globalentity_t tmpEntity;
|
|
|
|
ClearStates();
|
|
if ( !restore.ReadFields( "GLOBAL", this, m_SaveData, ARRAYSIZE(m_SaveData) ) ) return 0;
|
|
|
|
listCount = m_listCount; // Get new list count
|
|
m_listCount = 0; // Clear loaded data
|
|
|
|
for ( i = 0; i < listCount; i++ )
|
|
{
|
|
if ( !restore.ReadFields( "GENT", &tmpEntity, gGlobalEntitySaveData, ARRAYSIZE(gGlobalEntitySaveData) ) )
|
|
return 0;
|
|
EntityAdd( MAKE_STRING(tmpEntity.name), MAKE_STRING(tmpEntity.levelName), tmpEntity.state );
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void CGlobalState::EntityUpdate( string_t globalname, string_t mapname )
|
|
{
|
|
globalentity_t *pEnt = Find( globalname );
|
|
if ( pEnt ) strcpy( pEnt->levelName, STRING(mapname) );
|
|
}
|
|
|
|
|
|
void CGlobalState::ClearStates( void )
|
|
{
|
|
globalentity_t *pFree = m_pList;
|
|
while ( pFree )
|
|
{
|
|
globalentity_t *pNext = pFree->pNext;
|
|
FREE( pFree );
|
|
pFree = pNext;
|
|
}
|
|
Reset();
|
|
}
|
|
|
|
|
|
void SaveGlobalState( SAVERESTOREDATA *pSaveData )
|
|
{
|
|
CSave saveHelper( pSaveData );
|
|
gGlobalState.Save( saveHelper );
|
|
}
|
|
|
|
|
|
void RestoreGlobalState( SAVERESTOREDATA *pSaveData )
|
|
{
|
|
CRestore restoreHelper( pSaveData );
|
|
gGlobalState.Restore( restoreHelper );
|
|
}
|
|
|
|
|
|
void ResetGlobalState( void )
|
|
{
|
|
gGlobalState.ClearStates();
|
|
gInitHUD = TRUE; // Init the HUD on a new game / load game
|
|
} |