jkxr/Projects/Android/jni/OpenJK/codemp/cgame/cg_spawn.c

464 lines
11 KiB
C

/*
===========================================================================
Copyright (C) 1999 - 2005, Id Software, Inc.
Copyright (C) 2000 - 2013, Raven Software, Inc.
Copyright (C) 2001 - 2013, Activision, Inc.
Copyright (C) 2013 - 2015, OpenJK contributors
This file is part of the OpenJK source code.
OpenJK is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
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/>.
===========================================================================
*/
/*
** cg_spawn.c
**
** Client-side functions for parsing entity data.
*/
#include "cg_local.h"
/*
=============
CG_NewString
Builds a copy of the string, translating \n to real linefeeds
so message texts can be multi-line
=============
*/
#if 0
// Enable if you put in cgame alloc and need it for some reason...
// (ie: if you need to re-implement cross game field_t)
char *CG_NewString( const char *string ) {
char *newb, *new_p;
int i, l;
l = strlen( string ) + 1;
newb = CG_Alloc( l );
new_p = newb;
// turn \n into a real linefeed
for( i = 0; i < l; i++ ) {
if( string[i] == '\\' && i < l - 1 ) {
if( string[i + 1] == 'n' ) {
*new_p++ = '\n';
i++;
}
else {
*new_p++ = '\\';
}
}
else {
*new_p++ = string[i];
}
/*old code
if (string[i] == '\\' && i < l-1) {
i++;
if (string[i] == 'n') {
*new_p++ = '\n';
} else {
*new_p++ = '\\';
}
} else {
*new_p++ = string[i];
}
*/
}
return newb;
}
#endif
qboolean CG_SpawnString( const char *key, const char *defaultString, char **out ) {
int i;
if( !cg.spawning ) {
*out = (char*)defaultString;
// trap->Error( ERR_DROP, "CG_SpawnString() called while not spawning" );
}
for( i = 0; i < cg.numSpawnVars; i++ ) {
if( !Q_stricmp( key, cg.spawnVars[i][0] ) ) {
*out = cg.spawnVars[i][1];
return qtrue;
}
}
*out = (char*)defaultString;
return qfalse;
}
qboolean CG_SpawnFloat( const char *key, const char *defaultString, float *out ) {
char *s;
qboolean present;
present = CG_SpawnString( key, defaultString, &s );
*out = atof( s );
return present;
}
qboolean CG_SpawnInt( const char *key, const char *defaultString, int *out ) {
char *s;
qboolean present;
present = CG_SpawnString( key, defaultString, &s );
*out = atoi( s );
return present;
}
qboolean CG_SpawnBoolean( const char *key, const char *defaultString, qboolean *out ) {
char *s;
qboolean present;
present = CG_SpawnString( key, defaultString, &s );
if( !Q_stricmp( s, "qfalse" ) || !Q_stricmp( s, "false" ) || !Q_stricmp( s, "no" ) || !Q_stricmp( s, "0" ) ) {
*out = qfalse;
}
else if( !Q_stricmp( s, "qtrue" ) || !Q_stricmp( s, "true" ) || !Q_stricmp( s, "yes" ) || !Q_stricmp( s, "1" ) ) {
*out = qtrue;
}
else {
*out = qfalse;
}
return present;
}
qboolean CG_SpawnVector( const char *key, const char *defaultString, float *out ) {
char *s;
qboolean present;
present = CG_SpawnString( key, defaultString, &s );
if ( sscanf( s, "%f %f %f", &out[0], &out[1], &out[2] ) != 3 ) {
trap->Print( "CG_SpawnVector: Failed sscanf on %s (default: %s)\n", key, defaultString );
VectorClear( out );
return qfalse;
}
return present;
}
/*
=============
VectorToString
This is just a convenience function
for printing vectors
=============
*/
char *vtos( const vec3_t v ) {
static int index;
static char str[8][32];
char *s;
// use an array so that multiple vtos won't collide
s = str[index];
index = ( index + 1 ) & 7;
Com_sprintf( s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2] );
return s;
}
void SP_misc_model_static( void ) {
char* model;
float angle;
vec3_t angles;
float scale;
vec3_t vScale;
vec3_t org;
float zoffset;
int i;
int modelIndex;
cg_staticmodel_t *staticmodel;
if( cgs.numMiscStaticModels >= MAX_STATIC_MODELS ) {
trap->Error( ERR_DROP, "MAX_STATIC_MODELS(%i) hit", MAX_STATIC_MODELS );
}
CG_SpawnString( "model", "", &model );
if( !model || !model[0] ) {
trap->Error( ERR_DROP, "misc_model_static with no model." );
}
CG_SpawnVector( "origin", "0 0 0", org );
CG_SpawnFloat( "zoffset", "0", &zoffset );
if( !CG_SpawnVector( "angles", "0 0 0", angles ) ) {
if( CG_SpawnFloat( "angle", "0", &angle ) ) {
angles[YAW] = angle;
}
}
if( !CG_SpawnVector( "modelscale_vec", "1 1 1", vScale ) ) {
if( CG_SpawnFloat( "modelscale", "1", &scale ) ) {
VectorSet( vScale, scale, scale, scale );
}
}
modelIndex = trap->R_RegisterModel( model );
if( modelIndex == 0 ) {
trap->Error( ERR_DROP, "misc_model_static failed to load model '%s'", model );
return;
}
staticmodel = &cgs.miscStaticModels[cgs.numMiscStaticModels++];
staticmodel->model = modelIndex;
AnglesToAxis( angles, staticmodel->axes );
for( i = 0; i < 3; i++ ) {
VectorScale( staticmodel->axes[i], vScale[i], staticmodel->axes[i] );
}
VectorCopy( org, staticmodel->org );
staticmodel->zoffset = zoffset;
if( staticmodel->model ) {
vec3_t mins, maxs;
trap->R_ModelBounds( staticmodel->model, mins, maxs );
VectorScaleVector( mins, vScale, mins );
VectorScaleVector( maxs, vScale, maxs );
staticmodel->radius = RadiusFromBounds( mins, maxs );
}
else {
staticmodel->radius = 0;
}
}
qboolean cg_noFogOutsidePortal = qfalse;
void SP_misc_skyportal( void ) {
qboolean onlyfoghere;
CG_SpawnBoolean( "onlyfoghere", "0", &onlyfoghere );
if( onlyfoghere )
cg_noFogOutsidePortal = qtrue;
}
qboolean cg_skyOri = qfalse;
vec3_t cg_skyOriPos;
float cg_skyOriScale = 0.0f;
void SP_misc_skyportal_orient( void ) {
if( cg_skyOri )
trap->Print( S_COLOR_YELLOW "WARNING: multiple misc_skyportal_orients found.\n" );
cg_skyOri = qtrue;
CG_SpawnVector( "origin", "0 0 0", cg_skyOriPos );
CG_SpawnFloat( "modelscale", "0", &cg_skyOriScale );
}
void SP_misc_weather_zone( void ) {
char *model;
vec3_t mins, maxs;
CG_SpawnString( "model", "", &model );
if( !model || !model[0] ) {
trap->Error( ERR_DROP, "misc_weather_zone with invalid brush model data." );
return;
}
trap->R_ModelBounds( trap->R_RegisterModel( model ), mins, maxs );
trap->WE_AddWeatherZone( mins, maxs );
}
typedef struct spawn_s {
const char *name;
void (*spawn)( void );
} spawn_t;
spawn_t spawns [] = {
{ "misc_model_static", SP_misc_model_static },
{ "misc_skyportal", SP_misc_skyportal },
{ "misc_skyportal_orient", SP_misc_skyportal_orient },
{ "misc_weather_zone", SP_misc_weather_zone },
};
/*
===================
CG_ParseEntityFromSpawnVars
Spawn an entity and fill in all of the level fields from
cg.spawnVars[], then call the class specfic spawn function
===================
*/
static int spawncmp( const void *a, const void *b ) {
return Q_stricmp( (const char *)a, ((spawn_t*)b)->name );
}
void CG_ParseEntityFromSpawnVars( void ) {
spawn_t *s;
int i;
char *classname;
char *p, *value, *gametypeName;
static char *gametypeNames[GT_MAX_GAME_TYPE] = { "ffa", "holocron", "jedimaster", "duel", "powerduel", "single", "team", "siege", "ctf", "cty" };
// check for "notsingle" flag
if( cgs.gametype == GT_SINGLE_PLAYER ) {
CG_SpawnInt( "notsingle", "0", &i );
if( i ) {
return;
}
}
// check for "notteam" flag (GT_FFA, GT_DUEL, GT_SINGLE_PLAYER)
if( cgs.gametype >= GT_TEAM ) {
CG_SpawnInt( "notteam", "0", &i );
if( i ) {
return;
}
}
else {
CG_SpawnInt( "notfree", "0", &i );
if( i ) {
return;
}
}
if( CG_SpawnString( "gametype", NULL, &value ) ) {
if( cgs.gametype >= GT_FFA && cgs.gametype < GT_MAX_GAME_TYPE ) {
gametypeName = gametypeNames[cgs.gametype];
p = strstr( value, gametypeName );
if( !p ) {
return;
}
}
}
if( CG_SpawnString( "classname", "", &classname ) ) {
s = (spawn_t *)Q_LinearSearch( classname, spawns, ARRAY_LEN( spawns ), sizeof( spawn_t ), spawncmp );
if ( s )
s->spawn();
}
}
/*
====================
CG_AddSpawnVarToken
====================
*/
char *CG_AddSpawnVarToken( const char *string ) {
int l;
char *dest;
l = strlen( string );
if( cg.numSpawnVarChars + l + 1 > MAX_SPAWN_VARS_CHARS ) {
trap->Error( ERR_DROP, "CG_AddSpawnVarToken: MAX_SPAWN_VARS_CHARS" );
}
dest = cg.spawnVarChars + cg.numSpawnVarChars;
memcpy( dest, string, l + 1 );
cg.numSpawnVarChars += l + 1;
return dest;
}
/*
====================
CG_ParseSpawnVars
Parses a brace bounded set of key / value pairs out of the
level's entity strings into cg.spawnVars[]
This does not actually spawn an entity.
====================
*/
qboolean CG_ParseSpawnVars( void ) {
char keyname[MAX_TOKEN_CHARS];
char com_token[MAX_TOKEN_CHARS];
cg.numSpawnVars = 0;
cg.numSpawnVarChars = 0;
// parse the opening brace
if( !trap->R_GetEntityToken( com_token, sizeof( com_token ) ) ) {
// end of spawn string
return qfalse;
}
if( com_token[0] != '{' ) {
trap->Error( ERR_DROP, "CG_ParseSpawnVars: found %s when expecting {", com_token );
}
// go through all the key / value pairs
while( 1 ) {
// parse key
if( !trap->R_GetEntityToken( keyname, sizeof( keyname ) ) ) {
trap->Error( ERR_DROP, "CG_ParseSpawnVars: EOF without closing brace" );
}
if( keyname[0] == '}' ) {
break;
}
// parse value
if( !trap->R_GetEntityToken( com_token, sizeof( com_token ) ) ) {
trap->Error( ERR_DROP, "CG_ParseSpawnVars: EOF without closing brace" );
}
if( com_token[0] == '}' ) {
trap->Error( ERR_DROP, "CG_ParseSpawnVars: closing brace without data" );
}
if( cg.numSpawnVars == MAX_SPAWN_VARS ) {
trap->Error( ERR_DROP, "CG_ParseSpawnVars: MAX_SPAWN_VARS" );
}
cg.spawnVars[cg.numSpawnVars][0] = CG_AddSpawnVarToken( keyname );
cg.spawnVars[cg.numSpawnVars][1] = CG_AddSpawnVarToken( com_token );
cg.numSpawnVars++;
}
return qtrue;
}
extern float cg_linearFogOverride; // cg_view.c
extern float cg_radarRange; // cg_draw.c
void SP_worldspawn( void ) {
char *s;
CG_SpawnString( "classname", "", &s );
if( Q_stricmp( s, "worldspawn" ) ) {
trap->Error( ERR_DROP, "SP_worldspawn: The first entity isn't 'worldspawn'" );
}
CG_SpawnFloat( "fogstart", "0", &cg_linearFogOverride );
CG_SpawnFloat( "radarrange", "2500", &cg_radarRange );
}
/*
==============
CG_ParseEntitiesFromString
Parses textual entity definitions out of an entstring
==============
*/
void CG_ParseEntitiesFromString( void ) {
// make sure it is reset
trap->R_GetEntityToken( NULL, -1 );
// allow calls to CG_Spawn*()
cg.spawning = qtrue;
cg.numSpawnVars = 0;
// the worldspawn is not an actual entity, but it still
// has a "spawn" function to perform any global setup
// needed by a level (setting configstrings or cvars, etc)
if( !CG_ParseSpawnVars() ) {
trap->Error( ERR_DROP, "ParseEntities: no entities" );
}
SP_worldspawn();
// parse ents
while( CG_ParseSpawnVars() ) {
CG_ParseEntityFromSpawnVars();
}
cg.spawning = qfalse; // any future calls to CG_Spawn*() will be errors
}