// Give a gentity_t, create a sp entity, copy all pertinent data, and return it g_serverEntity_t *CreateServerEntity(gentity_t *ent) { // get the entity out of our pool g_serverEntity_t *newEnt = GetFreeServerEntity(); // if we managed to get one, copy over data if (newEnt) { // G_NewString crashes if you pass in NULL, so let's check... if (ent->classname) newEnt->classname = G_NewString(ent->classname); if (ent->targetname) newEnt->name = G_NewString(ent->targetname); if (ent->target) newEnt->target = G_NewString(ent->target); newEnt->spawnflags = ent->spawnflags; newEnt->team = ent->aiTeam; VectorCopy(ent->s.origin, newEnt->origin); VectorCopy(ent->s.angles, newEnt->angles); // DON'T set the number - that should have been set when it was spawned // set the areanum to -1, which means we haven't calculated it yet // these don't move, so we should only have to calc it once, the first // time someone asks for it newEnt->areaNum = -1; } return newEnt; }
// Create a server entity from some basic data void CreateServerEntityFromData(char* classname, char* targetname, char* target, vec3_t origin, int spawnflags, vec3_t angle) { // get the entity out of our pool g_serverEntity_t *newEnt = GetFreeServerEntity(); // if we managed to get one, copy over data if (newEnt) { // G_NewString crashes if you pass in NULL, so let's check... if (classname) newEnt->classname = G_NewString(classname); if (targetname) newEnt->name = G_NewString(targetname); if (target) newEnt->target = G_NewString(target); newEnt->spawnflags = spawnflags; //newEnt->team = ent->aiTeam; VectorCopy(origin, newEnt->origin); VectorCopy(angle, newEnt->angles); // DON'T set the number - that should have been set when it was spawned // set the areanum to -1, which means we haven't calculated it yet // these don't move, so we should only have to calc it once, the first // time someone asks for it newEnt->areaNum = -1; // and do class specific stuff InitServerEntitySetupFunc(newEnt); } }
/* =============== G_ParseField Takes a key/value pair and sets the binary values in a gentity =============== */ void G_ParseField( const char *key, const char *value, gentity_t *ent ) { field_t *f; byte *b; float v; vec3_t vec; char string[64]; for ( f=fields ; f->name ; f++ ) { if ( !Q_stricmp(f->name, key) ) { // found it b = (byte *)ent; switch( f->type ) { case F_LSTRING: *(char **)(b+f->ofs) = G_NewString (value); strcpy(string, G_NewString(value)); if(!Q_stricmp(key, "name")){ strcpy(intermission_names[ent->mappart-1], string); } break; case F_VECTOR: sscanf (value, "%f %f %f", &vec[0], &vec[1], &vec[2]); ((float *)(b+f->ofs))[0] = vec[0]; ((float *)(b+f->ofs))[1] = vec[1]; ((float *)(b+f->ofs))[2] = vec[2]; break; case F_INT: *(int *)(b+f->ofs) = atoi(value); break; case F_FLOAT: *(float *)(b+f->ofs) = atof(value); break; case F_ANGLEHACK: v = atof(value); ((float *)(b+f->ofs))[0] = 0; ((float *)(b+f->ofs))[1] = v; ((float *)(b+f->ofs))[2] = 0; break; default: case F_IGNORE: break; } return; } } }
void SP_target_stoptimer(gentity_t *ent) { char *t = NULL; // Nico, used to look for parent gentity_t *parent = NULL; // Nico, override wait -1 or wait 9999 on stop timer entities if (g_forceTimerReset.integer) { parent = G_FindByTarget(NULL, ent->targetname); if (parent && parent->wait != 0.5 && !Q_stricmp(parent->classname, "trigger_multiple")) { G_DPrintf("%s: SP_target_stoptimer, wait found = %f, overrided to 0.5\n", GAME_VERSION, parent->wait); G_SpawnFloat("wait", "0.5", &parent->wait); } } G_SpawnString("name", "default", &t); ent->timerunName = G_NewString(t); // create a timerun with this name if it doesn't exit yet GetTimerunNum(ent->timerunName); G_SpawnInt("mincheckpoints", "0", &ent->count); ent->use = target_stoptimer_use; level.isTimerun = qtrue; }
static char *GetStringPtr(int iStrlen, char *psOriginal/*may be NULL*/) { if (iStrlen != -1) { char sString[768]; // arb, inc if nec. sString[0]=0; assert(iStrlen+1<=sizeof(sString)); gi.ReadFromSaveGame('STRG', sString, iStrlen); #ifndef _XBOX // TAG_G_ALLOC is always blown away, we can never recycle if (psOriginal && gi.bIsFromZone(psOriginal, TAG_G_ALLOC)) { if (!strcmp(psOriginal,sString)) {//it's a legal ptr and they're the same so let's just reuse it instead of free/alloc return psOriginal; } gi.Free(psOriginal); } #endif return G_NewString(sString); } return NULL; }
void SP_misc_beam(gentity_t *self) { char *str; G_SpawnString("target2", "", &str); if (*str) { self->message = G_NewString(str); } G_SpawnString("shader", "lightningBolt", &str); if (*str) { self->s.modelindex2 = G_ShaderIndex(str); } G_SpawnInt("scale", "1", &self->s.torsoAnim); G_SpawnVector("color", "1 1 1", self->s.angles2); // let everything else get spawned before we start firing self->accuracy = 0; self->think = misc_beam_start; self->nextthink = level.time + FRAMETIME; }
void SP_target_checkpoint(gentity_t *ent) { char *t = NULL; int timerunNum = 0; // Nico, used to look for parent gentity_t *parent = NULL; // Nico, override wait -1 or wait 9999 on timer check entities if (g_forceTimerReset.integer) { parent = G_FindByTarget(NULL, ent->targetname); if (parent && parent->wait != 0.5 && !Q_stricmp(parent->classname, "trigger_multiple")) { G_DPrintf("%s: SP_target_checkpoint, wait found = %f, overrided to 0.5\n", GAME_VERSION, parent->wait); G_SpawnFloat("wait", "0.5", &parent->wait); } } G_SpawnString("name", "default", &t); ent->timerunName = G_NewString(t); // create a timerun with this name if it doesn't exit yet timerunNum = GetTimerunNum(ent->timerunName); if (level.numCheckpoints[timerunNum] >= MAX_TIMERUN_CHECKPOINTS) { G_Error("Exceeded maximum number of 'target_checkpoint' entities in '%s' timerun (max %i)\n", ent->timerunName, MAX_TIMERUN_CHECKPOINTS); return; } ent->count = level.numCheckpoints[timerunNum]; ent->use = target_checkpoint_use; level.numCheckpoints[timerunNum]++; level.isTimerun = qtrue; }
static char *GetStringPtr(int iStrlen, char *psOriginal/*may be NULL*/) { if (iStrlen != -1) { char sString[768]; // arb, inc if nec. sString[0]=0; assert(iStrlen+1<=(int)sizeof(sString)); ojk::SavedGameHelper saved_game( ::gi.saved_game); saved_game.read_chunk( INT_ID('S', 'T', 'R', 'G'), sString, iStrlen); // TAG_G_ALLOC is always blown away, we can never recycle if (psOriginal && gi.bIsFromZone(psOriginal, TAG_G_ALLOC)) { if (!strcmp(psOriginal,sString)) {//it's a legal ptr and they're the same so let's just reuse it instead of free/alloc return psOriginal; } gi.Free(psOriginal); } return G_NewString(sString); } return NULL; }
void UserSpawn( gentity_t *ent, const char *name ) { vec3_t origin; vec3_t vf; vec3_t angles; gentity_t *ent2; //Spawn the ent ent2 = G_Spawn(); ent2->classname = G_NewString( name ); //FIXME: This will leave floating memory... //TODO: This should ultimately make sure this is a safe spawn! //Spawn the entity and place it there VectorSet( angles, 0, ent->s.apos.trBase[YAW], 0 ); AngleVectors( angles, vf, NULL, NULL ); VectorMA( ent->s.pos.trBase, 96, vf, origin ); //FIXME: Find the radius size of the object, and push out 32 + radius origin[2] += 8; VectorCopy( origin, ent2->s.pos.trBase ); VectorCopy( origin, ent2->s.origin ); VectorCopy( ent->s.apos.trBase, ent2->s.angles ); gi.linkentity( ent2 ); //Find a valid spawning spot if ( G_CallSpawn( ent2 ) == qfalse ) { gi.SendServerCommand( ent-g_entities, "print \"Failed to spawn '%s'\n\"", name ); G_FreeEntity( ent2 ); return; } }
//---------------------------------------------------------- void fx_explosion_trail_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { gentity_t *missile = G_Spawn(); // We aren't a missile in the truest sense, rather we just move through the world and spawn effects if ( missile ) { missile->classname = "fx_exp_trail"; missile->nextthink = level.time + 50; missile->e_ThinkFunc = thinkF_fx_explosion_trail_think; missile->s.eType = ET_MOVER; missile->owner = self; missile->s.modelindex = self->s.modelindex2; missile->s.pos.trTime = level.time; G_SetOrigin( missile, self->currentOrigin ); if ( self->spawnflags & 1 ) // gravity { missile->s.pos.trType = TR_GRAVITY; } else { missile->s.pos.trType = TR_LINEAR; } missile->spawnflags = self->spawnflags; G_SetAngles( missile, self->currentAngles ); VectorScale( self->currentAngles, self->speed, missile->s.pos.trDelta ); missile->s.pos.trTime = level.time; missile->radius = self->radius; missile->damage = self->damage; missile->splashDamage = self->splashDamage; missile->splashRadius = self->splashRadius; missile->fxID = self->fxID; missile->cameraGroup = self->cameraGroup; //fxfile2 missile->clipmask = MASK_SHOT; gi.linkentity( missile ); if ( VALIDSTRING( self->soundSet ) == true ) { G_AddEvent( self, EV_BMODEL_SOUND, CAS_GetBModelSound( self->soundSet, BMS_START )); missile->s.loopSound = CAS_GetBModelSound( self->soundSet, BMS_MID ); missile->soundSet = G_NewString(self->soundSet);//get my own copy so i can free it when i die if ( missile->s.loopSound < 0 ) { missile->s.loopSound = 0; } } } }
/* =============== G_ParseField Takes a key/value pair and sets the binary values in a gentity =============== */ void G_ParseField(const char *key, const char *value, gentity_t *ent) { field_t *f; byte *b; float v; vec3_t vec; vec4_t vec4; for (f = fields; f->name; f++) { if (!Q_stricmp(f->name, key)) { // found it b = (byte *) ent; switch (f->type) { case F_LSTRING: *(char **) (b + f->ofs) = G_NewString(value); break; case F_VECTOR: sscanf(value, "%f %f %f", &vec[ 0 ], &vec[ 1 ], &vec[ 2 ]); ((float *) (b + f->ofs))[ 0 ] = vec[ 0 ]; ((float *) (b + f->ofs))[ 1 ] = vec[ 1 ]; ((float *) (b + f->ofs))[ 2 ] = vec[ 2 ]; break; case F_VECTOR4: sscanf(value, "%f %f %f %f", &vec4[ 0 ], &vec4[ 1 ], &vec4[ 2 ], &vec4[ 3 ]); ((float *) (b + f->ofs))[ 0 ] = vec4[ 0 ]; ((float *) (b + f->ofs))[ 1 ] = vec4[ 1 ]; ((float *) (b + f->ofs))[ 2 ] = vec4[ 2 ]; ((float *) (b + f->ofs))[ 3 ] = vec4[ 3 ]; break; case F_INT: *(int *) (b + f->ofs) = atoi(value); break; case F_FLOAT: *(float *) (b + f->ofs) = atof(value); break; case F_ANGLEHACK: v = atof(value); ((float *) (b + f->ofs))[ 0 ] = 0; ((float *) (b + f->ofs))[ 1 ] = v; ((float *) (b + f->ofs))[ 2 ] = 0; break; default: case F_IGNORE: break; } return; } } }
/* =============== G_ParseField Takes a key/value pair and sets the binary values in a gentity =============== */ void G_ParseField( const char *key, const char *value, gentity_t *ent ) { field_t *f; byte *b; float v; vec3_t vec; vec4_t vec4; f = bsearch( key, fields, ARRAY_LEN( fields ), sizeof( field_t ), cmdcmp ); if ( !f ) { return; } b = ( byte * ) ent; switch ( f->type ) { case F_STRING: * ( char ** )( b + f->ofs ) = G_NewString( value ); break; case F_VECTOR: sscanf( value, "%f %f %f", &vec[ 0 ], &vec[ 1 ], &vec[ 2 ] ); ( ( float * )( b + f->ofs ) ) [ 0 ] = vec[ 0 ]; ( ( float * )( b + f->ofs ) ) [ 1 ] = vec[ 1 ]; ( ( float * )( b + f->ofs ) ) [ 2 ] = vec[ 2 ]; break; case F_VECTOR4: sscanf( value, "%f %f %f %f", &vec4[ 0 ], &vec4[ 1 ], &vec4[ 2 ], &vec4[ 3 ] ); ( ( float * )( b + f->ofs ) ) [ 0 ] = vec4[ 0 ]; ( ( float * )( b + f->ofs ) ) [ 1 ] = vec4[ 1 ]; ( ( float * )( b + f->ofs ) ) [ 2 ] = vec4[ 2 ]; ( ( float * )( b + f->ofs ) ) [ 3 ] = vec4[ 3 ]; break; case F_INT: * ( int * )( b + f->ofs ) = atoi( value ); break; case F_FLOAT: * ( float * )( b + f->ofs ) = atof( value ); break; case F_ANGLEHACK: v = atof( value ); ( ( float * )( b + f->ofs ) ) [ 0 ] = 0; ( ( float * )( b + f->ofs ) ) [ 1 ] = v; ( ( float * )( b + f->ofs ) ) [ 2 ] = 0; break; } }
void G_ParseField( const char *key, const char *value, gentity_t *ent ) { field_t *f; byte *b; float v; vec3_t vec; f = (field_t *)bsearch( key, fields, ARRAY_LEN( fields ), sizeof( field_t ), fieldcmp ); if ( f ) {// found it b = (byte *)ent; switch( f->type ) { case F_STRING: *(char **)(b+f->ofs) = G_NewString (value); break; case F_VECTOR: sscanf (value, "%f %f %f", &vec[0], &vec[1], &vec[2]); ((float *)(b+f->ofs))[0] = vec[0]; ((float *)(b+f->ofs))[1] = vec[1]; ((float *)(b+f->ofs))[2] = vec[2]; break; case F_INT: *(int *)(b+f->ofs) = atoi(value); break; case F_FLOAT: *(float *)(b+f->ofs) = atof(value); break; case F_ANGLEHACK: v = atof(value); ((float *)(b+f->ofs))[0] = 0; ((float *)(b+f->ofs))[1] = v; ((float *)(b+f->ofs))[2] = 0; break; case F_PARM1: case F_PARM2: case F_PARM3: case F_PARM4: case F_PARM5: case F_PARM6: case F_PARM7: case F_PARM8: case F_PARM9: case F_PARM10: case F_PARM11: case F_PARM12: case F_PARM13: case F_PARM14: case F_PARM15: case F_PARM16: Q3_SetParm( ent->s.number, (f->type - F_PARM1), (char *) value ); break; } return; } }
/*QUAKED target_play_music (1 0 0) (-4 -4 -4) (4 4 4) target_play_music Plays the requested music files when this target is used. "targetname" "music" music WAV or MP3 file ( music/introfile.mp3 [optional] music/loopfile.mp3 ) If an intro file and loop file are specified, the intro plays first, then the looping portion will start and loop indefinetly. If no introfile is entered, only the loopfile will play. */ void SP_target_play_music( gentity_t *self ) { char *s; G_SetOrigin( self, self->s.origin ); if (!G_SpawnString( "music", "", &s )) { G_Error( "target_play_music without a music key at %s", vtos( self->s.origin ) ); } self->message = G_NewString(s); self->use = target_play_music_use; }
/** * @brief Takes a key-value pair and sets the binary values in an entity. */ static void G_ParseField(const char *key, const char *value, g_entity_t *ent) { const g_field_t *f; byte *b; vec_t v; vec3_t vec; for (f = fields; f->name; f++) { if (!(f->flags & FFL_NO_SPAWN) && !g_ascii_strcasecmp(f->name, key)) { // found it if (f->flags & FFL_SPAWN_TEMP) { b = (byte *) &g_game.spawn; } else { b = (byte *) ent; } switch (f->type) { case F_SHORT: *(int16_t *) (b + f->ofs) = (int16_t) atoi(value); break; case F_INT: *(int32_t *) (b + f->ofs) = atoi(value); break; case F_FLOAT: *(vec_t *) (b + f->ofs) = atof(value); break; case F_STRING: *(char **) (b + f->ofs) = G_NewString(value); break; case F_VECTOR: sscanf(value, "%f %f %f", &vec[0], &vec[1], &vec[2]); ((vec_t *) (b + f->ofs))[0] = vec[0]; ((vec_t *) (b + f->ofs))[1] = vec[1]; ((vec_t *) (b + f->ofs))[2] = vec[2]; break; case F_ANGLE: v = atof(value); ((vec_t *) (b + f->ofs))[0] = 0; ((vec_t *) (b + f->ofs))[1] = v; ((vec_t *) (b + f->ofs))[2] = 0; break; default: break; } return; } } //gi.Debug("%s is not a field\n", key); }
gentity_t *G_DropSaberItem( const char *saberType, saber_colors_t saberColor, vec3_t saberPos, vec3_t saberVel, vec3_t saberAngles, gentity_t *copySaber ) {//turn it into a pick-uppable item! gentity_t *newItem = NULL; if ( saberType && saberType[0] ) {//have a valid string to use for saberType newItem = G_Spawn(); if ( newItem ) { newItem->classname = G_NewString( "weapon_saber" ); VectorCopy( saberPos, newItem->s.origin ); G_SetOrigin( newItem, newItem->s.origin ); VectorCopy( saberAngles, newItem->s.angles ); G_SetAngles( newItem, newItem->s.angles ); newItem->spawnflags = 128;/*ITMSF_USEPICKUP*/ newItem->spawnflags |= 64;/*ITMSF_NOGLOW*/ newItem->NPC_type = G_NewString( saberType );//saberType //FIXME: transfer per-blade color somehow? newItem->NPC_targetname = (char *)saberColorStringForColor[saberColor]; newItem->count = 1; newItem->flags = FL_DROPPED_ITEM; G_SpawnItem( newItem, FindItemForWeapon( WP_SABER ) ); newItem->s.pos.trType = TR_GRAVITY; newItem->s.pos.trTime = level.time; VectorCopy( saberVel, newItem->s.pos.trDelta ); //newItem->s.eFlags |= EF_BOUNCE_HALF; //copy some values from another saber, if provided: G_CopySaberItemValues( copySaber, newItem ); //don't *think* about calling FinishSpawningItem, just do it! newItem->e_ThinkFunc = thinkF_NULL; newItem->nextthink = -1; FinishSpawningItem( newItem ); newItem->delay = level.time + 500;//so you can't pick it back up right away } } return newItem; }
static void IT_PickupSound( const char **holdBuf ) { int len; const char *tokenStr; if ( COM_ParseString( holdBuf, &tokenStr ) ) { return; } len = strlen( tokenStr ) + 1; if ( len > 32 ) { len = 32; gi.Printf("WARNING: Pickup Sound too long in external ITEMS.DAT '%s'\n", tokenStr); } bg_itemlist[itemNum].pickup_sound = G_NewString(tokenStr); }
static void IT_WorldModel( const char **holdBuf ) { int len; const char *tokenStr; if ( COM_ParseString( holdBuf, &tokenStr ) ) { return; } len = strlen( tokenStr ) + 1; if ( len > 64 ) { len = 64; gi.Printf( "WARNING: world model too long in external ITEMS.DAT '%s'\n", tokenStr ); } bg_itemlist[itemNum].world_model = G_NewString( tokenStr ); }
static void IT_ClassName( const char **holdBuf ) { int len; const char *tokenStr; if ( COM_ParseString( holdBuf, &tokenStr ) ) { return; } len = strlen( tokenStr ) + 1; if ( len > 32 ) { len = 32; gi.Printf( "WARNING: weaponclass too long in external ITEMS.DAT '%s'\n", tokenStr ); } bg_itemlist[itemNum].classname = G_NewString( tokenStr ); }
/*QUAKED target_level_change (1 0 0) (-4 -4 -4) (4 4 4) "mapname" - Name of map to change to */ void SP_target_level_change( gentity_t *self ) { char *s; G_SpawnString( "mapname", "", &s ); self->message = G_NewString(s); if ( !self->message || !self->message[0] ) { G_Error( "target_level_change with no mapname!\n"); return; } G_SetOrigin( self, self->s.origin ); self->use = target_level_change_use; }
char *GetStringPtr(int iStrlen, char *psOriginal/*may be NULL*/) { if (iStrlen != -1) { static char sString[768]; // arb, inc if nec. memset(sString,0, sizeof(sString)); assert(iStrlen+1<=sizeof(sString)); gi.ReadFromSaveGame('STRG', sString, iStrlen, NULL); // we can't do string recycling with the new g_alloc pool dumping, so just always alloc here... // return G_NewString(sString); } return NULL; }
static void IT_Icon(const char **holdBuf) { int len; const char *tokenStr; if (COM_ParseString(holdBuf,&tokenStr)) { return; } len = strlen(tokenStr); len++; if (len > 32) { len = 32; gi.Printf("WARNING: icon too long in external ITEMS.DAT '%s'\n", tokenStr); } bg_itemlist[itemParms.itemNum].icon = G_NewString(tokenStr); }
void SP_target_interest( gentity_t *self ) {//FIXME: rename point_interest if(level.numInterestPoints >= MAX_INTEREST_POINTS) { gi.Printf("ERROR: Too many interest points, limit is %d\n", MAX_INTEREST_POINTS); G_FreeEntity(self); return; } VectorCopy(self->currentOrigin, level.interestPoints[level.numInterestPoints].origin); if(self->target && self->target[0]) { level.interestPoints[level.numInterestPoints].target = G_NewString( self->target ); } level.numInterestPoints++; G_FreeEntity(self); }
// Finds the spawn function for the entity and calls it, returning qfalse if not found qboolean G_CallSpawn( gentity_t *ent ) { spawn_t *s; const gitem_t *item; char buf[MAX_STRING_CHARS] = { 0 }; if ( !ent->classname ) { trap->Print( "G_CallSpawn: NULL classname\n" ); return qfalse; } // swap this entity out for something else trap->Cvar_VariableStringBuffer( va( "replace_%s", ent->classname ), buf, sizeof(buf) ); if ( buf[0] ) ent->classname = G_NewString( buf ); // check item spawn functions //RAZTODO: cant reorder items because compat so....? for ( item = bg_itemlist + 1; item->classname; item++ ) { if ( !strcmp( item->classname, ent->classname ) ) { G_SpawnItem( ent, item ); return qtrue; } } // check normal spawn functions s = (spawn_t *)bsearch( ent->classname, spawns, ARRAY_LEN( spawns ), sizeof(spawn_t), spawncmp ); if ( s ) {// found it if ( VALIDSTRING( ent->healingsound ) ) G_SoundIndex( ent->healingsound ); s->spawn( ent ); return qtrue; } trap->Print( "%s doesn't have a spawn function\n", ent->classname ); return qfalse; }
/*QUAKED target_play_music (1 0 0) (-4 -4 -4) (4 4 4) target_play_music Plays the requested music files when this target is used. "targetname" "music" music WAV or MP3 file ( music/introfile.mp3 [optional] music/loopfile.mp3 ) If an intro file and loop file are specified, the intro plays first, then the looping portion will start and loop indefinetly. If no introfile is entered, only the loopfile will play. */ void SP_target_play_music( gentity_t *self ) { char *s; G_SetOrigin( self, self->s.origin ); if (!G_SpawnString( "music", "", &s )) { G_Error( "target_play_music without a music key at %s", vtos( self->s.origin ) ); } self->message = G_NewString (s); self->e_UseFunc = useF_target_play_music_use; extern cvar_t *com_buildScript; //Precache! if (com_buildScript->integer) {//copy this puppy over char buffer[MAX_QPATH]; fileHandle_t hFile; Q_strncpyz( buffer, s, sizeof(buffer) ); COM_DefaultExtension( buffer, sizeof(buffer), ".mp3"); gi.FS_FOpenFile(buffer, &hFile, FS_READ); if (hFile) { gi.FS_FCloseFile( hFile ); } } }
void GunRackAddItem( gitem_t *gun, vec3_t org, vec3_t angs, float ffwd, float fright, float fup ) { vec3_t fwd, right; gentity_t *it_ent = G_Spawn(); qboolean rotate = qtrue; AngleVectors( angs, fwd, right, NULL ); if ( it_ent && gun ) { // FIXME: scaling the ammo will probably need to be tweaked to a reasonable amount...adjust as needed // Set base ammo per type if ( gun->giType == IT_WEAPON ) { it_ent->spawnflags |= 16;// VERTICAL switch( gun->giTag ) { case WP_BLASTER: it_ent->count = 15; break; case WP_REPEATER: it_ent->count = 100; break; case WP_ROCKET_LAUNCHER: it_ent->count = 4; break; } } else { rotate = qfalse; // must deliberately make it small, or else the objects will spawn inside of each other. VectorSet( it_ent->maxs, 6.75f, 6.75f, 6.75f ); VectorScale( it_ent->maxs, -1, it_ent->mins ); } it_ent->spawnflags |= 1;// ITMSF_SUSPEND it_ent->classname = G_NewString(gun->classname); //copy it so it can be freed safely G_SpawnItem( it_ent, gun ); // FinishSpawningItem handles everything, so clear the thinkFunc that was set in G_SpawnItem FinishSpawningItem( it_ent ); if ( gun->giType == IT_AMMO ) { if ( gun->giTag == AMMO_BLASTER ) // I guess this just has to use different logic?? { if ( g_spskill->integer >= 2 ) { it_ent->count += 10; // give more on higher difficulty because there will be more/harder enemies? } } else { // scale ammo based on skill switch ( g_spskill->integer ) { case 0: // do default break; case 1: it_ent->count *= 0.75f; break; case 2: it_ent->count *= 0.5f; break; } } } it_ent->nextthink = 0; VectorCopy( org, it_ent->s.origin ); VectorMA( it_ent->s.origin, fright, right, it_ent->s.origin ); VectorMA( it_ent->s.origin, ffwd, fwd, it_ent->s.origin ); it_ent->s.origin[2] += fup; VectorCopy( angs, it_ent->s.angles ); // by doing this, we can force the amount of ammo we desire onto the weapon for when it gets picked-up it_ent->flags |= ( FL_DROPPED_ITEM | FL_FORCE_PULLABLE_ONLY ); it_ent->physicsBounce = 0.1f; for ( int t = 0; t < 3; t++ ) { if ( rotate ) { if ( t == YAW ) { it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + 180 + crandom() * 14 ); } else { it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + crandom() * 4 ); } } else { if ( t == YAW ) { it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + 90 + crandom() * 4 ); } } } G_SetAngles( it_ent, it_ent->s.angles ); G_SetOrigin( it_ent, it_ent->s.origin ); gi.linkentity( it_ent ); } }
static qboolean G_InitRoff( char *file, unsigned char *data ) { roff_hdr_t *header = (roff_hdr_t *)data; int count = (int)header->mCount; int i; roffs[num_roffs].fileName = G_NewString( file ); if ( header->mVersion == ROFF_VERSION ) { // We are Old School(tm) roffs[num_roffs].type = 1; roffs[num_roffs].data = (void *) G_Alloc( count * sizeof( move_rotate_t ) ); move_rotate_t *mem = (move_rotate_t *)roffs[num_roffs].data; roffs[num_roffs].mFrameTime = 100; // old school ones have a hard-coded frame time roffs[num_roffs].mLerp = 10; roffs[num_roffs].mNumNoteTracks = 0; roffs[num_roffs].mNoteTrackIndexes = NULL; if ( mem ) { // The allocation worked, so stash this stuff off so we can reference the data later if needed roffs[num_roffs].frames = count; // Step past the header to get to the goods move_rotate_t *roff_data = ( move_rotate_t *)&header[1]; // Copy all of the goods into our ROFF cache for ( int i = 0; i < count; i++, roff_data++, mem++ ) { // Copy just the delta position and orientation which can be applied to anything at a later point VectorCopy( roff_data->origin_delta, mem->origin_delta ); VectorCopy( roff_data->rotate_delta, mem->rotate_delta ); } return qtrue; } } else if ( header->mVersion == ROFF_VERSION2 ) { // Version 2.0, heck yeah! roff_hdr2_t *hdr = (roff_hdr2_t *)data; count = hdr->mCount; roffs[num_roffs].frames = count; roffs[num_roffs].data = (void *) G_Alloc( count * sizeof( move_rotate2_t )); move_rotate2_t *mem = (move_rotate2_t *)roffs[num_roffs].data; if ( mem ) { roffs[num_roffs].mFrameTime = hdr->mFrameRate; roffs[num_roffs].mLerp = 1000 / hdr->mFrameRate; roffs[num_roffs].mNumNoteTracks = hdr->mNumNotes; if (roffs[num_roffs].mFrameTime < 50) { Com_Printf(S_COLOR_RED"Error: \"%s\" has an invalid ROFF framerate (%d < 50)\n", file, roffs[num_roffs].mFrameTime); } assert( roffs[num_roffs].mFrameTime >= 50 );//HAS to be at least 50 to be reliable // Step past the header to get to the goods move_rotate2_t *roff_data = ( move_rotate2_t *)&hdr[1]; roffs[num_roffs].type = 2; //rww - any reason this wasn't being set already? // Copy all of the goods into our ROFF cache for ( i = 0; i < count; i++ ) { VectorCopy( roff_data[i].origin_delta, mem[i].origin_delta ); VectorCopy( roff_data[i].rotate_delta, mem[i].rotate_delta ); mem[i].mStartNote = roff_data[i].mStartNote; mem[i].mNumNotes = roff_data[i].mNumNotes; } if ( hdr->mNumNotes ) { int size; char *ptr, *start; ptr = start = (char *)&roff_data[i]; size = 0; for( i = 0; i < hdr->mNumNotes; i++ ) { size += strlen(ptr) + 1; ptr += strlen(ptr) + 1; } // ? Get rid of dynamic memory ? roffs[num_roffs].mNoteTrackIndexes = new char *[hdr->mNumNotes]; ptr = roffs[num_roffs].mNoteTrackIndexes[0] = new char[size]; memcpy(roffs[num_roffs].mNoteTrackIndexes[0], start, size); for( i = 1; i < hdr->mNumNotes; i++ ) { ptr += strlen(ptr) + 1; roffs[num_roffs].mNoteTrackIndexes[i] = ptr; } } return qtrue; } } return false; }
/* void NPC_Precache ( char *NPCName ) Precaches NPC skins, tgas and md3s. */ void NPC_Precache ( gentity_t *spawner ) { clientInfo_t ci={0}; renderInfo_t ri={0}; team_t playerTeam = TEAM_FREE; const char *token; const char *value; const char *p; char *patch; char sound[MAX_QPATH]; qboolean md3Model = qfalse; char playerModel[MAX_QPATH]; char customSkin[MAX_QPATH]; if ( !Q_stricmp( "random", spawner->NPC_type ) ) {//sorry, can't precache a random just yet return; } strcpy(customSkin,"default"); p = NPCParms; COM_BeginParseSession(); // look for the right NPC while ( p ) { token = COM_ParseExt( &p, qtrue ); if ( token[0] == 0 ) { return; } if ( !Q_stricmp( token, spawner->NPC_type ) ) { break; } SkipBracedSection( &p ); } if ( !p ) { return; } if ( G_ParseLiteral( &p, "{" ) ) { return; } // parse the NPC info block while ( 1 ) { token = COM_ParseExt( &p, qtrue ); if ( !token[0] ) { gi.Printf( S_COLOR_RED"ERROR: unexpected EOF while parsing '%s'\n", spawner->NPC_type ); return; } if ( !Q_stricmp( token, "}" ) ) { break; } // headmodel if ( !Q_stricmp( token, "headmodel" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if(!Q_stricmp("none", value)) { } else { Q_strncpyz( ri.headModelName, value, sizeof(ri.headModelName), qtrue); } md3Model = qtrue; continue; } // torsomodel if ( !Q_stricmp( token, "torsomodel" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if(!Q_stricmp("none", value)) { } else { Q_strncpyz( ri.torsoModelName, value, sizeof(ri.torsoModelName), qtrue); } md3Model = qtrue; continue; } // legsmodel if ( !Q_stricmp( token, "legsmodel" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } Q_strncpyz( ri.legsModelName, value, sizeof(ri.legsModelName), qtrue); md3Model = qtrue; continue; } // playerModel if ( !Q_stricmp( token, "playerModel" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } Q_strncpyz( playerModel, value, sizeof(playerModel), qtrue); md3Model = qfalse; continue; } // customSkin if ( !Q_stricmp( token, "customSkin" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } Q_strncpyz( customSkin, value, sizeof(customSkin), qtrue); continue; } // playerTeam if ( !Q_stricmp( token, "playerTeam" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } playerTeam = TranslateTeamName(value); continue; } // snd if ( !Q_stricmp( token, "snd" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if ( !(spawner->svFlags&SVF_NO_BASIC_SOUNDS) ) { //FIXME: store this in some sound field or parse in the soundTable like the animTable... Q_strncpyz( sound, value, sizeof( sound ) ); patch = strstr( sound, "/" ); if ( patch ) { *patch = 0; } ci.customBasicSoundDir = G_NewString( sound ); } continue; } // sndcombat if ( !Q_stricmp( token, "sndcombat" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if ( !(spawner->svFlags&SVF_NO_COMBAT_SOUNDS) ) { //FIXME: store this in some sound field or parse in the soundTable like the animTable... Q_strncpyz( sound, value, sizeof( sound ) ); patch = strstr( sound, "/" ); if ( patch ) { *patch = 0; } ci.customCombatSoundDir = G_NewString( sound ); } continue; } // sndextra if ( !Q_stricmp( token, "sndextra" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if ( !(spawner->svFlags&SVF_NO_EXTRA_SOUNDS) ) { //FIXME: store this in some sound field or parse in the soundTable like the animTable... Q_strncpyz( sound, value, sizeof( sound ) ); patch = strstr( sound, "/" ); if ( patch ) { *patch = 0; } ci.customExtraSoundDir = G_NewString( sound ); } continue; } // sndjedi if ( !Q_stricmp( token, "sndjedi" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if ( !(spawner->svFlags&SVF_NO_EXTRA_SOUNDS) ) { //FIXME: store this in some sound field or parse in the soundTable like the animTable... Q_strncpyz( sound, value, sizeof( sound ) ); patch = strstr( sound, "/" ); if ( patch ) { *patch = 0; } ci.customJediSoundDir = G_NewString( sound ); } continue; } } if ( md3Model ) { CG_RegisterClientRenderInfo( &ci, &ri ); } else { char skinName[MAX_QPATH]; //precache ghoul2 model gi.G2API_PrecacheGhoul2Model( va( "models/players/%s/model.glm", playerModel ) ); //precache skin Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_%s.skin", playerModel, customSkin ); // lets see if it's out there gi.RE_RegisterSkin( skinName ); } //precache this NPC's possible weapons NPC_PrecacheWeapons( playerTeam, spawner->spawnflags, spawner->NPC_type ); CG_RegisterNPCCustomSounds( &ci ); CG_RegisterNPCEffects( playerTeam ); //FIXME: Look for a "sounds" directory and precache death, pain, alert sounds }
qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) { const char *token; const char *value; const char *p; int n; float f; char *patch; char sound[MAX_QPATH]; char playerModel[MAX_QPATH]; char customSkin[MAX_QPATH]; clientInfo_t *ci = &NPC->client->clientInfo; renderInfo_t *ri = &NPC->client->renderInfo; gNPCstats_t *stats = NULL; qboolean md3Model = qtrue; char surfOff[1024]; char surfOn[1024]; strcpy(customSkin,"default"); if ( !NPCName || !NPCName[0]) { NPCName = "Player"; } if ( NPC->NPC ) { stats = &NPC->NPC->stats; /* NPC->NPC->allWeaponOrder[0] = WP_BRYAR_PISTOL; NPC->NPC->allWeaponOrder[1] = WP_SABER; NPC->NPC->allWeaponOrder[2] = WP_IMOD; NPC->NPC->allWeaponOrder[3] = WP_SCAVENGER_RIFLE; NPC->NPC->allWeaponOrder[4] = WP_TRICORDER; NPC->NPC->allWeaponOrder[6] = WP_NONE; NPC->NPC->allWeaponOrder[6] = WP_NONE; NPC->NPC->allWeaponOrder[7] = WP_NONE; */ // fill in defaults stats->aggression = 3; stats->aim = 3; stats->earshot = 1024; stats->evasion = 3; stats->hfov = 90; stats->intelligence = 3; stats->move = 3; stats->reactions = 3; stats->vfov = 60; stats->vigilance = 0.1f; stats->visrange = 1024; stats->health = 0; stats->moveType = MT_RUNJUMP; stats->yawSpeed = 90; stats->walkSpeed = 90; stats->runSpeed = 300; stats->acceleration = 15;//Increase/descrease speed this much per frame (20fps) } else { stats = NULL; } Q_strncpyz( ci->name, NPCName, sizeof( ci->name ) ); NPC->playerModel = -1; //Set defaults //FIXME: should probably put default torso and head models, but what about enemies //that don't have any- like Stasis? //Q_strncpyz( ri->headModelName, DEFAULT_HEADMODEL, sizeof(ri->headModelName), qtrue); //Q_strncpyz( ri->torsoModelName, DEFAULT_TORSOMODEL, sizeof(ri->torsoModelName), qtrue); //Q_strncpyz( ri->legsModelName, DEFAULT_LEGSMODEL, sizeof(ri->legsModelName), qtrue); memset( ri->headModelName, 0, sizeof( ri->headModelName ) ); memset( ri->torsoModelName, 0, sizeof( ri->torsoModelName ) ); memset( ri->legsModelName, 0, sizeof( ri->legsModelName ) ); //FIXME: should we have one for weapon too? memset( (char *)surfOff, 0, sizeof(surfOff) ); memset( (char *)surfOn, 0, sizeof(surfOn) ); /* ri->headYawRangeLeft = 50; ri->headYawRangeRight = 50; ri->headPitchRangeUp = 40; ri->headPitchRangeDown = 50; ri->torsoYawRangeLeft = 60; ri->torsoYawRangeRight = 60; ri->torsoPitchRangeUp = 30; ri->torsoPitchRangeDown = 70; */ ri->headYawRangeLeft = 80; ri->headYawRangeRight = 80; ri->headPitchRangeUp = 45; ri->headPitchRangeDown = 45; ri->torsoYawRangeLeft = 60; ri->torsoYawRangeRight = 60; ri->torsoPitchRangeUp = 30; ri->torsoPitchRangeDown = 50; VectorCopy(playerMins, NPC->mins); VectorCopy(playerMaxs, NPC->maxs); NPC->client->crouchheight = CROUCH_MAXS_2; NPC->client->standheight = DEFAULT_MAXS_2; NPC->client->dismemberProbHead = 100; NPC->client->dismemberProbArms = 100; NPC->client->dismemberProbHands = 100; NPC->client->dismemberProbWaist = 100; NPC->client->dismemberProbLegs = 100; if ( !Q_stricmp( "random", NPCName ) ) {//Randomly assemble a starfleet guy NPC_BuildRandom( NPC ); } else { p = NPCParms; COM_BeginParseSession(); // look for the right NPC while ( p ) { token = COM_ParseExt( &p, qtrue ); if ( token[0] == 0 ) { return qfalse; } if ( !Q_stricmp( token, NPCName ) ) { break; } SkipBracedSection( &p ); } if ( !p ) { return qfalse; } if ( G_ParseLiteral( &p, "{" ) ) { return qfalse; } // parse the NPC info block while ( 1 ) { token = COM_ParseExt( &p, qtrue ); if ( !token[0] ) { gi.Printf( S_COLOR_RED"ERROR: unexpected EOF while parsing '%s'\n", NPCName ); return qfalse; } if ( !Q_stricmp( token, "}" ) ) { break; } //===MODEL PROPERTIES=========================================================== // headmodel if ( !Q_stricmp( token, "headmodel" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if(!Q_stricmp("none", value)) { ri->headModelName[0] = NULL; //Zero the head clamp range so the torso & legs don't lag behind ri->headYawRangeLeft = ri->headYawRangeRight = ri->headPitchRangeUp = ri->headPitchRangeDown = 0; } else { Q_strncpyz( ri->headModelName, value, sizeof(ri->headModelName), qtrue); } continue; } // torsomodel if ( !Q_stricmp( token, "torsomodel" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if(!Q_stricmp("none", value)) { ri->torsoModelName[0] = NULL; //Zero the torso clamp range so the legs don't lag behind ri->torsoYawRangeLeft = ri->torsoYawRangeRight = ri->torsoPitchRangeUp = ri->torsoPitchRangeDown = 0; } else { Q_strncpyz( ri->torsoModelName, value, sizeof(ri->torsoModelName), qtrue); } continue; } // legsmodel if ( !Q_stricmp( token, "legsmodel" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } Q_strncpyz( ri->legsModelName, value, sizeof(ri->legsModelName), qtrue); //Need to do this here to get the right index G_ParseAnimFileSet( ri->legsModelName, ri->legsModelName, &ci->animFileIndex ); continue; } // playerModel if ( !Q_stricmp( token, "playerModel" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } Q_strncpyz( playerModel, value, sizeof(playerModel), qtrue); md3Model = qfalse; continue; } // customSkin if ( !Q_stricmp( token, "customSkin" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } Q_strncpyz( customSkin, value, sizeof(customSkin), qtrue); continue; } // surfOff if ( !Q_stricmp( token, "surfOff" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if ( surfOff[0] ) { strncat( (char *)surfOff, ",", sizeof(surfOff) ); strncat( (char *)surfOff, value, sizeof(surfOff) ); } else { Q_strncpyz( surfOff, value, sizeof(surfOff), qtrue); } continue; } // surfOn if ( !Q_stricmp( token, "surfOn" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if ( surfOn[0] ) { strncat( (char *)surfOn, ",", sizeof(surfOn) ); strncat( (char *)surfOn, value, sizeof(surfOn) ); } else { Q_strncpyz( surfOn, value, sizeof(surfOn), qtrue); } continue; } //headYawRangeLeft if ( !Q_stricmp( token, "headYawRangeLeft" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName ); continue; } ri->headYawRangeLeft = n; continue; } //headYawRangeRight if ( !Q_stricmp( token, "headYawRangeRight" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName ); continue; } ri->headYawRangeRight = n; continue; } //headPitchRangeUp if ( !Q_stricmp( token, "headPitchRangeUp" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName ); continue; } ri->headPitchRangeUp = n; continue; } //headPitchRangeDown if ( !Q_stricmp( token, "headPitchRangeDown" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName ); continue; } ri->headPitchRangeDown = n; continue; } //torsoYawRangeLeft if ( !Q_stricmp( token, "torsoYawRangeLeft" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName ); continue; } ri->torsoYawRangeLeft = n; continue; } //torsoYawRangeRight if ( !Q_stricmp( token, "torsoYawRangeRight" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName ); continue; } ri->torsoYawRangeRight = n; continue; } //torsoPitchRangeUp if ( !Q_stricmp( token, "torsoPitchRangeUp" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName ); continue; } ri->torsoPitchRangeUp = n; continue; } //torsoPitchRangeDown if ( !Q_stricmp( token, "torsoPitchRangeDown" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName ); continue; } ri->torsoPitchRangeDown = n; continue; } // Uniform XYZ scale if ( !Q_stricmp( token, "scale" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if (n != 100) { NPC->s.modelScale[0] = NPC->s.modelScale[1] = NPC->s.modelScale[2] = n/100.0f; } continue; } //X scale if ( !Q_stricmp( token, "scaleX" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if (n != 100) { NPC->s.modelScale[0] = n/100.0f; } continue; } //Y scale if ( !Q_stricmp( token, "scaleY" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if (n != 100) { NPC->s.modelScale[1] = n/100.0f; } continue; } //Z scale if ( !Q_stricmp( token, "scaleZ" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if (n != 100) { NPC->s.modelScale[2] = n/100.0f; } continue; } //===AI STATS===================================================================== // aggression if ( !Q_stricmp( token, "aggression" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 1 || n > 5 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->aggression = n; } continue; } // aim if ( !Q_stricmp( token, "aim" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 1 || n > 5 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->aim = n; } continue; } // earshot if ( !Q_stricmp( token, "earshot" ) ) { if ( COM_ParseFloat( &p, &f ) ) { SkipRestOfLine( &p ); continue; } if ( f < 0.0f ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->earshot = f; } continue; } // evasion if ( !Q_stricmp( token, "evasion" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 1 || n > 5 ) { gi.Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->evasion = n; } continue; } // hfov if ( !Q_stricmp( token, "hfov" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 30 || n > 180 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->hfov = n;// / 2; //FIXME: Why was this being done?! } continue; } // intelligence if ( !Q_stricmp( token, "intelligence" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 1 || n > 5 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->intelligence = n; } continue; } // move if ( !Q_stricmp( token, "move" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 1 || n > 5 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->move = n; } continue; } // reactions if ( !Q_stricmp( token, "reactions" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 1 || n > 5 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->reactions = n; } continue; } // shootDistance if ( !Q_stricmp( token, "saberColor" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if ( NPC->client ) { NPC->client->ps.saberColor = TranslateSaberColor( value ); } continue; } // shootDistance if ( !Q_stricmp( token, "shootDistance" ) ) { if ( COM_ParseFloat( &p, &f ) ) { SkipRestOfLine( &p ); continue; } if ( f < 0.0f ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->shootDistance = f; } continue; } // shootDistance if ( !Q_stricmp( token, "health" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->health = n; } continue; } // vfov if ( !Q_stricmp( token, "vfov" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 30 || n > 180 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->vfov = n / 2; } continue; } // vigilance if ( !Q_stricmp( token, "vigilance" ) ) { if ( COM_ParseFloat( &p, &f ) ) { SkipRestOfLine( &p ); continue; } if ( f < 0.0f ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->vigilance = f; } continue; } // visrange if ( !Q_stricmp( token, "visrange" ) ) { if ( COM_ParseFloat( &p, &f ) ) { SkipRestOfLine( &p ); continue; } if ( f < 0.0f ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->visrange = f; } continue; } // race // if ( !Q_stricmp( token, "race" ) ) // { // if ( COM_ParseString( &p, &value ) ) // { // continue; // } // NPC->client->race = TranslateRaceName(value); // continue; // } // rank if ( !Q_stricmp( token, "rank" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if ( NPC->NPC ) { NPC->NPC->rank = TranslateRankName(value); } continue; } // fullName if ( !Q_stricmp( token, "fullName" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } NPC->fullName = G_NewString(value); continue; } // playerTeam if ( !Q_stricmp( token, "playerTeam" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } NPC->client->playerTeam = TranslateTeamName(value); continue; } // enemyTeam if ( !Q_stricmp( token, "enemyTeam" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } NPC->client->enemyTeam = TranslateTeamName(value); continue; } // class if ( !Q_stricmp( token, "class" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } NPC->client->NPC_class = TranslateClassName(value); continue; } // dismemberment probability for head if ( !Q_stricmp( token, "dismemberProbHead" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { NPC->client->dismemberProbHead = n; } continue; } // dismemberment probability for arms if ( !Q_stricmp( token, "dismemberProbArms" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { NPC->client->dismemberProbArms = n; } continue; } // dismemberment probability for hands if ( !Q_stricmp( token, "dismemberProbHands" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { NPC->client->dismemberProbHands = n; } continue; } // dismemberment probability for waist if ( !Q_stricmp( token, "dismemberProbWaist" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { NPC->client->dismemberProbWaist = n; } continue; } // dismemberment probability for legs if ( !Q_stricmp( token, "dismemberProbLegs" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { NPC->client->dismemberProbLegs = n; } continue; } //===MOVEMENT STATS============================================================ if ( !Q_stricmp( token, "width" ) ) { if ( COM_ParseInt( &p, &n ) ) { continue; } NPC->mins[0] = NPC->mins[1] = -n; NPC->maxs[0] = NPC->maxs[1] = n; continue; } if ( !Q_stricmp( token, "height" ) ) { if ( COM_ParseInt( &p, &n ) ) { continue; } NPC->mins[2] = DEFAULT_MINS_2;//Cannot change NPC->maxs[2] = NPC->client->standheight = n + DEFAULT_MINS_2; NPC->radius = n; continue; } if ( !Q_stricmp( token, "crouchheight" ) ) { if ( COM_ParseInt( &p, &n ) ) { continue; } NPC->client->crouchheight = n + DEFAULT_MINS_2; continue; } if ( !Q_stricmp( token, "movetype" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if ( NPC->NPC ) { stats->moveType = (movetype_t)MoveTypeNameToEnum(value); } continue; } // yawSpeed if ( !Q_stricmp( token, "yawSpeed" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n <= 0) { gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->yawSpeed = ((float)(n)); } continue; } // walkSpeed if ( !Q_stricmp( token, "walkSpeed" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->walkSpeed = n; } continue; } //runSpeed if ( !Q_stricmp( token, "runSpeed" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->runSpeed = n; } continue; } //acceleration if ( !Q_stricmp( token, "acceleration" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < 0 ) { gi.Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { stats->acceleration = n; } continue; } //===MISC=============================================================================== // default behavior if ( !Q_stricmp( token, "behavior" ) ) { if ( COM_ParseInt( &p, &n ) ) { SkipRestOfLine( &p ); continue; } if ( n < BS_DEFAULT || n >= NUM_BSTATES ) { gi.Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName ); continue; } if ( NPC->NPC ) { NPC->NPC->defaultBehavior = (bState_t)(n); } continue; } // snd if ( !Q_stricmp( token, "snd" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if ( !(NPC->svFlags&SVF_NO_BASIC_SOUNDS) ) { //FIXME: store this in some sound field or parse in the soundTable like the animTable... Q_strncpyz( sound, value, sizeof( sound ) ); patch = strstr( sound, "/" ); if ( patch ) { *patch = 0; } ci->customBasicSoundDir = G_NewString( sound ); } continue; } // sndcombat if ( !Q_stricmp( token, "sndcombat" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if ( !(NPC->svFlags&SVF_NO_COMBAT_SOUNDS) ) { //FIXME: store this in some sound field or parse in the soundTable like the animTable... Q_strncpyz( sound, value, sizeof( sound ) ); patch = strstr( sound, "/" ); if ( patch ) { *patch = 0; } ci->customCombatSoundDir = G_NewString( sound ); } continue; } // sndextra if ( !Q_stricmp( token, "sndextra" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if ( !(NPC->svFlags&SVF_NO_EXTRA_SOUNDS) ) { //FIXME: store this in some sound field or parse in the soundTable like the animTable... Q_strncpyz( sound, value, sizeof( sound ) ); patch = strstr( sound, "/" ); if ( patch ) { *patch = 0; } ci->customExtraSoundDir = G_NewString( sound ); } continue; } // sndjedi if ( !Q_stricmp( token, "sndjedi" ) ) { if ( COM_ParseString( &p, &value ) ) { continue; } if ( !(NPC->svFlags&SVF_NO_EXTRA_SOUNDS) ) { //FIXME: store this in some sound field or parse in the soundTable like the animTable... Q_strncpyz( sound, value, sizeof( sound ) ); patch = strstr( sound, "/" ); if ( patch ) { *patch = 0; } ci->customJediSoundDir = G_NewString( sound ); } continue; } gi.Printf( "WARNING: unknown keyword '%s' while parsing '%s'\n", token, NPCName ); SkipRestOfLine( &p ); } } ci->infoValid = qfalse; /* Ghoul2 Insert Start */ if ( !md3Model ) { NPC->weaponModel = -1; G_SetG2PlayerModel( NPC, playerModel, customSkin, surfOff, surfOn ); } /* Ghoul2 Insert End */ if( NPCsPrecached ) {//Spawning in after initial precache, our models are precached, we just need to set our clientInfo CG_RegisterClientModels( NPC->s.number ); CG_RegisterNPCCustomSounds( ci ); CG_RegisterNPCEffects( NPC->client->playerTeam ); } return qtrue; }
/* =============== G_ParseField Takes a key/value pair and sets the binary values in a gentity =============== */ void G_ParseField( const char *key, const char *rawString, gentity_t *entity ) { fieldDescriptor_t *fieldDescriptor; byte *entityDataField; vec4_t tmpFloatData; variatingTime_t varTime = {0, 0}; fieldDescriptor = bsearch( key, fields, ARRAY_LEN( fields ), sizeof( fieldDescriptor_t ), cmdcmp ); if ( !fieldDescriptor ) { return; } entityDataField = ( byte * ) entity + fieldDescriptor->offset; switch ( fieldDescriptor->type ) { case F_STRING: * ( char ** ) entityDataField = G_NewString( rawString ); break; case F_TARGET: if(entity->targetCount >= MAX_ENTITY_TARGETS) G_Error("Maximal number of %i targets reached.", MAX_ENTITY_TARGETS); ( ( char ** ) entityDataField ) [ entity->targetCount++ ] = G_NewString( rawString ); break; case F_CALLTARGET: if(entity->callTargetCount >= MAX_ENTITY_CALLTARGETS) G_Error("Maximal number of %i calltargets reached. You can solve this by using a Relay.", MAX_ENTITY_CALLTARGETS); ( ( gentityCallDefinition_t * ) entityDataField ) [ entity->callTargetCount++ ] = G_NewCallDefinition( fieldDescriptor->replacement ? fieldDescriptor->replacement : fieldDescriptor->name, rawString ); break; case F_TIME: sscanf( rawString, "%f %f", &varTime.time, &varTime.variance ); * ( variatingTime_t * ) entityDataField = varTime; break; case F_3D_VECTOR: sscanf( rawString, "%f %f %f", &tmpFloatData[ 0 ], &tmpFloatData[ 1 ], &tmpFloatData[ 2 ] ); ( ( float * ) entityDataField ) [ 0 ] = tmpFloatData[ 0 ]; ( ( float * ) entityDataField ) [ 1 ] = tmpFloatData[ 1 ]; ( ( float * ) entityDataField ) [ 2 ] = tmpFloatData[ 2 ]; break; case F_4D_VECTOR: sscanf( rawString, "%f %f %f %f", &tmpFloatData[ 0 ], &tmpFloatData[ 1 ], &tmpFloatData[ 2 ], &tmpFloatData[ 3 ] ); ( ( float * ) entityDataField ) [ 0 ] = tmpFloatData[ 0 ]; ( ( float * ) entityDataField ) [ 1 ] = tmpFloatData[ 1 ]; ( ( float * ) entityDataField ) [ 2 ] = tmpFloatData[ 2 ]; ( ( float * ) entityDataField ) [ 3 ] = tmpFloatData[ 3 ]; break; case F_INT: * ( int * ) entityDataField = atoi( rawString ); break; case F_FLOAT: * ( float * ) entityDataField = atof( rawString ); break; case F_YAW: ( ( float * ) entityDataField ) [ PITCH ] = 0; ( ( float * ) entityDataField ) [ YAW ] = atof( rawString ); ( ( float * ) entityDataField ) [ ROLL ] = 0; break; case F_SOUNDINDEX: if ( strlen( rawString ) >= MAX_QPATH ) { G_Error( S_ERROR "Sound filename %s in field %s of %s exceeds MAX_QPATH\n", rawString, fieldDescriptor->name, etos( entity ) ); } * ( int * ) entityDataField = G_SoundIndex( rawString ); break; default: G_Printf( S_ERROR "unknown datatype %i for field %s\n", fieldDescriptor->type, fieldDescriptor->name ); break; } if ( fieldDescriptor->replacement && fieldDescriptor->versionState ) G_WarnAboutDeprecatedEntityField(entity, fieldDescriptor->replacement, key, fieldDescriptor->versionState ); }