602 lines
12 KiB
C++
602 lines
12 KiB
C++
#include "b_local.h"
|
|
#include "boltOns.h"
|
|
//#include "b_public.h"
|
|
|
|
//extern void G_ParseBoltOnList( boltOn_t *boltOn );
|
|
extern gentity_t *G_CreateObject ( gentity_t *owner, vec3_t origin, vec3_t angles, int modelIndex, int frame, trType_t trType );
|
|
extern qboolean G_ParseLiteral( char **data, const char *string );
|
|
extern qboolean G_ParseString( char **data, char **s ) ;
|
|
extern qboolean G_ParseInt( char **data, int *i );
|
|
extern qboolean G_ParseFloat( char **data, float *f );
|
|
|
|
#define BOLTON_NONE MAX_BOLT_ONS
|
|
|
|
boltOn_t knownBoltOns[MAX_GAME_BOLTONS];
|
|
int numBoltOns;
|
|
char boltOnList[0x10000];
|
|
|
|
int G_GetBoltOnIndex( const char *boltOnName )
|
|
{
|
|
for ( int i = 0; i < numBoltOns; i++ )
|
|
{
|
|
if ( Q_stricmp( knownBoltOns[i].name, boltOnName ) == 0 )
|
|
{//found it!
|
|
return i;
|
|
}
|
|
}
|
|
|
|
G_Error( "ERROR: Unknown boltOn name: %s!\n", boltOnName );
|
|
|
|
return MAX_GAME_BOLTONS;
|
|
}
|
|
|
|
void G_RegisterBoltOns (void)
|
|
{
|
|
boltOn_t *boltOn = NULL;
|
|
char *token = NULL;
|
|
char *value;
|
|
char *p;
|
|
int n;
|
|
float f;
|
|
|
|
p = boltOnList;
|
|
COM_BeginParseSession();
|
|
|
|
while(1)
|
|
{
|
|
if ( numBoltOns >= MAX_GAME_BOLTONS )
|
|
{
|
|
G_Error( "ERROR: Too many boltOns (%d) in boltOn.cfg!\n", MAX_GAME_BOLTONS );
|
|
return;
|
|
}
|
|
|
|
boltOn = &knownBoltOns[numBoltOns];
|
|
|
|
while ( 1 )
|
|
{
|
|
token = COM_ParseExt( &p, qtrue );
|
|
if ( token[0] == 0 )
|
|
{
|
|
//reached EOF
|
|
return;
|
|
}
|
|
else if ( token[0] == '{' )
|
|
{
|
|
//WTF? Unnamed boltOn?
|
|
continue;
|
|
}
|
|
else if ( !p )
|
|
{
|
|
//???
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//found a boltOn name
|
|
break;
|
|
}
|
|
}
|
|
|
|
Q_strncpyz( (char *)&boltOn->name, token, sizeof(boltOn->name), qtrue );
|
|
|
|
if ( G_ParseLiteral( &p, "{" ) )
|
|
{//couldn't find an open brace "{"!
|
|
return;
|
|
}
|
|
|
|
//Set default scale
|
|
VectorSet(boltOn->model.scaleXYZ, 100, 100, 100);
|
|
|
|
// parse the boltOn info block
|
|
while ( 1 )
|
|
{
|
|
token = COM_ParseExt( &p, qtrue );
|
|
if ( !token[0] )
|
|
{
|
|
gi.Printf( S_COLOR_RED"ERROR: unexpected EOF while parsing '%s'\n", boltOn->name );
|
|
return;
|
|
}
|
|
|
|
if ( !Q_stricmp( token, "}" ) )
|
|
{
|
|
//reached end of boltOn, on to next
|
|
break;
|
|
}
|
|
|
|
// modelName
|
|
if ( !Q_stricmp( token, "modelName" ) )
|
|
{
|
|
if ( G_ParseString( &p, &value ) )
|
|
{
|
|
continue;
|
|
}
|
|
boltOn->model.modelIndex = G_ModelIndex( value );
|
|
continue;
|
|
}
|
|
|
|
// target model to attach to, head, torso, legs or weapon
|
|
if ( !Q_stricmp( token, "targetModel" ) )
|
|
{
|
|
if ( G_ParseInt( &p, &n ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( n < 0 || n >= NUM_TARGET_MODELS )
|
|
{
|
|
gi.Printf("WARNING boltOn %s has targetModel out of range (<0 or >= %d)\n", boltOn->name, NUM_TARGET_MODELS);
|
|
return;
|
|
}
|
|
|
|
boltOn->targetModel = (targetModel_t)n;
|
|
continue;
|
|
}
|
|
|
|
// target tag to attach to
|
|
if ( !Q_stricmp( token, "targetTag" ) )
|
|
{
|
|
if ( G_ParseString( &p, &value ) )
|
|
{
|
|
continue;
|
|
}
|
|
//boltOn->targetTag = G_NewString(value);
|
|
Q_strncpyz( boltOn->targetTag, value, sizeof(boltOn->targetTag), qtrue);
|
|
continue;
|
|
}
|
|
|
|
// pitch offset
|
|
if ( !Q_stricmp( token, "pitchOffset" ) )
|
|
{
|
|
if ( G_ParseFloat( &p, &f ) )
|
|
{
|
|
continue;
|
|
}
|
|
boltOn->angleOffsets[0] = f;
|
|
continue;
|
|
}
|
|
|
|
// yaw offset
|
|
if ( !Q_stricmp( token, "yawOffset" ) )
|
|
{
|
|
if ( G_ParseFloat( &p, &f ) )
|
|
{
|
|
continue;
|
|
}
|
|
boltOn->angleOffsets[1] = f;
|
|
continue;
|
|
}
|
|
|
|
// roll offset
|
|
if ( !Q_stricmp( token, "rollOffset" ) )
|
|
{
|
|
if ( G_ParseFloat( &p, &f ) )
|
|
{
|
|
continue;
|
|
}
|
|
boltOn->angleOffsets[2] = f;
|
|
continue;
|
|
}
|
|
|
|
// x offset
|
|
if ( !Q_stricmp( token, "xOffset" ) )
|
|
{
|
|
if ( G_ParseFloat( &p, &f ) )
|
|
{
|
|
continue;
|
|
}
|
|
boltOn->originOffsets[0] = f;
|
|
continue;
|
|
}
|
|
|
|
// y offset
|
|
if ( !Q_stricmp( token, "yOffset" ) )
|
|
{
|
|
if ( G_ParseFloat( &p, &f ) )
|
|
{
|
|
continue;
|
|
}
|
|
boltOn->originOffsets[1] = f;
|
|
continue;
|
|
}
|
|
|
|
// z offset
|
|
if ( !Q_stricmp( token, "zOffset" ) )
|
|
{
|
|
if ( G_ParseFloat( &p, &f ) )
|
|
{
|
|
continue;
|
|
}
|
|
boltOn->originOffsets[2] = f;
|
|
continue;
|
|
}
|
|
|
|
// Uniform XYZ scale
|
|
if ( !Q_stricmp( token, "scale" ) )
|
|
{
|
|
if ( G_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
if ( n < 0 )
|
|
{
|
|
gi.Printf( "bad %s in boltOn '%s'\n", token, boltOn->name );
|
|
continue;
|
|
}
|
|
boltOn->model.scaleXYZ[0] = boltOn->model.scaleXYZ[1] = boltOn->model.scaleXYZ[2] = n;
|
|
continue;
|
|
}
|
|
|
|
//X scale
|
|
if ( !Q_stricmp( token, "scaleX" ) )
|
|
{
|
|
if ( G_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
if ( n < 0 )
|
|
{
|
|
gi.Printf( "bad %s in boltOn '%s'\n", token, boltOn->name );
|
|
continue;
|
|
}
|
|
boltOn->model.scaleXYZ[0] = n;
|
|
continue;
|
|
}
|
|
|
|
//Y scale
|
|
if ( !Q_stricmp( token, "scaleY" ) )
|
|
{
|
|
if ( G_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
if ( n < 0 )
|
|
{
|
|
gi.Printf( "bad %s in boltOn '%s'\n", token, boltOn->name );
|
|
continue;
|
|
}
|
|
boltOn->model.scaleXYZ[1] = n;
|
|
continue;
|
|
}
|
|
|
|
//Z scale
|
|
if ( !Q_stricmp( token, "scaleZ" ) )
|
|
{
|
|
if ( G_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
if ( n < 0 )
|
|
{
|
|
gi.Printf( "bad %s in boltOn '%s'\n", token, boltOn->name );
|
|
continue;
|
|
}
|
|
boltOn->model.scaleXYZ[2] = n;
|
|
continue;
|
|
}
|
|
|
|
// Uniform RGB tint
|
|
if ( !Q_stricmp( token, "tint" ) )
|
|
{
|
|
if ( G_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
if ( n < 0 )
|
|
{
|
|
gi.Printf( "bad %s in boltOn '%s'\n", token, boltOn->name );
|
|
continue;
|
|
}
|
|
boltOn->model.customRGB[0] = boltOn->model.customRGB[1] = boltOn->model.customRGB[2] = n;
|
|
continue;
|
|
}
|
|
|
|
//red tint
|
|
if ( !Q_stricmp( token, "tintRed" ) )
|
|
{
|
|
if ( G_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
if ( n < 0 )
|
|
{
|
|
gi.Printf( "bad %s in boltOn '%s'\n", token, boltOn->name );
|
|
continue;
|
|
}
|
|
boltOn->model.customRGB[0] = n;
|
|
continue;
|
|
}
|
|
|
|
//Green tint
|
|
if ( !Q_stricmp( token, "tintGreen" ) )
|
|
{
|
|
if ( G_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
if ( n < 0 )
|
|
{
|
|
gi.Printf( "bad %s in boltOn '%s'\n", token, boltOn->name );
|
|
continue;
|
|
}
|
|
boltOn->model.customRGB[1] = n;
|
|
continue;
|
|
}
|
|
|
|
//Blue tint
|
|
if ( !Q_stricmp( token, "tintBlue" ) )
|
|
{
|
|
if ( G_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
if ( n < 0 )
|
|
{
|
|
gi.Printf( "bad %s in boltOn '%s'\n", token, boltOn->name );
|
|
continue;
|
|
}
|
|
boltOn->model.customRGB[2] = n;
|
|
continue;
|
|
}
|
|
|
|
//alpha
|
|
if ( !Q_stricmp( token, "alpha" ) )
|
|
{
|
|
if ( G_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
if ( n < 0 )
|
|
{
|
|
gi.Printf( "bad %s in boltOn '%s'\n", token, boltOn->name );
|
|
continue;
|
|
}
|
|
boltOn->model.customAlpha = n;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
numBoltOns++;
|
|
}
|
|
}
|
|
|
|
void G_ClearBoltOnInfo( boltOnInfo_t *bOInfo )
|
|
{
|
|
bOInfo->index = MAX_GAME_BOLTONS;
|
|
VectorClear( bOInfo->lastOrigin );
|
|
VectorClear( bOInfo->lastAngles );
|
|
bOInfo->frame = bOInfo->startFrame = bOInfo->endFrame = 0;
|
|
bOInfo->loopAnim = qfalse;
|
|
}
|
|
|
|
void G_LoadBoltOns( void )
|
|
{
|
|
int len;
|
|
const char filename[] = "ext_data/boltOns.cfg";
|
|
char *buffer;
|
|
|
|
gi.Printf( "Parsing %s\n", filename );
|
|
len = gi.FS_ReadFile( filename, (void **) &buffer );
|
|
if ( len == -1 )
|
|
{
|
|
gi.Printf( "file not found\n" );
|
|
return;
|
|
}
|
|
|
|
if ( len >= sizeof( boltOnList ) )
|
|
{
|
|
G_Error( "ext_data/boltOns.cfg is too large" );
|
|
}
|
|
strncpy( boltOnList, buffer, sizeof( boltOnList ) - 1 );
|
|
gi.FS_FreeFile( buffer );
|
|
|
|
G_RegisterBoltOns();
|
|
}
|
|
|
|
byte G_AddBoltOn( gentity_t *ent, const char *boltOnName )
|
|
{
|
|
int freeSlot, newIndex;
|
|
|
|
if ( !ent || !boltOnName || !boltOnName[0] )
|
|
{
|
|
return BOLTON_NONE;
|
|
}
|
|
|
|
newIndex = G_GetBoltOnIndex( boltOnName );
|
|
if ( newIndex < 0 || newIndex >= numBoltOns )
|
|
{
|
|
return BOLTON_NONE;
|
|
}
|
|
|
|
if ( !ent->client )
|
|
{
|
|
freeSlot = 0;
|
|
|
|
G_ClearBoltOnInfo( &ent->boltOn );
|
|
ent->boltOn.index = newIndex;
|
|
}
|
|
else
|
|
{
|
|
for ( freeSlot = 0; freeSlot < MAX_BOLT_ONS; freeSlot++ )
|
|
{
|
|
if ( ent->client->renderInfo.boltOns[freeSlot].index == MAX_GAME_BOLTONS )
|
|
{//Found a free slot
|
|
break;
|
|
}
|
|
else if ( ent->client->renderInfo.boltOns[freeSlot].index == newIndex )
|
|
{
|
|
#ifndef FINAL_BUILD
|
|
gi.Printf("WARNING: %s already has boltOn turned on!\n", ent->targetname, boltOnName );
|
|
#endif
|
|
return BOLTON_NONE;
|
|
}
|
|
}
|
|
|
|
if ( freeSlot >= MAX_BOLT_ONS )
|
|
{
|
|
gi.Printf("WARNING: %s out of free boltOn slots! (MAX = %d)\n", ent->targetname, MAX_BOLT_ONS );
|
|
return BOLTON_NONE;
|
|
}
|
|
|
|
G_ClearBoltOnInfo( &ent->client->renderInfo.boltOns[freeSlot] );
|
|
ent->client->renderInfo.boltOns[freeSlot].index = newIndex;
|
|
}
|
|
|
|
return freeSlot;
|
|
}
|
|
|
|
void G_RemoveBoltOn( gentity_t *ent, const char *boltOnName )
|
|
{
|
|
int namedIndex;
|
|
|
|
if ( !ent || !boltOnName || !boltOnName[0] )
|
|
{
|
|
return;
|
|
}
|
|
|
|
namedIndex = G_GetBoltOnIndex( boltOnName );
|
|
if ( namedIndex < 0 || namedIndex >= numBoltOns )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( !ent->client )
|
|
{
|
|
if ( ent->boltOn.index == namedIndex )
|
|
{
|
|
G_ClearBoltOnInfo( &ent->boltOn );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( int i = 0; i < MAX_BOLT_ONS; i++ )
|
|
{
|
|
if ( ent->client->renderInfo.boltOns[i].index == namedIndex )
|
|
{//Found it, clear it
|
|
G_ClearBoltOnInfo( &ent->client->renderInfo.boltOns[i] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void G_DropBoltOn( gentity_t *ent, const char *boltOnName )
|
|
{
|
|
boltOn_t *boltOn;
|
|
int namedIndex;
|
|
|
|
if ( !ent || !boltOnName || !boltOnName[0] )
|
|
{
|
|
return;
|
|
}
|
|
|
|
namedIndex = G_GetBoltOnIndex( boltOnName );
|
|
if ( namedIndex < 0 || namedIndex >= numBoltOns )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( !ent->client )
|
|
{
|
|
if ( ent->boltOn.index != namedIndex )
|
|
{
|
|
//FIXME: error msg
|
|
return;
|
|
}
|
|
|
|
if ( ent->boltOn.index < 0 || ent->boltOn.index >= numBoltOns )
|
|
{
|
|
return;
|
|
}
|
|
|
|
boltOn = &knownBoltOns[ent->boltOn.index];
|
|
//FIXME: what about tint, alpha and scale? And animation?
|
|
gentity_t *newObject = G_CreateObject( ent, ent->boltOn.lastOrigin, ent->boltOn.lastAngles,
|
|
boltOn->model.modelIndex, ent->boltOn.frame, TR_GRAVITY );
|
|
if ( newObject )
|
|
{
|
|
newObject->targetname = G_NewString( va( "%s_%s", ent->targetname, boltOnName ) );
|
|
}
|
|
|
|
//Remove it
|
|
G_RemoveBoltOn( ent, boltOnName );
|
|
}
|
|
else
|
|
{
|
|
for ( int i = 0; i < MAX_BOLT_ONS; i++ )
|
|
{
|
|
if ( ent->client->renderInfo.boltOns[i].index < 0 || ent->client->renderInfo.boltOns[i].index >= numBoltOns )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( ent->client->renderInfo.boltOns[i].index == namedIndex )
|
|
{//Found it
|
|
boltOn = &knownBoltOns[ent->client->renderInfo.boltOns[i].index];
|
|
//GUH!!! Move these onto ENT!!!
|
|
gentity_t *newObject = G_CreateObject( ent, ent->client->renderInfo.boltOns[i].lastOrigin,
|
|
ent->client->renderInfo.boltOns[i].lastAngles,
|
|
boltOn->model.modelIndex,
|
|
ent->client->renderInfo.boltOns[i].frame,
|
|
TR_GRAVITY );//TR_STATIONARY );//
|
|
if ( newObject )
|
|
{
|
|
newObject->targetname = G_NewString( va( "%s_%s", ent->targetname, boltOnName ) );
|
|
}
|
|
//FIXME: what about tint, alpha and scale? And animation?
|
|
|
|
//Remove it
|
|
G_RemoveBoltOn( ent, boltOnName );
|
|
//Done
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
byte G_BoltOnNumberForName( gentity_t *ent, const char *boltOnName )
|
|
{
|
|
if ( !ent || !boltOnName || !boltOnName[0] )
|
|
{
|
|
return MAX_BOLT_ONS;
|
|
}
|
|
|
|
if ( !ent->client )
|
|
{//non-clients only have one
|
|
return 0;
|
|
}
|
|
|
|
for ( int i = 0; i < MAX_BOLT_ONS; i++ )
|
|
{
|
|
if ( !Q_stricmp( boltOnName, knownBoltOns[ent->client->renderInfo.boltOns[i].index].name ) )
|
|
{//Found it, return it
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return MAX_BOLT_ONS;
|
|
}
|
|
|
|
void G_InitBoltOnData ( gentity_t *ent )
|
|
{
|
|
//UG, crappy here, but need to initialize boltOn index to invalid number
|
|
ent->activeBoltOn = MAX_BOLT_ONS;
|
|
|
|
G_ClearBoltOnInfo( &ent->boltOn );
|
|
if ( ent->client )
|
|
{
|
|
for ( int i = 0; i < MAX_BOLT_ONS; i++ )
|
|
{
|
|
G_ClearBoltOnInfo( &ent->client->renderInfo.boltOns[i] );
|
|
}
|
|
}
|
|
}
|
|
|