Esempio n. 1
0
void G_ParseCampaigns(void)
{
	int      i;
	qboolean mapFound = qfalse;

	level.campaignCount   = 0;
	level.currentCampaign = -1;
	memset(&g_campaigns, 0, sizeof(g_campaignInfo_t) * MAX_CAMPAIGNS);

	if (g_gametype.integer != GT_WOLF_CAMPAIGN)
	{
		trap_Cvar_Set("g_oldCampaign", "");
		trap_Cvar_Set("g_currentCampaign", "");
		trap_Cvar_Set("g_currentCampaignMap", "0");
		return;
	}

	if (g_campaignFile.string[0])
	{
		if (G_LoadCampaignsFromFile(g_campaignFile.string))
		{
			mapFound = qtrue;
		}
	}

	if (!mapFound)
	{
		// get all campaigns from .campaign files
		int  dirlen;
		int  numdirs = trap_FS_GetFileList("scripts", ".campaign", bigTextBuffer, sizeof(bigTextBuffer));
		char filename[MAX_QPATH]; // was 128
		char *dirptr = bigTextBuffer;

		for (i = 0; i < numdirs; i++, dirptr += dirlen + 1)
		{
			// log a warning if server has more than MAX_CAMPAIGNS
			if (level.campaignCount >= MAX_CAMPAIGNS)
			{
				G_LogPrintf("WARNING G_ParseCampaigns: number of campaigns larger then MAX_CAMPAIGNS\n");
				break;
			}

			dirlen = strlen(dirptr);
			strcpy(filename, "scripts/");
			strcat(filename, dirptr);

			if (G_LoadCampaignsFromFile(filename))
			{
				mapFound = qtrue;
			}
		}
	}

	if (!mapFound)
	{
		// map isn't found in the current campaign, see if it's the first map in another campaign
		for (i = 0; i < level.campaignCount; i++)
		{
			if (!Q_stricmp(g_campaigns[i].mapnames[0], level.rawmapname))
			{
				// someone manually specified a /map command, and it's the first map in a campaign
				trap_Cvar_Set("g_currentCampaign", g_campaigns[i].shortname);
				trap_Cvar_Set("g_currentCampaignMap", "0");

				level.newCampaign = qtrue;

				g_campaigns[level.campaignCount].current = 0;
				level.currentCampaign                    = i;

				break;
			}
		}

		if (i == level.campaignCount)
		{
			char buf[MAX_STRING_CHARS];

			if (trap_Argc() < 1) // command not found, throw error
			{
				G_Error("Usage 'map <mapname>\n'");
			}

			trap_Argv(0, buf, sizeof(buf));

			if (!(*buf)) // command not found, throw error
			{
				G_Error("Usage 'map <mapname>\n'");
			}

			// no campaign found, fallback to GT_WOLF
			// and reload the map
			trap_Cvar_Set("g_gametype", "2");
			trap_SendConsoleCommand(EXEC_APPEND, va("%s %s\n", buf, level.rawmapname));
		}
	}
}
Esempio n. 2
0
/*QUAKED worldspawn (0 0 0) ? NO_GT_WOLF NO_GT_STOPWATCH NO_GT_CHECKPOINT NO_LMS

Every map should have exactly one worldspawn.
"music"     Music wav file
"gravity"   800 is default gravity
"message" Text to print during connection process
"ambient"  Ambient light value (must use '_color')
"_color"    Ambient light color (must be used with 'ambient')
"sun"        Shader to use for 'sun' image
*/
void SP_worldspawn(void)
{
	char           *s;

	G_SpawnString("classname", "", &s);
	if(Q_stricmp(s, "worldspawn"))
	{
		G_Error("SP_worldspawn: The first entity isn't 'worldspawn'");
	}

	// make some data visible to connecting client
	trap_SetConfigstring(CS_GAME_VERSION, GAME_VERSION);

	trap_SetConfigstring(CS_LEVEL_START_TIME, va("%i", level.startTime));

	G_SpawnString("music", "", &s);
	trap_SetConfigstring(CS_MUSIC, s);

	G_SpawnString("message", "", &s);
	trap_SetConfigstring(CS_MESSAGE, s);	// map specific message

	G_SpawnString("cclayers", "0", &s);
	if(atoi(s))
	{
		level.ccLayers = qtrue;
	}

	level.mapcoordsValid = qfalse;
	if(G_SpawnVector2D("mapcoordsmins", "-128 128", level.mapcoordsMins) &&	// top left
	   G_SpawnVector2D("mapcoordsmaxs", "128 -128", level.mapcoordsMaxs))
	{							// bottom right
		level.mapcoordsValid = qtrue;
	}

	BG_InitLocations(level.mapcoordsMins, level.mapcoordsMaxs);

	trap_SetConfigstring(CS_MOTD, g_motd.string);	// message of the day

	G_SpawnString("gravity", "800", &s);
	trap_Cvar_Set("g_gravity", s);

	G_SpawnString("spawnflags", "0", &s);
	g_entities[ENTITYNUM_WORLD].spawnflags = atoi(s);
	g_entities[ENTITYNUM_WORLD].r.worldflags = g_entities[ENTITYNUM_WORLD].spawnflags;

	g_entities[ENTITYNUM_WORLD].s.number = ENTITYNUM_WORLD;
	g_entities[ENTITYNUM_WORLD].classname = "worldspawn";

	// see if we want a warmup time
	trap_SetConfigstring(CS_WARMUP, "");
	if(g_restarted.integer)
	{
		trap_Cvar_Set("g_restarted", "0");
		level.warmupTime = 0;
	}

	if(g_gamestate.integer == GS_PLAYING)
	{
		G_initMatch();
	}
}
Esempio n. 3
0
static void EnumerateField(const save_field_t *pField, const byte *pbBase)
{
	void *pv = (void *)(pbBase + pField->iOffset);

	switch (pField->eFieldType)
	{
	case F_STRING:
		*(int *)pv = GetStringNum(*(char **)pv);
		break;

	case F_GENTITY:
		*(int *)pv = GetGEntityNum(*(gentity_t **)pv);
		break;

	case F_GROUP:
		*(int *)pv = GetGroupNumber(*(AIGroupInfo_t **)pv);
		break;

	case F_GCLIENT:
		*(int *)pv = GetGClientNum(*(gclient_t **)pv, (gentity_t *) pbBase);
		break;

	case F_ITEM:
		*(int *)pv = GetGItemNum(*(gitem_t **)pv);
		break;

	case F_VEHINFO:
		*(int *)pv = GetVehicleInfoNum(*(vehicleInfo_t **)pv);
		break;

	case F_BEHAVIORSET:
		{
			const char **p = (const char **) pv;
			for (int i=0; i<NUM_BSETS; i++)
			{
				pv = &p[i];	// since you can't ++ a void ptr
				*(int *)pv = GetStringNum(*(char **)pv);
			}
		}
		break;

/*MCG
	case F_BODYQUEUE:
		{
			gentity_t **p = (gentity_t **) pv;
			for (int i=0; i<BODY_QUEUE_SIZE; i++)
			{
				pv = &p[i];	// since you can't ++ a void ptr
				*(int *)pv = GetGEntityNum(*(gentity_t **)pv);
			}
		}
		break;
*/

	case F_ALERTEVENT:	// convert all gentity_t ptrs in an alertEvent array into indexes...
		{
			alertEvent_t* p = (alertEvent_t *) pv;

			for (int i=0; i<MAX_ALERT_EVENTS; i++)
			{
				p[i].owner = (gentity_t *) GetGEntityNum(p[i].owner);
			}
		}
		break;

	case F_AIGROUPS:	// convert to ptrs within this into indexes...
		{
			AIGroupInfo_t* p = (AIGroupInfo_t *) pv;

			for (int i=0; i<MAX_FRAME_GROUPS; i++)
			{
				p[i].enemy		= (gentity_t *) GetGEntityNum(p[i].enemy);
				p[i].commander	= (gentity_t *) GetGEntityNum(p[i].commander);
			}
		}
		break;

	case F_ANIMFILESETS:
		{
			animFileSet_t* p = (animFileSet_t *) pv;

			for (int i=0; i<MAX_ANIM_FILES; i++)
			{
				for ( int j=0; j<MAX_ANIM_EVENTS; j++ )
				{
					const char *ptAnimEventStringData = p[i].torsoAnimEvents[j].stringData;
					*(int *)(&p[i].torsoAnimEvents[j].stringData) = GetStringNum( ptAnimEventStringData );
					const char *plAnimEventStringData = p[i].legsAnimEvents[j].stringData;
					*(int *)(&p[i].legsAnimEvents[j].stringData) = GetStringNum( plAnimEventStringData );
				}
			}
		}
		break;

	case F_BOOLPTR:
		*(qboolean *)pv = !!(*(int *)pv);
		break;

	// These are pointers that are always recreated
	case F_NULL:
		*(void **)pv = NULL;
		break;

	case F_IGNORE:
		break;

	default:
		G_Error ("EnumerateField: unknown field type");
		break;
	}
}
Esempio n. 4
0
/*
===========
SelectNearestSpawnPoint

for teleporting you back after falling into the void
============
*/
gentity_t *SelectNearestSpawnPoint ( vec3_t Point, vec3_t origin, vec3_t angles ) {
	gentity_t	*spot;
	vec3_t	delta;
	float		dist, nearestDist;
	gentity_t	*nearestSpot;

	nearestDist = 999999;
	nearestSpot = NULL;
	spot = NULL;

	while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) {
		VectorSubtract( spot->s.origin, Point, delta );
		dist = VectorLength( delta );
		if ( dist < nearestDist ) {
			nearestDist = dist;
			nearestSpot = spot;
		}
	}
	while ((spot = G_Find (spot, FOFS(classname), "team_CTF_redplayer")) != NULL) {
		VectorSubtract( spot->s.origin, Point, delta );
		dist = VectorLength( delta );
		if ( dist < nearestDist ) {
			nearestDist = dist;
			nearestSpot = spot;
		}
	}
	while ((spot = G_Find (spot, FOFS(classname), "team_CTF_blueplayer")) != NULL) {
		VectorSubtract( spot->s.origin, Point, delta );
		dist = VectorLength( delta );
		if ( dist < nearestDist ) {
			nearestDist = dist;
			nearestSpot = spot;
		}
	}
	while ((spot = G_Find (spot, FOFS(classname), "team_CTF_redspawn")) != NULL) {
		VectorSubtract( spot->s.origin, Point, delta );
		dist = VectorLength( delta );
		if ( dist < nearestDist ) {
			nearestDist = dist;
			nearestSpot = spot;
		}
	}
	while ((spot = G_Find (spot, FOFS(classname), "team_CTF_bluespawn")) != NULL) {
		VectorSubtract( spot->s.origin, Point, delta );
		dist = VectorLength( delta );
		if ( dist < nearestDist ) {
			nearestDist = dist;
			nearestSpot = spot;
		}
	}

	// find a single player start spot
	if (!nearestSpot) {
		G_Error( "Couldn't find a spawn point" );
	}

	VectorCopy (nearestSpot->s.origin, origin);
	origin[2] += 9;
	VectorCopy (nearestSpot->s.angles, angles);

	return nearestSpot;
}
Esempio n. 5
0
void G_RunFrame( int levelTime ) {
	int			i;
	gentity_t	*ent;
	int			ents_inuse=0; // someone's gonna be pissed I put this here...
#if	AI_TIMERS
	AITime = 0;
	navTime = 0;
#endif//	AI_TIMERS

	level.framenum++;
	level.previousTime = level.time;
	level.time = levelTime;
	//msec = level.time - level.previousTime;

	NAV_CheckCalcPaths();
	//ResetTeamCounters();

	AI_UpdateGroups();

	if ( d_altRoutes->integer )
	{
		navigator.CheckAllFailedEdges();
	}
	navigator.ClearCheckedNodes();

	//remember last waypoint, clear current one
//	for ( i = 0, ent = &g_entities[0]; i < globals.num_entities ; i++, ent++)
	for ( i = 0; i < globals.num_entities ; i++)
	{
//		if ( !ent->inuse )
//			continue;

		if(!PInUse(i))
			continue;

		ent = &g_entities[i];

		if ( ent->waypoint != WAYPOINT_NONE
			&& ent->noWaypointTime < level.time )
		{
			ent->lastWaypoint = ent->waypoint;
			ent->waypoint = WAYPOINT_NONE;
		}
		if ( d_altRoutes->integer )
		{
			navigator.CheckFailedNodes( ent );
		}
	}

	//Look to clear out old events
	ClearPlayerAlertEvents();

	//Run the frame for all entities
//	for ( i = 0, ent = &g_entities[0]; i < globals.num_entities ; i++, ent++)
	for ( i = 0; i < globals.num_entities ; i++)
	{
//		if ( !ent->inuse )
//			continue;

		if(!PInUse(i))
			continue;
		ents_inuse++;
		ent = &g_entities[i];

		// clear events that are too old
		if ( level.time - ent->eventTime > EVENT_VALID_MSEC ) {
			if ( ent->s.event ) {
				ent->s.event = 0;	// &= EV_EVENT_BITS;
				if ( ent->client ) {
					ent->client->ps.externalEvent = 0;
				}
			}
			if ( ent->freeAfterEvent ) {
				// tempEntities or dropped items completely go away after their event
				G_FreeEntity( ent );
				continue;
			} else if ( ent->unlinkAfterEvent ) {
				// items that will respawn will hide themselves after their pickup event
				ent->unlinkAfterEvent = qfalse;
				gi.unlinkentity( ent );
			}
		}

		// temporary entities don't think
		if ( ent->freeAfterEvent )
			continue;

		G_CheckTasksCompleted(ent);

		G_Roff( ent );

		if( !ent->client )
		{
			if ( !(ent->svFlags & SVF_SELF_ANIMATING) )
			{//FIXME: make sure this is done only for models with frames?
				//Or just flag as animating?
				if ( ent->s.eFlags & EF_ANIM_ONCE )
				{
					ent->s.frame++;
				}
				else if ( !(ent->s.eFlags & EF_ANIM_ALLFAST) )
				{
					G_Animate( ent );
				}
			}
		}
		G_CheckSpecialPersistentEvents( ent );

		if ( ent->s.eType == ET_MISSILE )
		{
			G_RunMissile( ent );
			continue;
		}

		if ( ent->s.eType == ET_ITEM )
		{
			G_RunItem( ent );
			continue;
		}

		if ( ent->s.eType == ET_MOVER )
		{
			if ( ent->model && Q_stricmp( "models/test/mikeg/tie_fighter.md3", ent->model ) == 0 )
			{
				TieFighterThink( ent );
			}
			G_RunMover( ent );
			continue;
		}

		//The player
		if ( i == 0 )
		{
			// decay batteries if the goggles are active
			if ( cg.zoomMode == 1 && ent->client->ps.batteryCharge > 0 )
			{
				ent->client->ps.batteryCharge--;
			}
			else if ( cg.zoomMode == 3 && ent->client->ps.batteryCharge > 0 )
			{
				ent->client->ps.batteryCharge -= 2;

				if ( ent->client->ps.batteryCharge < 0 )
				{
					ent->client->ps.batteryCharge = 0;
				}
			}

			G_CheckEndLevelTimers( ent );
			//Recalculate the nearest waypoint for the coming NPC updates
			NAV_FindPlayerWaypoint();

			if( ent->taskManager && !stop_icarus )
			{
				ent->taskManager->Update();
			}
			//dead
			if ( ent->health <= 0 )
			{
				if ( ent->client->ps.groundEntityNum != ENTITYNUM_NONE )
				{//on the ground
					pitch_roll_for_slope( ent, NULL );
				}
			}

			continue;	// players are ucmd driven
		}

		G_RunThink( ent );	// be aware that ent may be free after returning from here, at least one func frees them
		ClearNPCGlobals();			//	but these 2 funcs are ok
		//UpdateTeamCounters( ent );	//	   to call anyway on a freed ent.
	}

	// perform final fixups on the player
	ent = &g_entities[0];
	if ( ent->inuse )
	{
		ClientEndFrame( ent );
	}
	if( g_numEntities->integer )
	{
		gi.Printf( S_COLOR_WHITE"Number of Entities in use : %d\n", ents_inuse );
	}
	//DEBUG STUFF
	NAV_ShowDebugInfo();
	NPC_ShowDebugInfo();

	G_DynamicMusicUpdate();

#if	AI_TIMERS
	AITime -= navTime;
	if ( AITime > 20 )
	{
		gi.Printf( S_COLOR_RED"ERROR: total AI time: %d\n", AITime );
	}
	else if ( AITime > 10 )
	{
		gi.Printf( S_COLOR_YELLOW"WARNING: total AI time: %d\n", AITime );
	}
	else if ( AITime > 2 )
	{
		gi.Printf( S_COLOR_GREEN"total AI time: %d\n", AITime );
	}
	if ( navTime > 20 )
	{
		gi.Printf( S_COLOR_RED"ERROR: total nav time: %d\n", navTime );
	}
	else if ( navTime > 10 )
	{
		gi.Printf( S_COLOR_YELLOW"WARNING: total nav time: %d\n", navTime );
	}
	else if ( navTime > 2 )
	{
		gi.Printf( S_COLOR_GREEN"total nav time: %d\n", navTime );
	}
#endif//	AI_TIMERS

#ifndef FINAL_BUILD
	if ( delayedShutDown != 0 && delayedShutDown < level.time )
	{
		G_Error( "Game Errors. Scroll up the console to read them." );
	}
#endif

#ifdef _DEBUG
	if(!(level.framenum&0xff))
	{
		ValidateInUseBits();
	}
#endif
}
/*
=================
G_ScriptAction_FaceAngles

  syntax: faceangles <pitch> <yaw> <roll> <duration/GOTOTIME> [ACCEL/DECCEL]

  The entity will face the given angles, taking <duration> to get there. If the
  GOTOTIME is given instead of a timed duration, the duration calculated from the
  last gotomarker command will be used instead.
=================
*/
qboolean G_ScriptAction_FaceAngles( gentity_t *ent, char *params ) {
    char *pString, *token;
    int duration, i;
    vec3_t diff;
    vec3_t angles;
    int trType = TR_LINEAR_STOP;

    if ( !params || !params[0] ) {
        G_Error( "G_Scripting: syntax: faceangles <pitch> <yaw> <roll> <duration/GOTOTIME>\n" );
    }

    if ( ent->scriptStatus.scriptStackChangeTime == level.time ) {
        pString = params;
        for ( i = 0; i < 3; i++ ) {
            token = COM_Parse( &pString );
            if ( !token || !token[0] ) {
                G_Error( "G_Scripting: syntax: faceangles <pitch> <yaw> <roll> <duration/GOTOTIME>\n" );
            }
            angles[i] = atoi( token );
        }

        token = COM_Parse( &pString );
        if ( !token || !token[0] ) {
            G_Error( "G_Scripting: faceangles requires a <pitch> <yaw> <roll> <duration/GOTOTIME>\n" );
        }
        if ( !Q_strcasecmp( token, "gototime" ) ) {
            duration = ent->s.pos.trDuration;
        } else {
            duration = atoi( token );
        }

        token = COM_Parse( &pString );
        if ( token && token[0] ) {
            if ( !Q_strcasecmp( token, "accel" ) ) {
                trType = TR_ACCELERATE;
            }
            if ( !Q_strcasecmp( token, "deccel" ) ) {
                trType = TR_DECCELERATE;
            }
        }

        for ( i = 0; i < 3; i++ ) {
            diff[i] = AngleDifference( angles[i], ent->s.angles[i] );
            while ( diff[i] > 180 )
                diff[i] -= 360;
            while ( diff[i] < -180 )
                diff[i] += 360;
        }

        VectorCopy( ent->s.angles, ent->s.apos.trBase );
        if ( duration ) {
            VectorScale( diff, 1000.0 / (float)duration, ent->s.apos.trDelta );
        } else {
            VectorClear( ent->s.apos.trDelta );
        }
        ent->s.apos.trDuration = duration;
        ent->s.apos.trTime = level.time;
        ent->s.apos.trType = TR_LINEAR_STOP;

        if ( trType != TR_LINEAR_STOP ) { // accel / deccel logic
            // calc the speed from duration and start/end delta
            for ( i = 0; i < 3; i++ ) {
                ent->s.apos.trDelta[i] = 2.0 * 1000.0 * diff[i] / (float)duration;
            }
            ent->s.apos.trType = trType;
        }

    } else if ( ent->s.apos.trTime + ent->s.apos.trDuration <= level.time ) {
        // finished turning
        BG_EvaluateTrajectory( &ent->s.apos, ent->s.apos.trTime + ent->s.apos.trDuration, ent->s.angles );
        VectorCopy( ent->s.angles, ent->s.apos.trBase );
        VectorCopy( ent->s.angles, ent->r.currentAngles );
        ent->s.apos.trTime = level.time;
        ent->s.apos.trDuration = 0;
        ent->s.apos.trType = TR_STATIONARY;
        VectorClear( ent->s.apos.trDelta );

        script_linkentity( ent );

        return qtrue;
    }

    BG_EvaluateTrajectory( &ent->s.apos, level.time, ent->r.currentAngles );
    script_linkentity( ent );

    return qfalse;
}
Esempio n. 7
0
//initization for misc_model_breakable
void misc_model_breakable_init( gentity_t *ent )
{
	int		type;

	type = MDL_OTHER;

	if (!ent->model) {
		G_Error("no model set on %s at (%.1f %.1f %.1f)\n", ent->classname, ent->s.origin[0],ent->s.origin[1],ent->s.origin[2]);
	}
	//Main model
	ent->s.modelindex = ent->sound2to1 = G_ModelIndex( ent->model );

	if ( ent->spawnflags & 1 )
	{//Blocks movement
		ent->r.contents = CONTENTS_SOLID|CONTENTS_OPAQUE|CONTENTS_BODY|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;//Was CONTENTS_SOLID, but only architecture should be this
		ent->s.solid = 2; //SOLID_BBOX
		ent->clipmask = MASK_PLAYERSOLID;
	}
	else if ( ent->health )
	{//Can only be shot
		ent->r.contents = CONTENTS_SHOTCLIP;
	}

	if (type == MDL_OTHER)
	{
		ent->use = misc_model_use;	
	}
	else if ( type == MDL_ARMOR_HEALTH )
	{
//		G_SoundIndex("sound/player/suithealth.wav");
		ent->use = health_use;
		if (!ent->count)
		{
			ent->count = 100;
		}
		ent->health = 60;
	}
	else if ( type == MDL_AMMO )
	{
//		G_SoundIndex("sound/player/suitenergy.wav");
		//RAFIXME: add this use function
		ent->use = ammo_use;
		if (!ent->count)
		{
			ent->count = 100;
		}
		ent->health = 60;
	}

	if ( ent->health ) 
	{
		G_SoundIndex("sound/weapons/explosions/cargoexplode.wav");
		ent->maxHealth = ent->health;
		G_ScaleNetHealth(ent);
		ent->takedamage = qtrue;
		ent->pain = misc_model_breakable_pain;

		//RACC - I think should be ->die
		ent->die  = misc_model_breakable_die;
		//ent->think  = misc_model_breakable_die;

	}

	ent->touch = misc_model_breakable_touch;
}
Esempio n. 8
0
void SP_target_speaker( gentity_t *ent ) {
	char	buffer[MAX_QPATH];
	char	*s;

	G_SpawnFloat( "wait", "0", &ent->wait );
	G_SpawnFloat( "random", "0", &ent->random );

	if ( !G_SpawnString( "noise", "NOSOUND", &s ) ) {
		G_Error( "target_speaker without a noise key at %s", vtos( ent->s.origin ) );
	}
	
	// force all client reletive sounds to be "activator" speakers that
	// play on the entity that activates it
	if ( s[0] == '*' ) {
		ent->spawnflags |= 8;
	}

	// Ridah, had to disable this so we can use sound scripts
	// don't worry, if the script isn't found, it'll default back to
	// .wav on the client-side
	//if (!strstr( s, ".wav" )) {
	//	Com_sprintf (buffer, sizeof(buffer), "%s.wav", s );
	//} else {
		Q_strncpyz( buffer, s, sizeof(buffer) );
	//}
	ent->noise_index = G_SoundIndex(buffer);

	// a repeating speaker can be done completely client side
	ent->s.eType = ET_SPEAKER;
	ent->s.eventParm = ent->noise_index;
	ent->s.frame = ent->wait * 10;
	ent->s.clientNum = ent->random * 10;


	// check for prestarted looping sound
	if ( ent->spawnflags & 1 ) {
		ent->s.loopSound = ent->noise_index;
	}

	ent->use = Use_Target_Speaker;

	// GLOBAL
	if (ent->spawnflags & (4|32)) {
		ent->r.svFlags |= SVF_BROADCAST;
	}

	VectorCopy( ent->s.origin, ent->s.pos.trBase );

	if (ent->spawnflags & 16)
	{
		ent->think = target_speaker_multiple;
		ent->nextthink = level.time + 50;
	}

	// NO_PVS
	if(ent->spawnflags & 32)
		ent->s.density = 1;
	else
		ent->s.density = 0;

	if(ent->radius)
		ent->s.dmgFlags = ent->radius;	// store radius in dmgflags
	else
		ent->s.dmgFlags = 0;


	// must link the entity so we get areas and clusters so
	// the server can determine who to send updates to
	trap_LinkEntity( ent );
}
Esempio n. 9
0
// Should be the only function that ever creates a fireteam
void G_RegisterFireteam(/*const char* name,*/ int entityNum)
{
	fireteamData_t *ft;
	gentity_t      *leader;
	int            count, ident;

	if (entityNum < 0 || entityNum >= MAX_CLIENTS)
	{
		G_Error("G_RegisterFireteam: invalid client\n");
	}

	leader = &g_entities[entityNum];
	if (!leader->client)
	{
		G_Error("G_RegisterFireteam: attempting to register a Fireteam to an entity with no client\n");
	}

	if (G_IsOnFireteam(entityNum, NULL))
	{
		G_ClientPrint(entityNum, "You are already on a fireteam, leave it first");
		return;
	}

	if ((ft = G_FindFreeFireteam()) == NULL)
	{
		G_ClientPrint(entityNum, "No free fireteams available");
		return;
	}

	if (leader->client->sess.sessionTeam != TEAM_AXIS && leader->client->sess.sessionTeam != TEAM_ALLIES)
	{
		G_ClientPrint(entityNum, "Only players on a team can create a fireteam");
		return;
	}

	count = G_CountTeamFireteams(leader->client->sess.sessionTeam);
	if (count >= MAX_FIRETEAMS / 2)
	{
		G_ClientPrint(entityNum, "Your team already has the maximum number of fireteams allowed");
		return;
	}

	ident = G_FindFreeFireteamIdent(leader->client->sess.sessionTeam) + 1;
	if (ident == 0)
	{
		G_ClientPrint(entityNum, "Um, something is broken, spoink Gordon");
		return;
	}

	// good to go now, i hope!
	ft->inuse = qtrue;
	memset(ft->joinOrder, -1, sizeof(level.fireTeams[0].joinOrder));
	ft->joinOrder[0] = leader - g_entities;
	ft->ident        = ident;

	if (g_autoFireteams.integer)
	{
		ft->priv = qfalse;

		trap_SendServerCommand(entityNum, "aft -1");
		leader->client->pers.autofireteamEndTime = level.time + 20500;
	}
	else
	{
		ft->priv = qfalse;
	}

#ifdef FEATURE_OMNIBOT
	Bot_Event_FireTeamCreated(entityNum, ft->ident);
	Bot_Event_JoinedFireTeam(leader - g_entities, leader);
#endif

	G_UpdateFireteamConfigString(ft);
}
Esempio n. 10
0
/*
==============
G_Script_ScriptParse

  Parses the script for the given entity
==============
*/
void G_Script_ScriptParse(gentity_t *ent)
{
	char             *pScript;
	char             *token;
	qboolean         wantName;
	qboolean         inScript;
	int              eventNum;
	g_script_event_t events[G_MAX_SCRIPT_STACK_ITEMS];
	int              numEventItems;
	g_script_event_t *curEvent;
	// DHM - Nerve :: Some of our multiplayer script commands have longer parameters
	//char		params[MAX_QPATH];
	char params[MAX_INFO_STRING];
	// dhm - end
	g_script_stack_action_t *action;
	int                     i;
	int                     bracketLevel;
	qboolean                buildScript;

	if (!ent->scriptName)
	{
		return;
	}
	if (!level.scriptEntity)
	{
		return;
	}

	buildScript = trap_Cvar_VariableIntegerValue("com_buildScript");

	pScript  = level.scriptEntity;
	wantName = qtrue;
	inScript = qfalse;
	COM_BeginParseSession("G_Script_ScriptParse");
	bracketLevel  = 0;
	numEventItems = 0;

	memset(events, 0, sizeof(events));

	while (1)
	{
		token = COM_Parse(&pScript);

		if (!token[0])
		{
			if (!wantName)
			{
				G_Error("G_Script_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine());
			}
			break;
		}

		// end of script
		if (token[0] == '}')
		{
			if (inScript)
			{
				break;
			}
			if (wantName)
			{
				G_Error("G_Script_ScriptParse(), Error (line %d): '}' found, but not expected.\n", COM_GetCurrentParseLine());
			}
			wantName = qtrue;
		}
		else if (token[0] == '{')
		{
			if (wantName)
			{
				G_Error("G_Script_ScriptParse(), Error (line %d): '{' found, NAME expected.\n", COM_GetCurrentParseLine());
			}
		}
		else if (wantName)
		{
			if (!Q_stricmp(token, "bot"))
			{
				// a bot, skip this whole entry
				SkipRestOfLine(&pScript);
				// skip this section
				SkipBracedSection(&pScript);
				//
				continue;
			}
			if (!Q_stricmp(token, "entity"))
			{
				// this is an entity, so go back to look for a name
				continue;
			}
			if (!Q_stricmp(ent->scriptName, token))
			{
				inScript      = qtrue;
				numEventItems = 0;
			}
			wantName = qfalse;
		}
		else if (inScript)
		{
			Q_strlwr(token);
			eventNum = G_Script_EventForString(token);
			if (eventNum < 0)
			{
				G_Error("G_Script_ScriptParse(), Error (line %d): unknown event: %s.\n", COM_GetCurrentParseLine(), token);
			}

			if (numEventItems >= G_MAX_SCRIPT_STACK_ITEMS)
			{
				G_Error("G_Script_ScriptParse(), Error (line %d): G_MAX_SCRIPT_STACK_ITEMS reached (%d)\n", COM_GetCurrentParseLine(), G_MAX_SCRIPT_STACK_ITEMS);
			}

			curEvent           = &events[numEventItems];
			curEvent->eventNum = eventNum;
			memset(params, 0, sizeof(params));

			// parse any event params before the start of this event's actions
			while ((token = COM_Parse(&pScript)) != NULL && (token[0] != '{'))
			{
				if (!token[0])
				{
					G_Error("G_Script_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine());
				}

				if (strlen(params))        // add a space between each param
				{
					Q_strcat(params, sizeof(params), " ");
				}
				Q_strcat(params, sizeof(params), token);
			}

			if (strlen(params))          // copy the params into the event
			{
				curEvent->params = G_Alloc(strlen(params) + 1);
				Q_strncpyz(curEvent->params, params, strlen(params) + 1);
			}

			// parse the actions for this event
			while ((token = COM_Parse(&pScript)) != NULL && (token[0] != '}'))
			{
				if (!token[0])
				{
					G_Error("G_Script_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine());
				}

				action = G_Script_ActionForString(token);
				if (!action)
				{
					G_Error("G_Script_ScriptParse(), Error (line %d): unknown action: %s.\n", COM_GetCurrentParseLine(), token);
				}

				curEvent->stack.items[curEvent->stack.numItems].action = action;

				memset(params, 0, sizeof(params));

				// Ikkyo - Parse for {}'s if this is a set command
				if (!Q_stricmp(action->actionString, "set"))
				{
					token = COM_Parse(&pScript);
					if (token[0] != '{')
					{
						COM_ParseError("'{' expected, found: %s.\n", token);
					}

					while ((token = COM_Parse(&pScript)) && (token[0] != '}'))
					{
						if (strlen(params))       // add a space between each param
						{
							Q_strcat(params, sizeof(params), " ");
						}

						if (strrchr(token, ' '))      // need to wrap this param in quotes since it has more than one word
						{
							Q_strcat(params, sizeof(params), "\"");
						}

						Q_strcat(params, sizeof(params), token);

						if (strrchr(token, ' '))      // need to wrap this param in quotes since it has mor
						{
							Q_strcat(params, sizeof(params), "\"");
						}
					}
				}
				else
				// hackly precaching of custom characters
				if (!Q_stricmp(token, "spawnbot"))
				{
					// this is fairly indepth, so I'll move it to a separate function for readability
					G_Script_ParseSpawnbot(&pScript, params, MAX_INFO_STRING);
				}
				else
				{
					token = COM_ParseExt(&pScript, qfalse);
					for (i = 0; token[0]; i++)
					{
						if (strlen(params))       // add a space between each param
						{
							Q_strcat(params, sizeof(params), " ");
						}

						if (i == 0)
						{
							// Special case: playsound's need to be cached on startup to prevent in-game pauses
							if (!Q_stricmp(action->actionString, "playsound"))
							{
								G_SoundIndex(token);
							}
							else if (!Q_stricmp(action->actionString, "changemodel"))
							{
								G_ModelIndex(token);
							}
							else if (buildScript && (
							             !Q_stricmp(action->actionString, "mu_start") ||
							             !Q_stricmp(action->actionString, "mu_play") ||
							             !Q_stricmp(action->actionString, "mu_queue") ||
							             !Q_stricmp(action->actionString, "startcam"))
							         )
							{
								if (strlen(token))       // we know there's a [0], but don't know if it's '0'
								{
									trap_SendServerCommand(-1, va("addToBuild %s\n", token));
								}
							}
						}

						if (i == 0 || i == 1)
						{
							if (!Q_stricmp(action->actionString, "remapshader"))
							{
								G_ShaderIndex(token);
							}
						}

						if (strrchr(token, ' '))      // need to wrap this param in quotes since it has more than one word
						{
							Q_strcat(params, sizeof(params), "\"");
						}

						Q_strcat(params, sizeof(params), token);

						if (strrchr(token, ' '))      // need to wrap this param in quotes since it has more than one word
						{
							Q_strcat(params, sizeof(params), "\"");
						}

						token = COM_ParseExt(&pScript, qfalse);
					}
				}

				if (strlen(params))       // copy the params into the event
				{
					curEvent->stack.items[curEvent->stack.numItems].params = G_Alloc(strlen(params) + 1);
					Q_strncpyz(curEvent->stack.items[curEvent->stack.numItems].params, params, strlen(params) + 1);
				}

				curEvent->stack.numItems++;

				if (curEvent->stack.numItems >= G_MAX_SCRIPT_STACK_ITEMS)
				{
					G_Error("G_Script_ScriptParse(): script exceeded G_MAX_SCRIPT_STACK_ITEMS (%d), line %d\n", G_MAX_SCRIPT_STACK_ITEMS, COM_GetCurrentParseLine());
				}
			}

			numEventItems++;
		}
		else     // skip this character completely
		{   // TTimo gcc: suggest parentheses around assignment used as truth value
			while ((token = COM_Parse(&pScript)) != NULL)
			{
				if (!token[0])
				{
					G_Error("G_Script_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine());
				}
				else if (token[0] == '{')
				{
					bracketLevel++;
				}
				else if (token[0] == '}')
				{
					if (!--bracketLevel)
					{
						break;
					}
				}
			}
		}
	}

	// alloc and copy the events into the gentity_t for this cast
	if (numEventItems > 0)
	{
		ent->scriptEvents = G_Alloc(sizeof(g_script_event_t) * numEventItems);
		memcpy(ent->scriptEvents, events, sizeof(g_script_event_t) * numEventItems);
		ent->numScriptEvents = numEventItems;
	}
}
Esempio n. 11
0
/*QUAKED misc_model_breakable (1 0 0) (-16 -16 -16) (16 16 16) SOLID AUTOANIMATE DEADSOLID NO_DMODEL NO_SMOKE USE_MODEL USE_NOT_BREAK PLAYER_USE NO_EXPLOSION
SOLID - Movement is blocked by it, if not set, can still be broken by explosions and shots if it has health
AUTOANIMATE - Will cycle it's anim
DEADSOLID - Stay solid even when destroyed (in case damage model is rather large).
NO_DMODEL - Makes it NOT display a damage model when destroyed, even if one exists
USE_MODEL - When used, will toggle to it's usemodel (model name + "_u1.md3")... this obviously does nothing if USE_NOT_BREAK is not checked
USE_NOT_BREAK - Using it, doesn't make it break, still can be destroyed by damage
PLAYER_USE - Player can use it with the use button
NO_EXPLOSION - By default, will explode when it dies...this is your override.

"model"		arbitrary .md3 file to display
"health"	how much health to have - default is zero (not breakable)  If you don't set the SOLID flag, but give it health, it can be shot but will not block NPCs or players from moving
"targetname" when used, dies and displays damagemodel, if any (if not, removes itself)
"target" What to use when it dies
"target2" What to use when it's repaired
"target3" What to use when it's used while it's broken
"paintarget" target to fire when hit (but not destroyed)
"count"  the amount of armor/health/ammo given (default 50)
"gravity"	if set to 1, this will be affected by gravity
"radius"  Chunk code tries to pick a good volume of chunks, but you can alter this to scale the number of spawned chunks. (default 1)  (.5) is half as many chunks, (2) is twice as many chunks

Damage: default is none
"splashDamage" - damage to do (will make it explode on death)
"splashRadius" - radius for above damage

"team" - This cannot take damage from members of this team:
	"player"
	"neutral"
	"enemy"

"material" - default is "8 - MAT_NONE" - choose from this list:
0 = MAT_METAL		(grey metal)
1 = MAT_GLASS		
2 = MAT_ELECTRICAL	(sparks only)
3 = MAT_ELEC_METAL	(METAL chunks and sparks)
4 =	MAT_DRK_STONE	(brown stone chunks)
5 =	MAT_LT_STONE	(tan stone chunks)
6 =	MAT_GLASS_METAL (glass and METAL chunks)
7 = MAT_METAL2		(blue/grey metal)
8 = MAT_NONE		(no chunks-DEFAULT)
9 = MAT_GREY_STONE	(grey colored stone)
10 = MAT_METAL3		(METAL and METAL2 chunk combo)
11 = MAT_CRATE1		(yellow multi-colored crate chunks)
12 = MAT_GRATE1		(grate chunks--looks horrible right now)
13 = MAT_ROPE		(for yavin_trial, no chunks, just wispy bits )
14 = MAT_CRATE2		(red multi-colored crate chunks)
15 = MAT_WHITE_METAL (white angular chunks for Stu, NS_hideout )
FIXME/TODO: 
set size better?
multiple damage models?
custom explosion effect/sound?
*/
void SP_misc_model_breakable( gentity_t *ent ) 
{
	char	damageModel[MAX_QPATH];
	char	chunkModel[MAX_QPATH];
	char	useModel[MAX_QPATH];
	int		len;
	
	// Chris F. requested default for misc_model_breakable to be NONE...so don't arbitrarily change this.
	G_SpawnInt( "material", "8", (int*)&ent->material );
	G_SpawnFloat( "radius", "1", &ent->radius ); // used to scale chunk code if desired by a designer
	CacheChunkEffects( ent->material );

	misc_model_breakable_init( ent );

	len = strlen( ent->model ) - 4;
	strncpy( damageModel, ent->model, len );
	damageModel[len] = 0;	//chop extension
	strncpy( chunkModel, damageModel, sizeof(chunkModel));
	strncpy( useModel, damageModel, sizeof(useModel));
	
	if (ent->takedamage) {
		//Dead/damaged model
		if( !(ent->spawnflags & 8) ) {	//no dmodel
			strcat( damageModel, "_d1.md3" );
			ent->s.modelindex2 = G_ModelIndex( damageModel );
		}
		
		//Chunk model
		strcat( chunkModel, "_c1.md3" );
		ent->s.modelindex3 = G_ModelIndex( chunkModel );
	}

	//Use model
	if( ent->spawnflags & 32 ) {	//has umodel
		strcat( useModel, "_u1.md3" );
		ent->sound1to2 = G_ModelIndex( useModel );
	}
	if ( !ent->mins[0] && !ent->mins[1] && !ent->mins[2] )
	{
		VectorSet (ent->mins, -16, -16, -16);
	}
	if ( !ent->maxs[0] && !ent->maxs[1] && !ent->maxs[2] )
	{
		VectorSet (ent->maxs, 16, 16, 16);
	}

	if ( ent->spawnflags & 2 )
	{
		ent->s.eFlags |= EF_ANIM_ALLFAST;
	}

	G_SetOrigin( ent, ent->s.origin );
	G_SetAngles( ent, ent->s.angles );
	gi.linkentity (ent);

	if ( ent->spawnflags & 128 )
	{//Can be used by the player's BUTTON_USE
		ent->svFlags |= SVF_PLAYER_USABLE;
	}

	if ( ent->team && ent->team[0] )
	{
		ent->noDamageTeam = TranslateTeamName( ent->team );
		if ( ent->noDamageTeam == TEAM_FREE )
		{
			G_Error("team name %s not recognized\n", ent->team);
		}
	}
	
	ent->team = NULL;

	//HACK
	if ( ent->model && Q_stricmp( "models/map_objects/ships/tie_fighter.md3", ent->model ) == 0 )
	{//run a think
		G_EffectIndex( "fighter_explosion2" );
		G_SoundIndex( "sound/weapons/tie_fighter/tiepass1.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tiepass2.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tiepass3.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tiepass4.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tiepass5.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tie_fire.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tie_fire2.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tie_fire3.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/TIEexplode.wav" );
		ent->e_ThinkFunc = thinkF_TieFighterThink;
		ent->nextthink = level.time + FRAMETIME;
	}
	float grav = 0;
	G_SpawnFloat( "gravity", "0", &grav );
	if ( grav )
	{//affected by gravity
		G_SetAngles( ent, ent->s.angles );
		G_SetOrigin( ent, ent->currentOrigin );
		misc_model_breakable_gravity_init( ent, qtrue );
	}
}
Esempio n. 12
0
/*QUAKED script_mover (0.5 0.25 1.0) ? TRIGGERSPAWN SOLID EXPLOSIVEDAMAGEONLY RESURECTABLE COMPASS ALLIED AXIS MOUNTED_GUN
Scripted brush entity. A simplified means of moving brushes around based on events.

"modelscale" - Scale multiplier (defaults to 1, and scales uniformly)
"modelscale_vec" - Set scale per-axis.  Overrides "modelscale", so if you have both the "modelscale" is ignored
"model2" optional md3 to draw over the solid clip brush
"scriptname" name used for scripting purposes (like aiName in AI scripting)
"health" optionally make this entity damagable
"description" used with health, if the entity is damagable, it draws a healthbar with this description above it.
*/
void SP_script_mover(gentity_t *ent)
{

	float  scale[3] = { 1, 1, 1 };
	vec3_t scalevec;
	char   tagname[MAX_QPATH];
	char   *modelname;
	char   *tagent;
	char   cs[MAX_INFO_STRING];
	char   *s;

	if (!ent->model)
	{
		G_Error("script_mover must have a \"model\"\n");
	}
	if (!ent->scriptName)
	{
		G_Error("script_mover must have a \"scriptname\"\n");
	}

	ent->blocked = script_mover_blocked;

	// first position at start
	VectorCopy(ent->s.origin, ent->pos1);

//	VectorCopy( ent->r.currentOrigin, ent->pos1 );
	VectorCopy(ent->pos1, ent->pos2);   // don't go anywhere just yet

	trap_SetBrushModel(ent, ent->model);

	InitMover(ent);
	ent->reached        = NULL;
	ent->s.animMovetype = 0;

	ent->s.density = 0;

	if (ent->spawnflags & 256)
	{
		ent->s.density |= 2;
	}

	if (ent->spawnflags & 8)
	{
		ent->use = script_mover_use;
	}

	if (ent->spawnflags & 16)
	{
		ent->s.time2 = 1;
	}
	else
	{
		ent->s.time2 = 0;
	}

	if (ent->spawnflags & 32)
	{
		ent->s.teamNum = TEAM_ALLIES;
	}
	else if (ent->spawnflags & 64)
	{
		ent->s.teamNum = TEAM_AXIS;
	}
	else
	{
		ent->s.teamNum = TEAM_FREE;
	}

	if (ent->spawnflags & 1)
	{
		ent->use = script_mover_use;
		trap_UnlinkEntity(ent);   // make sure it's not visible
		return;
	}

	G_SetAngle(ent, ent->s.angles);

	G_SpawnInt("health", "0", &ent->health);
	if (ent->health)
	{
		ent->takedamage = qtrue;
		ent->count      = ent->health;

		// client needs to know about it as well
		ent->s.effect1Time  = ent->count;
		ent->s.dl_intensity = 255;

		if (G_SpawnString("description", "", &s))
		{
			trap_GetConfigstring(CS_SCRIPT_MOVER_NAMES, cs, sizeof(cs));
			Info_SetValueForKey(cs, va("%i", ent - g_entities), s);
			trap_SetConfigstring(CS_SCRIPT_MOVER_NAMES, cs);
		}
	}
	else
	{
		ent->count = 0;
	}

	ent->die = script_mover_die;

	// look for general scaling
	if (G_SpawnFloat("modelscale", "1", &scale[0]))
	{
		scale[2] = scale[1] = scale[0];
	}

	if (G_SpawnString("model2", "", &modelname))
	{
		COM_StripExtension(modelname, tagname);
		Q_strcat(tagname, MAX_QPATH, ".tag");

		ent->tagNumber = trap_LoadTag(tagname);

/*		if( !(ent->tagNumber = trap_LoadTag( tagname )) ) {
            Com_Error( ERR_DROP, "Failed to load Tag File (%s)\n", tagname );
        }*/
	}

	// look for axis specific scaling
	if (G_SpawnVector("modelscale_vec", "1 1 1", &scalevec[0]))
	{
		VectorCopy(scalevec, scale);
	}

	if (scale[0] != 1 || scale[1] != 1 || scale[2] != 1)
	{
		ent->s.density |= 1;
		// scale is stored in 'angles2'
		VectorCopy(scale, ent->s.angles2);
	}

	if (ent->spawnflags & 128)
	{
		ent->s.density |= 4;
		ent->waterlevel = 0;

		if (G_SpawnString("gun", "", &modelname))
		{
			if (!Q_stricmp(modelname, "browning"))
			{
				ent->s.density |= 8;
			}
		}

		G_SpawnString("tagent", "", &tagent);
		Q_strncpyz(ent->tagBuffer, tagent, 16);
		ent->s.powerups = -1;
	}

	ent->think     = script_mover_spawn;
	ent->nextthink = level.time + FRAMETIME;
}
Esempio n. 13
0
void *G_Alloc( int size )
{
	// Find a free block and allocate.
	// Does two passes, attempts to fill same-sized free slot first.

	struct freememnode *fmn, *prev, *next, *smallest;

	int                allocsize, smallestsize;
	char               *endptr;
	int                *ptr;

	allocsize = ( size + sizeof( int ) + ROUNDBITS ) & ~ROUNDBITS;  // Round to 32-byte boundary
	ptr = NULL;

	smallest = NULL;
	smallestsize = POOLSIZE + 1; // Guaranteed not to miss any slots :)

	for ( fmn = freehead; fmn; fmn = fmn->next )
	{
		if ( fmn->cookie != FREEMEMCOOKIE )
		{
			G_Error( "G_Alloc: Memory corruption detected!\n" );
		}

		if ( fmn->size >= allocsize )
		{
			// We've got a block
			if ( fmn->size == allocsize )
			{
				// Same size, just remove

				prev = fmn->prev;
				next = fmn->next;

				if ( prev )
				{
					prev->next = next; // Point previous node to next
				}

				if ( next )
				{
					next->prev = prev; // Point next node to previous
				}

				if ( fmn == freehead )
				{
					freehead = next; // Set head pointer to next
				}

				ptr = ( int * ) fmn;
				break; // Stop the loop, this is fine
			}
			else
			{
				// Keep track of the smallest free slot
				if ( fmn->size < smallestsize )
				{
					smallest = fmn;
					smallestsize = fmn->size;
				}
			}
		}
	}

	if ( !ptr && smallest )
	{
		// We found a slot big enough
		smallest->size -= allocsize;
		endptr = ( char * ) smallest + smallest->size;
		ptr = ( int * ) endptr;
	}

	if ( ptr )
	{
		freemem -= allocsize;

		if ( g_debugAlloc.integer )
		{
			G_Printf( "G_Alloc of %i bytes (%i left)\n", allocsize, freemem );
		}

		memset( ptr, 0, allocsize );
		*ptr++ = allocsize; // Store a copy of size for deallocation
		return ( ( void * ) ptr );
	}

	G_Error( "G_Alloc: failed on allocation of %i bytes\n", size );
	return ( NULL );
}
Esempio n. 14
0
/*
===========
G_SelectRandomFurthestSpawnPoint

Chooses a player start, deathmatch start, etc
============
*/
gentity_t *G_SelectRandomFurthestSpawnPoint( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
{
	gentity_t *spot = NULL;
	vec3_t    delta;
	float     dist;
	float     list_dist[ 64 ];
	gentity_t *list_spot[ 64 ];
	int       numSpots, rnd, i, j;

	numSpots = 0;

	while ( ( spot = G_IterateEntitiesOfClass( spot, S_POS_PLAYER_SPAWN ) ) != NULL )
	{
		if ( SpotWouldTelefrag( spot ) )
		{
			continue;
		}

		VectorSubtract( spot->s.origin, avoidPoint, delta );
		dist = VectorLength( delta );

		for ( i = 0; i < numSpots; i++ )
		{
			if ( dist > list_dist[ i ] )
			{
				if ( numSpots >= 64 )
				{
					numSpots = 64 - 1;
				}

				for ( j = numSpots; j > i; j-- )
				{
					list_dist[ j ] = list_dist[ j - 1 ];
					list_spot[ j ] = list_spot[ j - 1 ];
				}

				list_dist[ i ] = dist;
				list_spot[ i ] = spot;
				numSpots++;

				if ( numSpots > 64 )
				{
					numSpots = 64;
				}

				break;
			}
		}

		if ( i >= numSpots && numSpots < 64 )
		{
			list_dist[ numSpots ] = dist;
			list_spot[ numSpots ] = spot;
			numSpots++;
		}
	}

	if ( !numSpots )
	{
		spot = G_IterateEntitiesOfClass( NULL, S_POS_PLAYER_SPAWN );

		if ( !spot )
		{
			G_Error( "Couldn't find a spawn point" );
		}

		VectorCopy( spot->s.origin, origin );
		origin[ 2 ] += 9;
		VectorCopy( spot->s.angles, angles );
		return spot;
	}

	// select a random spot from the spawn points furthest away
	rnd = random() * ( numSpots / 2 );

	VectorCopy( list_spot[ rnd ]->s.origin, origin );
	origin[ 2 ] += 9;
	VectorCopy( list_spot[ rnd ]->s.angles, angles );

	return list_spot[ rnd ];
}
/*
===============
G_ScriptAction_GotoMarker

  syntax: gotomarker <targetname> <speed> [accel/deccel] [turntotarget] [wait]

  NOTE: speed may be modified to round the duration to the next 50ms for smooth
  transitions
===============
*/
qboolean G_ScriptAction_GotoMarker( gentity_t *ent, char *params ) {
    char    *pString, *token;
    gentity_t *target;
    vec3_t vec;
    float speed, dist;
    qboolean wait = qfalse, turntotarget = qfalse;
    int trType;
    int duration, i;
    vec3_t diff;
    vec3_t angles;

    if ( params && ( ent->scriptStatus.scriptFlags & SCFL_GOING_TO_MARKER ) ) {
        // we can't process a new movement until the last one has finished
        return qfalse;
    }

    if ( !params || ent->scriptStatus.scriptStackChangeTime < level.time ) {          // we are waiting for it to reach destination
        if ( ent->s.pos.trTime + ent->s.pos.trDuration <= level.time ) {  // we made it
            ent->scriptStatus.scriptFlags &= ~SCFL_GOING_TO_MARKER;

            // set the angles at the destination
            BG_EvaluateTrajectory( &ent->s.apos, ent->s.apos.trTime + ent->s.apos.trDuration, ent->s.angles );
            VectorCopy( ent->s.angles, ent->s.apos.trBase );
            VectorCopy( ent->s.angles, ent->r.currentAngles );
            ent->s.apos.trTime = level.time;
            ent->s.apos.trDuration = 0;
            ent->s.apos.trType = TR_STATIONARY;
            VectorClear( ent->s.apos.trDelta );

            // stop moving
            BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->s.origin );
            VectorCopy( ent->s.origin, ent->s.pos.trBase );
            VectorCopy( ent->s.origin, ent->r.currentOrigin );
            ent->s.pos.trTime = level.time;
            ent->s.pos.trDuration = 0;
            ent->s.pos.trType = TR_STATIONARY;
            VectorClear( ent->s.pos.trDelta );

            script_linkentity( ent );

            return qtrue;
        }
    } else {    // we have just started this command

        pString = params;
        token = COM_ParseExt( &pString, qfalse );
        if ( !token[0] ) {
            G_Error( "G_Scripting: gotomarker must have an targetname\n" );
        }

        // find the entity with the given "targetname"
        target = G_Find( NULL, FOFS( targetname ), token );

        if ( !target ) {
            G_Error( "G_Scripting: can't find entity with \"targetname\" = \"%s\"\n", token );
        }

        VectorSubtract( target->r.currentOrigin, ent->r.currentOrigin, vec );

        token = COM_ParseExt( &pString, qfalse );
        if ( !token[0] ) {
            G_Error( "G_Scripting: gotomarker must have a speed\n" );
        }

        speed = atof( token );
        trType = TR_LINEAR_STOP;

        while ( token[0] ) {
            token = COM_ParseExt( &pString, qfalse );
            if ( token[0] ) {
                if ( !Q_stricmp( token, "accel" ) ) {
                    trType = TR_ACCELERATE;
                } else if ( !Q_stricmp( token, "deccel" ) )      {
                    trType = TR_DECCELERATE;
                } else if ( !Q_stricmp( token, "wait" ) )      {
                    wait = qtrue;
                } else if ( !Q_stricmp( token, "turntotarget" ) )      {
                    turntotarget = qtrue;
                }
            }
        }

        // start the movement
        if ( ent->s.eType == ET_MOVER ) {

            VectorCopy( vec, ent->movedir );
            VectorCopy( ent->r.currentOrigin, ent->pos1 );
            VectorCopy( target->r.currentOrigin, ent->pos2 );
            ent->speed = speed;
            dist = VectorDistance( ent->pos1, ent->pos2 );
            // setup the movement with the new parameters
            InitMover( ent );

            // start the movement

            SetMoverState( ent, MOVER_1TO2, level.time );
            if ( trType != TR_LINEAR_STOP ) { // allow for acceleration/decceleration
                ent->s.pos.trDuration = 1000.0 * dist / ( speed / 2.0 );
                ent->s.pos.trType = trType;
            }
            ent->reached = NULL;

            if ( turntotarget ) {
                duration = ent->s.pos.trDuration;
                VectorCopy( target->s.angles, angles );

                for ( i = 0; i < 3; i++ ) {
                    diff[i] = AngleDifference( angles[i], ent->s.angles[i] );
                    while ( diff[i] > 180 )
                        diff[i] -= 360;
                    while ( diff[i] < -180 )
                        diff[i] += 360;
                }
                VectorCopy( ent->s.angles, ent->s.apos.trBase );
                if ( duration ) {
                    VectorScale( diff, 1000.0 / (float)duration, ent->s.apos.trDelta );
                } else {
                    VectorClear( ent->s.apos.trDelta );
                }
                ent->s.apos.trDuration = duration;
                ent->s.apos.trTime = level.time;
                ent->s.apos.trType = TR_LINEAR_STOP;
                if ( trType != TR_LINEAR_STOP ) { // allow for acceleration/decceleration
                    ent->s.pos.trDuration = 1000.0 * dist / ( speed / 2.0 );
                    ent->s.pos.trType = trType;
                }
            }

        } else {
            // calculate the trajectory
            ent->s.pos.trType = TR_LINEAR_STOP;
            ent->s.pos.trTime = level.time;
            VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
            dist = VectorNormalize( vec );
            VectorScale( vec, speed, ent->s.pos.trDelta );
            ent->s.pos.trDuration = 1000 * ( dist / speed );

            if ( turntotarget ) {
                duration = ent->s.pos.trDuration;
                VectorCopy( target->s.angles, angles );

                for ( i = 0; i < 3; i++ ) {
                    diff[i] = AngleDifference( angles[i], ent->s.angles[i] );
                    while ( diff[i] > 180 )
                        diff[i] -= 360;
                    while ( diff[i] < -180 )
                        diff[i] += 360;
                }
                VectorCopy( ent->s.angles, ent->s.apos.trBase );
                if ( duration ) {
                    VectorScale( diff, 1000.0 / (float)duration, ent->s.apos.trDelta );
                } else {
                    VectorClear( ent->s.apos.trDelta );
                }
                ent->s.apos.trDuration = duration;
                ent->s.apos.trTime = level.time;
                ent->s.apos.trType = TR_LINEAR_STOP;
            }

        }

        if ( !wait ) {
            // round the duration to the next 50ms
            if ( ent->s.pos.trDuration % 50 ) {
                float frac;

                frac = (float)( ( ( ent->s.pos.trDuration / 50 ) * 50 + 50 ) - ent->s.pos.trDuration ) / (float)( ent->s.pos.trDuration );
                if ( frac < 1 ) {
                    VectorScale( ent->s.pos.trDelta, 1.0 / ( 1.0 + frac ), ent->s.pos.trDelta );
                    ent->s.pos.trDuration = ( ent->s.pos.trDuration / 50 ) * 50 + 50;
                }
            }

            // set the goto flag, so we can keep processing the move until we reach the destination
            ent->scriptStatus.scriptFlags |= SCFL_GOING_TO_MARKER;
            return qtrue;   // continue to next command
        }

    }

    BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->r.currentOrigin );
    BG_EvaluateTrajectory( &ent->s.apos, level.time, ent->r.currentAngles );
    script_linkentity( ent );

    return qfalse;
}
Esempio n. 16
0
// The only way a client should be removed from a fireteam
void G_RemoveClientFromFireteams(int entityNum, qboolean update, qboolean print)
{
	fireteamData_t *ft;
	int            i;

	if ((entityNum < 0 || entityNum >= MAX_CLIENTS) || !g_entities[entityNum].client)
	{
		G_Error("G_RemoveClientFromFireteams: invalid client\n");
	}

	if (G_IsOnFireteam(entityNum, &ft))
	{
		int j, firstHuman;

		for (i = 0; i < MAX_FIRETEAM_MEMBERS && i < g_maxclients.integer; ++i)
		{
			if (ft->joinOrder[i] == entityNum)
			{
				if (i == 0)
				{
					if (ft->joinOrder[1] == -1)
					{
						ft->inuse = qfalse;
						ft->ident = -1;
					}
					else
					{
						// core: only bots left in the fireteam? we disband the fireteam..
						if (G_OnlyBotsInFireteam(ft, entityNum, &firstHuman))
						{
							// empty the fireteam
							for (j = 0; j < g_maxclients.integer - 1; j++)
							{
#ifdef FEATURE_OMNIBOT
								Bot_Event_LeftFireTeam(ft->joinOrder[j]);
#endif
								ft->joinOrder[j] = -1;
							}
							ft->inuse = qfalse;
							ft->ident = -1;
							G_UpdateFireteamConfigString(ft);
							return;
						}
						else
						{
							// core: a bot as FT-leader? we try to pick a human..
							if (g_entities[(int)(ft->joinOrder[1])].r.svFlags & SVF_BOT)
							{
								if (firstHuman != -1)
								{
									// Swap first human with first bot..
									int tmp = ft->joinOrder[1];

									ft->joinOrder[1]          = ft->joinOrder[firstHuman];
									ft->joinOrder[firstHuman] = tmp;
								}
							}
							else
							{
								firstHuman = 1;
							}
							// Inform client of promotion to leader
							if (firstHuman != -1)
							{
								trap_SendServerCommand(ft->joinOrder[firstHuman], "cpm \"You are now the leader of your fireteam\"\n");
							}
						}
					}
				}
				for (j = i; j < g_maxclients.integer - 1; j++)
				{
					ft->joinOrder[j] = ft->joinOrder[j + 1];
				}
				ft->joinOrder[g_maxclients.integer - 1] = -1;

				break;
			}
		}
	}
	else
	{
		return;
	}

#ifdef FEATURE_OMNIBOT
	Bot_Event_LeftFireTeam(entityNum);
#endif

	if (print)
	{
		for (i = 0; i < MAX_CLIENTS; i++)
		{
			if (ft->joinOrder[i] == -1)
			{
				break;
			}

			trap_SendServerCommand(ft->joinOrder[i], va("cpm \"%s ^7has left the Fireteam\"\n", level.clients[entityNum].pers.netname));
		}
	}

	if (update)
	{
		G_UpdateFireteamConfigString(ft);
	}
}
/*
=================
G_ScriptAction_Accum

  syntax: accum <buffer_index> <command> <paramater>

  Commands:

	accum <n> inc <m>
	accum <n> abort_if_less_than <m>
	accum <n> abort_if_greater_than <m>
	accum <n> abort_if_not_equal <m>
	accum <n> abort_if_equal <m>
	accum <n> set <m>
	accum <n> random <m>
	accum <n> bitset <m>
	accum <n> bitreset <m>
	accum <n> abort_if_bitset <m>
	accum <n> abort_if_not_bitset <m>
=================
*/
qboolean G_ScriptAction_Accum( gentity_t *ent, char *params ) {
    char *pString, *token, lastToken[MAX_QPATH];
    int bufferIndex;

    pString = params;

    token = COM_ParseExt( &pString, qfalse );
    if ( !token[0] ) {
        G_Error( "G_Scripting: accum without a buffer index\n" );
    }

    bufferIndex = atoi( token );
    if ( bufferIndex >= G_MAX_SCRIPT_ACCUM_BUFFERS ) {
        G_Error( "G_Scripting: accum buffer is outside range (0 - %i)\n", G_MAX_SCRIPT_ACCUM_BUFFERS );
    }

    token = COM_ParseExt( &pString, qfalse );
    if ( !token[0] ) {
        G_Error( "G_Scripting: accum without a command\n" );
    }

    Q_strncpyz( lastToken, token, sizeof( lastToken ) );
    token = COM_ParseExt( &pString, qfalse );

    if ( !Q_stricmp( lastToken, "inc" ) ) {
        if ( !token[0] ) {
            G_Error( "Scripting: accum %s requires a parameter\n", lastToken );
        }
        ent->scriptAccumBuffer[bufferIndex] += atoi( token );
    } else if ( !Q_stricmp( lastToken, "abort_if_less_than" ) ) {
        if ( !token[0] ) {
            G_Error( "Scripting: accum %s requires a parameter\n", lastToken );
        }
        if ( ent->scriptAccumBuffer[bufferIndex] < atoi( token ) ) {
            // abort the current script
            ent->scriptStatus.scriptStackHead = ent->scriptEvents[ent->scriptStatus.scriptEventIndex].stack.numItems;
        }
    } else if ( !Q_stricmp( lastToken, "abort_if_greater_than" ) ) {
        if ( !token[0] ) {
            G_Error( "Scripting: accum %s requires a parameter\n", lastToken );
        }
        if ( ent->scriptAccumBuffer[bufferIndex] > atoi( token ) ) {
            // abort the current script
            ent->scriptStatus.scriptStackHead = ent->scriptEvents[ent->scriptStatus.scriptEventIndex].stack.numItems;
        }
    } else if ( !Q_stricmp( lastToken, "abort_if_not_equal" ) ) {
        if ( !token[0] ) {
            G_Error( "Scripting: accum %s requires a parameter\n", lastToken );
        }
        if ( ent->scriptAccumBuffer[bufferIndex] != atoi( token ) ) {
            // abort the current script
            ent->scriptStatus.scriptStackHead = ent->scriptEvents[ent->scriptStatus.scriptEventIndex].stack.numItems;
        }
    } else if ( !Q_stricmp( lastToken, "abort_if_equal" ) ) {
        if ( !token[0] ) {
            G_Error( "Scripting: accum %s requires a parameter\n", lastToken );
        }
        if ( ent->scriptAccumBuffer[bufferIndex] == atoi( token ) ) {
            // abort the current script
            ent->scriptStatus.scriptStackHead = ent->scriptEvents[ent->scriptStatus.scriptEventIndex].stack.numItems;
        }
    } else if ( !Q_stricmp( lastToken, "bitset" ) ) {
        if ( !token[0] ) {
            G_Error( "Scripting: accum %s requires a parameter\n", lastToken );
        }
        ent->scriptAccumBuffer[bufferIndex] |= ( 1 << atoi( token ) );
    } else if ( !Q_stricmp( lastToken, "bitreset" ) ) {
        if ( !token[0] ) {
            G_Error( "Scripting: accum %s requires a parameter\n", lastToken );
        }
        ent->scriptAccumBuffer[bufferIndex] &= ~( 1 << atoi( token ) );
    } else if ( !Q_stricmp( lastToken, "abort_if_bitset" ) ) {
        if ( !token[0] ) {
            G_Error( "Scripting: accum %s requires a parameter\n", lastToken );
        }
        if ( ent->scriptAccumBuffer[bufferIndex] & ( 1 << atoi( token ) ) ) {
            // abort the current script
            ent->scriptStatus.scriptStackHead = ent->scriptEvents[ent->scriptStatus.scriptEventIndex].stack.numItems;
        }
    } else if ( !Q_stricmp( lastToken, "abort_if_not_bitset" ) ) {
        if ( !token[0] ) {
            G_Error( "Scripting: accum %s requires a parameter\n", lastToken );
        }
        if ( !( ent->scriptAccumBuffer[bufferIndex] & ( 1 << atoi( token ) ) ) ) {
            // abort the current script
            ent->scriptStatus.scriptStackHead = ent->scriptEvents[ent->scriptStatus.scriptEventIndex].stack.numItems;
        }
    } else if ( !Q_stricmp( lastToken, "set" ) ) {
        if ( !token[0] ) {
            G_Error( "Scripting: accum %s requires a parameter\n", lastToken );
        }
        ent->scriptAccumBuffer[bufferIndex] = atoi( token );
    } else if ( !Q_stricmp( lastToken, "random" ) ) {
        if ( !token[0] ) {
            G_Error( "Scripting: accum %s requires a parameter\n", lastToken );
        }
        ent->scriptAccumBuffer[bufferIndex] = rand() % atoi( token );
    } else {
        G_Error( "Scripting: accum: \"%s\": unknown command\n", params );
    }

    return qtrue;
}
Esempio n. 18
0
/*
======================
CG_ParseAnimationFile

Read a configuration file containing animation coutns and rates
models/players/visor/animation.cfg, etc

======================
*/
qboolean G_ParseAnimationFile( const char *af_filename ) 
{
	const char		*text_p;
	int			len;
	int			i;
	const char		*token;
	float		fps;
	int			skip;
	char		text[40000];
	int			animNum;
	animation_t	*animations = level.knownAnimFileSets[level.numKnownAnimFileSets].animations;

	len = gi.RE_GetAnimationCFG(af_filename, NULL, 0);
	if (len <= 0)
	{
		return qfalse;
	}
	if ( len <= 0 ) 
	{
		return qfalse;
	}
	if ( len >= sizeof( text ) - 1 ) 
	{
		G_Error( "G_ParseAnimationFile: File %s too long\n (%d > %d)", af_filename, len, sizeof( text ) - 1);
		return qfalse;
	}
	len = gi.RE_GetAnimationCFG(af_filename, text, sizeof(text));

	// parse the text
	text_p = text;
	skip = 0;	// quiet the compiler warning

	//FIXME: have some way of playing anims backwards... negative numFrames?

	//initialize anim array so that from 0 to MAX_ANIMATIONS, set default values of 0 1 0 100
	for(i = 0; i < MAX_ANIMATIONS; i++)
	{
		animations[i].firstFrame = 0;
		animations[i].numFrames = 0;
		animations[i].loopFrames = -1;
		animations[i].frameLerp = 100;
		animations[i].initialLerp = 100;
	}

	// read information for each frame
	while(1) 
	{
		token = COM_Parse( &text_p );

		if ( !token || !token[0]) 
		{
			break;
		}

		animNum = GetIDForString(animTable, token);
		if(animNum == -1)
		{
//#ifndef FINAL_BUILD
#ifdef _DEBUG
			Com_Printf(S_COLOR_RED"WARNING: Unknown token %s in %s\n", token, af_filename);
#endif
			continue;
		}

		token = COM_Parse( &text_p );
		if ( !token ) 
		{
			break;
		}
		animations[animNum].firstFrame = atoi( token );

		token = COM_Parse( &text_p );
		if ( !token ) 
		{
			break;
		}
		animations[animNum].numFrames = atoi( token );

		token = COM_Parse( &text_p );
		if ( !token ) 
		{
			break;
		}
		animations[animNum].loopFrames = atoi( token );

		token = COM_Parse( &text_p );
		if ( !token ) 
		{
			break;
		}
		fps = atof( token );
		if ( fps == 0 ) 
		{
			fps = 1;//Don't allow divide by zero error
		}
		if ( fps < 0 )
		{//backwards
			animations[animNum].frameLerp = floor(1000.0f / fps);
		}
		else
		{
			animations[animNum].frameLerp = ceil(1000.0f / fps);
		}

		animations[animNum].initialLerp = ceil(1000.0f / fabs(fps));
	}

#ifdef CONVENIENT_ANIMATION_FILE_DEBUG_THING
	if (strstr(af_filename, "humanoid"))
	{
		SpewDebugStuffToFile(animations);
	}
#endif

	return qtrue;
}
Esempio n. 19
0
void teleport_touch()
{
	gedict_t       *t;
	vec3_t          org;

	if ( self->s.v.targetname )
	{
		if ( self->s.v.nextthink < g_globalvars.time )
		{
			return;	// not fired yet
		}
	}

	if ( ( int ) ( self->s.v.spawnflags ) & PLAYER_ONLY )
	{
		if ( strneq( other->s.v.classname, "player" ) )
			return;
	}
// only teleport living creatures
	if ( other->s.v.health <= 0 || other->s.v.solid != SOLID_SLIDEBOX )
		return;

// activator = other;
	SUB_UseTargets();

	//put a tfog where the player was
	spawn_tfog( other->s.v.origin );

	t = find( world, FOFS( s.v.targetname ), self->s.v.target );
	if ( !t )
		G_Error( "couldn't find target" );

// spawn a tfog flash in front of the destination
	makevectors( t->mangle );
	org[0] = t->s.v.origin[0] + 32 * g_globalvars.v_forward[0];
	org[1] = t->s.v.origin[1] + 32 * g_globalvars.v_forward[1];
	org[2] = t->s.v.origin[2] + 32 * g_globalvars.v_forward[2];

	spawn_tfog( org );
	spawn_tdeath( t->s.v.origin, other );

// move the player and lock him down for a little while
	if ( !other->s.v.health )
	{
		VectorCopy( t->s.v.origin, other->s.v.origin );
		other->s.v.velocity[0] =
		    ( g_globalvars.v_forward[0] * other->s.v.velocity[0] ) +
		    ( g_globalvars.v_forward[0] * other->s.v.velocity[1] );
		other->s.v.velocity[1] =
		    ( g_globalvars.v_forward[1] * other->s.v.velocity[0] ) +
		    ( g_globalvars.v_forward[1] * other->s.v.velocity[1] );
		other->s.v.velocity[2] =
		    ( g_globalvars.v_forward[2] * other->s.v.velocity[0] ) +
		    ( g_globalvars.v_forward[2] * other->s.v.velocity[1] );

		//other->s.v.velocity = (v_forward * other->s.v.velocity[0]) + (v_forward * other->s.v.velocity[1]);
		return;
	}

	setorigin( other, PASSVEC3( t->s.v.origin ) );
	VectorCopy( t->mangle, other->s.v.angles );
// other.angles = t.mangle;
	if ( streq( other->s.v.classname, "player" ) )
	{
		other->s.v.fixangle = 1;	// turn this way immediately
		other->s.v.teleport_time = g_globalvars.time + 0.7;
		if ( ( int ) other->s.v.flags & FL_ONGROUND )
			other->s.v.flags = other->s.v.flags - FL_ONGROUND;
		VectorScale( g_globalvars.v_forward, 300, other->s.v.velocity );
//  other->s.v.velocity = v_forward * 300;
	}
	other->s.v.flags -= ( int ) other->s.v.flags & FL_ONGROUND;
}
/*
===============
RespawnItem
===============
*/
void RespawnItem( gentity_t *ent ) {
	// select from teamed entities
	if (ent->team) {
		gentity_t	*master;
		int	count;
		int choice;

		if ( !ent->teammaster ) {
			G_Error( "RespawnItem: bad teammaster");
		}

		if( ent->spawnflags & 2)	// randomly select from the group
		{
			master = ent->teammaster;

			for (count = 0, ent = master; ent; ent = ent->teamchain, count++)
				;

			choice = rand() % count;

			for (count = 0, ent = master; count < choice; ent = ent->teamchain, count++)
				;
		}
		else	// loop through the group
		{
			if(ent->teamchain)
				ent = ent->teamchain;
			else
				ent = ent->teammaster;
		}
	}

	if(ent->team)
	{
		if ( !ent->teammaster )
			G_Error( "RespawnItem: bad teammaster");


	}

	ent->r.contents = CONTENTS_TRIGGER;
	ent->s.eFlags &= ~EF_NODRAW;
	ent->r.svFlags &= ~SVF_NOCLIENT;
	trap_LinkEntity (ent);

	if ( ent->item->giType == IT_POWERUP ) {
		// play powerup spawn sound to all clients
		gentity_t	*te;

		// if the powerup respawn sound should Not be global
		if (ent->speed) {
			te = G_TempEntity( ent->s.pos.trBase, EV_GENERAL_SOUND );
		}
		else {
			te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_SOUND );
		}
		te->s.eventParm = G_SoundIndex( "sounds/items/powerup_respawn" );
		te->r.svFlags |= SVF_BROADCAST;
	}

	// play the normal respawn sound only to nearby clients
	G_AddEvent( ent, EV_ITEM_RESPAWN, 0 );

	ent->nextthink = 0;
}
Esempio n. 21
0
/*QUAKED misc_model_breakable (1 0 0) (-16 -16 -16) (16 16 16) SOLID AUTOANIMATE DEADSOLID NO_DMODEL NO_SMOKE USE_MODEL USE_NOT_BREAK PLAYER_USE NO_EXPLOSION START_OFF
SOLID - Movement is blocked by it, if not set, can still be broken by explosions and shots if it has health
AUTOANIMATE - Will cycle it's anim
DEADSOLID - Stay solid even when destroyed (in case damage model is rather large).
NO_DMODEL - Makes it NOT display a damage model when destroyed, even if one exists
USE_MODEL - When used, will toggle to it's usemodel (model + "_u1.md3")... this obviously does nothing if USE_NOT_BREAK is not checked
USE_NOT_BREAK - Using it, doesn't make it break, still can be destroyed by damage
PLAYER_USE - Player can use it with the use button
NO_EXPLOSION - By default, will explode when it dies...this is your override.
START_OFF - Will start off and will not appear until used.

"model"		arbitrary .md3 file to display
"modelscale"	"x" uniform scale
"modelscale_vec" "x y z" scale model in each axis - height, width and length - bbox will scale with it
"health"	how much health to have - default is zero (not breakable)  If you don't set the SOLID flag, but give it health, it can be shot but will not block NPCs or players from moving
"targetname" when used, dies and displays damagemodel (model + "_d1.md3"), if any (if not, removes itself)
"target" What to use when it dies
"target2" What to use when it's repaired
"target3" What to use when it's used while it's broken
"paintarget" target to fire when hit (but not destroyed)
"count"  the amount of armor/health/ammo given (default 50)
"radius"  Chunk code tries to pick a good volume of chunks, but you can alter this to scale the number of spawned chunks. (default 1)  (.5) is half as many chunks, (2) is twice as many chunks
"NPC_targetname" - Only the NPC with this name can damage this
"forcevisible" - When you turn on force sight (any level), you can see these draw through the entire level...
"redCrosshair" - crosshair turns red when you look at this

"gravity"	if set to 1, this will be affected by gravity
"throwtarget" if set (along with gravity), this thing, when used, will throw itself at the entity whose targetname matches this string
"mass"		if gravity is on, this determines how much damage this thing does when it hits someone.  Default is the size of the object from one corner to the other, that works very well.  Only override if this is an object whose mass should be very high or low for it's size (density)

Damage: default is none
"splashDamage" - damage to do (will make it explode on death)
"splashRadius" - radius for above damage

"team" - This cannot take damage from members of this team:
	"player"
	"neutral"
	"enemy"

"material" - default is "8 - MAT_NONE" - choose from this list:
0 = MAT_METAL		(grey metal)
1 = MAT_GLASS		
2 = MAT_ELECTRICAL	(sparks only)
3 = MAT_ELEC_METAL	(METAL chunks and sparks)
4 =	MAT_DRK_STONE	(brown stone chunks)
5 =	MAT_LT_STONE	(tan stone chunks)
6 =	MAT_GLASS_METAL (glass and METAL chunks)
7 = MAT_METAL2		(blue/grey metal)
8 = MAT_NONE		(no chunks-DEFAULT)
9 = MAT_GREY_STONE	(grey colored stone)
10 = MAT_METAL3		(METAL and METAL2 chunk combo)
11 = MAT_CRATE1		(yellow multi-colored crate chunks)
12 = MAT_GRATE1		(grate chunks--looks horrible right now)
13 = MAT_ROPE		(for yavin_trial, no chunks, just wispy bits )
14 = MAT_CRATE2		(red multi-colored crate chunks)
15 = MAT_WHITE_METAL (white angular chunks for Stu, NS_hideout )
*/
void SP_misc_model_breakable( gentity_t *ent ) 
{
	char	damageModel[MAX_QPATH];
	char	chunkModel[MAX_QPATH];
	char	useModel[MAX_QPATH];
	int		len;
	qboolean bHasScale;

	float grav = 0;
	//[CoOp]
	int forceVisible = 0;
	//[/CoOp]
	int redCrosshair = 0;
	
	// Chris F. requested default for misc_model_breakable to be NONE...so don't arbitrarily change this.
	G_SpawnInt( "material", "8", (int*)&ent->material );
	G_SpawnFloat( "radius", "1", &ent->radius ); // used to scale chunk code if desired by a designer
	bHasScale = G_SpawnVector("modelscale_vec", "0 0 0", ent->modelScale);
	if (!bHasScale)
	{
		float temp;
		G_SpawnFloat( "modelscale", "0", &temp);
		if (temp != 0.0f)
		{
			ent->modelScale[ 0 ] = ent->modelScale[ 1 ] = ent->modelScale[ 2 ] = temp;
			bHasScale = qtrue;
		}
	}

	CacheChunkEffects( ent->material );
	misc_model_breakable_init( ent );

	len = strlen( ent->model ) - 4;
	assert(ent->model[len]=='.');//we're expecting ".md3"
	strncpy( damageModel, ent->model, sizeof(damageModel) );
	damageModel[len] = 0;	//chop extension
	strncpy( chunkModel, damageModel, sizeof(chunkModel));
	strncpy( useModel, damageModel, sizeof(useModel));
	
	if (ent->takedamage) {
		//Dead/damaged model
		if( !(ent->spawnflags & 8) ) {	//no dmodel
			strcat( damageModel, "_d1.md3" );
			ent->s.modelindex2 = G_ModelIndex( damageModel );
		}
		
		/* RAFIXME - add modelindex3
		//Chunk model
		strcat( chunkModel, "_c1.md3" );
		ent->s.modelindex3 = G_ModelIndex( chunkModel );
		*/
	}

	//Use model
	if( ent->spawnflags & 32 ) {	//has umodel
		strcat( useModel, "_u1.md3" );
		ent->sound1to2 = G_ModelIndex( useModel );
	}

	G_SpawnVector("mins", "-16 -16 -16", ent->r.mins);
	G_SpawnVector("maxs", "16 16 16", ent->r.maxs);
	/*  This code wasn't working right
	if ( !ent->r.mins[0] && !ent->r.mins[1] && !ent->r.mins[2] )
	{
		VectorSet (ent->r.mins, -16, -16, -16);
	}
	if ( !ent->r.maxs[0] && !ent->r.maxs[1] && !ent->r.maxs[2] )
	{
		VectorSet (ent->r.maxs, 16, 16, 16);
	}
	*/

	// Scale up the tie-bomber bbox a little.
	if ( ent->model && Q_stricmp( "models/map_objects/ships/tie_bomber.md3", ent->model ) == 0 )
	{
		VectorSet (ent->r.mins, -80, -80, -80);
		VectorSet (ent->r.maxs, 80, 80, 80); 

		//ent->s.modelScale[ 0 ] = ent->s.modelScale[ 1 ] = ent->s.modelScale[ 2 ] *= 2.0f;
		//bHasScale = qtrue;
	}

	if (bHasScale)
	{
		float oldMins2;

		//scale the x axis of the bbox up.
		ent->r.maxs[0] *= ent->modelScale[0];//*scaleFactor;
		ent->r.mins[0] *= ent->modelScale[0];//*scaleFactor;
		
		//scale the y axis of the bbox up.
		ent->r.maxs[1] *= ent->modelScale[1];//*scaleFactor;
		ent->r.mins[1] *= ent->modelScale[1];//*scaleFactor;
		
		//scale the z axis of the bbox up and adjust origin accordingly
		ent->r.maxs[2] *= ent->modelScale[2];
		oldMins2 = ent->r.mins[2];
		ent->r.mins[2] *= ent->modelScale[2];
		ent->s.origin[2] += (oldMins2-ent->r.mins[2]);
	}

	if ( ent->spawnflags & 2 )
	{
		ent->s.eFlags |= EF_ANIM_ALLFAST;
	}

	G_SetOrigin( ent, ent->s.origin );
	G_SetAngles( ent, ent->s.angles );
	trap_LinkEntity(ent);

	if ( ent->spawnflags & 128 )
	{//Can be used by the player's BUTTON_USE
		ent->r.svFlags |= SVF_PLAYER_USABLE;
	}

	if ( ent->team && ent->team[0] )
	{
		ent->teamnodmg= (team_t)GetIDForString( TeamTable, ent->team );
		if ( ent->teamnodmg == TEAM_FREE )
		{
			G_Error("team name %s not recognized\n", ent->team);
		}
	}
	
	ent->team = NULL;

	//HACK
	if ( ent->model && Q_stricmp( "models/map_objects/ships/x_wing_nogear.md3", ent->model ) == 0 )
	{
		if( ent->splashDamage > 0 && ent->splashRadius > 0 )
		{
			ent->s.loopSound = G_SoundIndex( "sound/vehicles/x-wing/loop.wav" );
			/* RAFIXME - impliment this flag?
			ent->s.eFlags |= EF_LESS_ATTEN;
			*/
		}
	}
	else if ( ent->model && Q_stricmp( "models/map_objects/ships/tie_fighter.md3", ent->model ) == 0 )
	{//run a think
		G_EffectIndex( "explosions/fighter_explosion2" );
		G_SoundIndex( "sound/weapons/tie_fighter/tiepass1.wav" );
/*		G_SoundIndex( "sound/weapons/tie_fighter/tiepass2.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tiepass3.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tiepass4.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tiepass5.wav" );*/
		G_SoundIndex( "sound/weapons/tie_fighter/tie_fire.wav" );
/*		G_SoundIndex( "sound/weapons/tie_fighter/tie_fire2.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tie_fire3.wav" );*/
		G_SoundIndex( "sound/weapons/tie_fighter/TIEexplode.wav" );

		//RAFIXME - add Tie Fighter weapon?
		//RegisterItem( BG_FindItemForWeapon( WP_TIE_FIGHTER ));
		/* RAFIXME - impliment this flag?
		ent->s.eFlags |= EF_LESS_ATTEN;
		*/

		if( ent->splashDamage > 0 && ent->splashRadius > 0 )
		{
			// Yeah, I could have just made this value changable from the editor, but I
			// need it immediately!
			float		light;
			vec3_t		color;
			qboolean	lightSet, colorSet;

			ent->s.loopSound = G_SoundIndex( "sound/vehicles/tie-bomber/loop.wav" );
			//ent->e_ThinkFunc = thinkF_TieFighterThink;
			//ent->e_UseFunc = thinkF_TieFighterThink;
			//ent->nextthink = level.time + FRAMETIME;
			//RAFIXME: create this use
			//ent->use = useF_TieFighterUse;

			// if the "color" or "light" keys are set, setup constantLight
			lightSet = qtrue;//G_SpawnFloat( "light", "100", &light );
			light = 255;
			//colorSet = "1 1 1"//G_SpawnVector( "color", "1 1 1", color );
			colorSet = qtrue;
			color[0] = 1;	color[1] = 1;	color[2] = 1;
			if ( lightSet || colorSet ) 
			{
				int		r, g, b, i;

				r = color[0] * 255;
				if ( r > 255 ) {
					r = 255;
				}
				g = color[1] * 255;
				if ( g > 255 ) {
					g = 255;
				}
				b = color[2] * 255;
				if ( b > 255 ) {
					b = 255;
				}
				i = light / 4;
				if ( i > 255 ) {
					i = 255;
				}
				ent->s.constantLight = r | ( g << 8 ) | ( b << 16 ) | ( i << 24 );
			}
		}
	}
	else if ( ent->model && Q_stricmp( "models/map_objects/ships/tie_bomber.md3", ent->model ) == 0 )
	{
		G_EffectIndex( "ships/tiebomber_bomb_falling" );
		G_EffectIndex( "ships/tiebomber_explosion2" );
		G_EffectIndex( "explosions/fighter_explosion2" );
		G_SoundIndex( "sound/weapons/tie_fighter/TIEexplode.wav" );
		//RAFIXME: create this function
		//ent->e_ThinkFunc = thinkF_TieBomberThink;
		ent->nextthink = level.time + FRAMETIME;
		ent->attackDebounceTime = level.time + 1000;
		// We only take damage from a heavy weapon class missiles.
		ent->flags |= FL_DMG_BY_HEAVY_WEAP_ONLY;
		ent->s.loopSound = G_SoundIndex( "sound/vehicles/tie-bomber/loop.wav" );
		//RAFIXME: create this
		//ent->s.eFlags |= EF_LESS_ATTEN;
	}

	
	G_SpawnFloat( "gravity", "0", &grav );
	if ( grav )
	{//affected by gravity
		G_SetAngles( ent, ent->s.angles );
		G_SetOrigin( ent, ent->r.currentOrigin );
		G_SpawnString( "throwtarget", NULL, &ent->target4 ); // used to throw itself at something
		misc_model_breakable_gravity_init( ent, qtrue );
	}

	// Start off.
	if ( ent->spawnflags & 4096 )
	{
		//RAFIXME - need to fix this guy somehow
		//ent->spawnContents = ent->contents;	// It Navs can temporarly turn it "on"

		ent->s.solid = 0;
		ent->r.contents = 0;
		ent->clipmask = 0;
		ent->r.svFlags |= SVF_NOCLIENT;
		ent->s.eFlags |= EF_NODRAW;
		ent->count = 0;
	}

	G_SpawnInt( "forcevisible", "0", &forceVisible );
	if ( forceVisible )
	{//can see these through walls with force sight, so must be broadcast
		//[CoOp]
		ent->r.svFlags |= SVF_BROADCAST;
		//RAFIXME - impliment this flag
		ent->s.eFlags |= EF_FORCE_VISIBLE;
		//[/CoOp]
	}

	G_SpawnInt( "redCrosshair", "0", &redCrosshair );
	if ( redCrosshair )
	{//can see these through walls with force sight, so must be broadcast
		//RAFIXME - impliment this
		//ent->flags |= FL_RED_CROSSHAIR;
	}
}
/*
===============
RegisterItem

The item will be added to the precache list
===============
*/
void RegisterItem( gitem_t *item ) {
	if ( !item ) {
		G_Error( "RegisterItem: NULL" );
	}
	itemRegistered[ item - bg_itemlist ] = qtrue;
}
Esempio n. 23
0
void SP_trigger_objective_info(gentity_t *ent) {
	char *scorestring;
	char *customimage;
	int  cix, cia, objflags;

	if (!ent->track) {
		G_Error("'trigger_objective_info' does not have a 'track' \n");
	}

	if ((ent->spawnflags & MESSAGE_OVERRIDE) && !ent->spawnitem) {
		G_Error("'trigger_objective_info' has override flag set but no override text\n");
	}

	// Gordon: for specifying which commandmap objectives this entity "belongs" to
	G_SpawnInt("objflags", "0", &objflags);

	if (G_SpawnString("customimage", "", &customimage)) {
		cix = cia = G_ShaderIndex(customimage);
	} else {
		if (G_SpawnString("customaxisimage", "", &customimage)) {
			cix = G_ShaderIndex(customimage);
		} else {
			cix = 0;
		}

		if (G_SpawnString("customalliesimage", "", &customimage)) {
			cia = G_ShaderIndex(customimage);
		} else if (G_SpawnString("customalliedimage", "", &customimage)) {
			cia = G_ShaderIndex(customimage);
		} else {
			cia = 0;
		}
	}

	G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "e", va("%d", (int)(ent - g_entities)));
	G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "o", va("%i", objflags));
	G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "cix", va("%i", cix));
	G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "cia", va("%i", cia));
	G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "s", va("%i", ent->spawnflags));
	G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "n", ent->message ? ent->message : "");

	if (level.numOidTriggers >= MAX_OID_TRIGGERS) {
		G_Error("Exceeded maximum number of 'trigger_objective_info' entities\n");
	}

	// JPW NERVE -- if this trigger has a "score" field set, then blowing up an objective
	//  inside of this field will add "score" to the right player team.  storing this
	//  in ent->accuracy since that's unused.
	G_SpawnString("score", "0", &scorestring);
	ent->accuracy = atof(scorestring);

	trap_SetConfigstring(CS_OID_TRIGGERS + level.numOidTriggers, ent->track);

	InitTrigger(ent);

	if (ent->s.origin[0] || ent->s.origin[1] || ent->s.origin[2]) {
		G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "x", va("%i", (int)ent->s.origin[0]));
		G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "y", va("%i", (int)ent->s.origin[1]));
		G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "z", va("%i", (int)ent->s.origin[2]));
	} else {
		vec3_t mid;
		VectorAdd(ent->r.absmin, ent->r.absmax, mid);
		VectorScale(mid, 0.5f, mid);

		G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "x", va("%i", (int)mid[0]));
		G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "y", va("%i", (int)mid[1]));
		G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "z", va("%i", (int)mid[2]));
	}

	ent->s.teamNum = level.numOidTriggers++;

	// unlike other triggers, we need to send this one to the client
	ent->r.svFlags &= ~SVF_NOCLIENT;
	ent->s.eType    = ET_OID_TRIGGER;

	if (!ent->target) {
		// no target - just link and go
		trap_LinkEntity(ent);
	} else {
		// Arnout: finalize spawing on fourth frame to allow for proper linking with targets
		ent->nextthink = level.time + (3 * FRAMETIME);
		ent->think     = Think_SetupObjectiveInfo;
	}
}
Esempio n. 24
0
/*
===========
ClientSpawn

Called every time a client is placed fresh in the world:
after the first ClientBegin, and after each respawn
Initializes all non-persistant parts of playerState
============
*/
void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles )
{
  int                 index;
  vec3_t              spawn_origin, spawn_angles;
  gclient_t           *client;
  int                 i;
  clientPersistant_t  saved;
  clientSession_t     savedSess;
  int                 persistant[ MAX_PERSISTANT ];
  gentity_t           *spawnPoint = NULL;
  int                 flags;
  int                 savedPing;
  int                 teamLocal;
  int                 eventSequence;
  char                userinfo[ MAX_INFO_STRING ];
  vec3_t              up = { 0.0f, 0.0f, 1.0f };
  int                 maxAmmo, maxClips;
  weapon_t            weapon;

  index = ent - g_entities;
  client = ent->client;

  teamLocal = client->pers.teamSelection;

  //if client is dead and following teammate, stop following before spawning
  if( client->sess.spectatorClient != -1 )
  {
    client->sess.spectatorClient = -1;
    client->sess.spectatorState = SPECTATOR_FREE;
  }

  // only start client if chosen a class and joined a team
  if( client->pers.classSelection == PCL_NONE && teamLocal == TEAM_NONE )
    client->sess.spectatorState = SPECTATOR_FREE;
  else if( client->pers.classSelection == PCL_NONE )
    client->sess.spectatorState = SPECTATOR_LOCKED;

  // if client is dead and following teammate, stop following before spawning
  if( ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
    G_StopFollowing( ent );

  if( origin != NULL )
    VectorCopy( origin, spawn_origin );

  if( angles != NULL )
    VectorCopy( angles, spawn_angles );

  // find a spawn point
  // do it before setting health back up, so farthest
  // ranging doesn't count this client
  if( client->sess.spectatorState != SPECTATOR_NOT )
  {
    if( teamLocal == TEAM_NONE )
      spawnPoint = G_SelectSpectatorSpawnPoint( spawn_origin, spawn_angles );
    else if( teamLocal == TEAM_ALIENS )
      spawnPoint = G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
    else if( teamLocal == TEAM_HUMANS )
      spawnPoint = G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
  }
  else
  {
    if( spawn == NULL )
    {
      G_Error( "ClientSpawn: spawn is NULL\n" );
      return;
    }

    spawnPoint = spawn;

    if( ent != spawn )
    {
      //start spawn animation on spawnPoint
      G_SetBuildableAnim( spawnPoint, BANIM_SPAWN1, qtrue );

      if( spawnPoint->buildableTeam == TEAM_ALIENS )
        spawnPoint->clientSpawnTime = ALIEN_SPAWN_REPEAT_TIME;
      else if( spawnPoint->buildableTeam == TEAM_HUMANS )
        spawnPoint->clientSpawnTime = HUMAN_SPAWN_REPEAT_TIME;
    }
  }

  // toggle the teleport bit so the client knows to not lerp
  flags = ( ent->client->ps.eFlags & EF_TELEPORT_BIT ) ^ EF_TELEPORT_BIT;
  G_UnlaggedClear( ent );

  // clear everything but the persistant data

  saved = client->pers;
  savedSess = client->sess;
  savedPing = client->ps.ping;

  for( i = 0; i < MAX_PERSISTANT; i++ )
    persistant[ i ] = client->ps.persistant[ i ];

  eventSequence = client->ps.eventSequence;
  memset( client, 0, sizeof( *client ) );

  client->pers = saved;
  client->sess = savedSess;
  client->ps.ping = savedPing;
  client->lastkilled_client = -1;

  for( i = 0; i < MAX_PERSISTANT; i++ )
    client->ps.persistant[ i ] = persistant[ i ];

  client->ps.eventSequence = eventSequence;

  // increment the spawncount so the client will detect the respawn
  client->ps.persistant[ PERS_SPAWN_COUNT ]++;
  client->ps.persistant[ PERS_SPECSTATE ] = client->sess.spectatorState;

  client->airOutTime = level.time + 12000;

  trap_GetUserinfo( index, userinfo, sizeof( userinfo ) );
  client->ps.eFlags = flags;

  //Com_Printf( "ent->client->pers->pclass = %i\n", ent->client->pers.classSelection );

  ent->s.groundEntityNum = ENTITYNUM_NONE;
  ent->client = &level.clients[ index ];
  ent->takedamage = qtrue;
  ent->inuse = qtrue;
  ent->classname = "player";
  ent->r.contents = CONTENTS_BODY;
  ent->clipmask = MASK_PLAYERSOLID;
  ent->die = player_die;
  ent->waterlevel = 0;
  ent->watertype = 0;
  ent->flags = 0;

  // calculate each client's acceleration
  ent->evaluateAcceleration = qtrue;

  client->ps.stats[ STAT_MISC ] = 0;

  client->ps.eFlags = flags;
  client->ps.clientNum = index;

  BG_ClassBoundingBox( ent->client->pers.classSelection, ent->r.mins, ent->r.maxs, NULL, NULL, NULL );

  if( client->sess.spectatorState == SPECTATOR_NOT )
    client->ps.stats[ STAT_MAX_HEALTH ] =
      BG_Class( ent->client->pers.classSelection )->health;
  else
    client->ps.stats[ STAT_MAX_HEALTH ] = 100;

  // clear entity values
  if( ent->client->pers.classSelection == PCL_HUMAN )
  {
    BG_AddUpgradeToInventory( UP_MEDKIT, client->ps.stats );
    weapon = client->pers.humanItemSelection;
  }
  else if( client->sess.spectatorState == SPECTATOR_NOT )
    weapon = BG_Class( ent->client->pers.classSelection )->startWeapon;
  else
    weapon = WP_NONE;

  maxAmmo = BG_Weapon( weapon )->maxAmmo;
  maxClips = BG_Weapon( weapon )->maxClips;
  client->ps.stats[ STAT_WEAPON ] = weapon;
  client->ps.ammo = maxAmmo;
  client->ps.clips = maxClips;

  // We just spawned, not changing weapons
  client->ps.persistant[ PERS_NEWWEAPON ] = 0;

  ent->client->ps.stats[ STAT_CLASS ] = ent->client->pers.classSelection;
  ent->client->ps.stats[ STAT_TEAM ] = ent->client->pers.teamSelection;

  ent->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE;
  ent->client->ps.stats[ STAT_STATE ] = 0;
  VectorSet( ent->client->ps.grapplePoint, 0.0f, 0.0f, 1.0f );

  // health will count down towards max_health
  ent->health = client->ps.stats[ STAT_HEALTH ] = client->ps.stats[ STAT_MAX_HEALTH ]; //* 1.25;

  //if evolving scale health
  if( ent == spawn )
  {
    ent->health *= ent->client->pers.evolveHealthFraction;
    client->ps.stats[ STAT_HEALTH ] *= ent->client->pers.evolveHealthFraction;
  }

  //clear the credits array
  for( i = 0; i < MAX_CLIENTS; i++ )
    ent->credits[ i ] = 0;

  client->ps.stats[ STAT_STAMINA ] = STAMINA_MAX;

  G_SetOrigin( ent, spawn_origin );
  VectorCopy( spawn_origin, client->ps.origin );

#define UP_VEL  150.0f
#define F_VEL   50.0f

  //give aliens some spawn velocity
  if( client->sess.spectatorState == SPECTATOR_NOT &&
      client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
  {
    if( ent == spawn )
    {
      //evolution particle system
      G_AddPredictableEvent( ent, EV_ALIEN_EVOLVE, DirToByte( up ) );
    }
    else
    {
      spawn_angles[ YAW ] += 180.0f;
      AngleNormalize360( spawn_angles[ YAW ] );

      if( spawnPoint->s.origin2[ 2 ] > 0.0f )
      {
        vec3_t  forward, dir;

        AngleVectors( spawn_angles, forward, NULL, NULL );
        VectorScale( forward, F_VEL, forward );
        VectorAdd( spawnPoint->s.origin2, forward, dir );
        VectorNormalize( dir );

        VectorScale( dir, UP_VEL, client->ps.velocity );
      }

      G_AddPredictableEvent( ent, EV_PLAYER_RESPAWN, 0 );
    }
  }
  else if( client->sess.spectatorState == SPECTATOR_NOT &&
           client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
  {
    spawn_angles[ YAW ] += 180.0f;
    AngleNormalize360( spawn_angles[ YAW ] );
  }

  // the respawned flag will be cleared after the attack and jump keys come up
  client->ps.pm_flags |= PMF_RESPAWNED;

  trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
  G_SetClientViewAngle( ent, spawn_angles );

  if( client->sess.spectatorState == SPECTATOR_NOT )
  {
    trap_LinkEntity( ent );

    // force the base weapon up
    if( client->pers.teamSelection == TEAM_HUMANS )
      G_ForceWeaponChange( ent, weapon );

    client->ps.weaponstate = WEAPON_READY;
  }

  // don't allow full run speed for a bit
  client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
  client->ps.pm_time = 100;

  client->respawnTime = level.time;
  ent->nextRegenTime = level.time;

  client->inactivityTime = level.time + g_inactivity.integer * 1000;
  client->latched_buttons = 0;

  // set default animations
  client->ps.torsoAnim = TORSO_STAND;
  client->ps.legsAnim = LEGS_IDLE;

  if( level.intermissiontime )
    MoveClientToIntermission( ent );
  else
  {
    // fire the targets of the spawn point
    if( !spawn )
      G_UseTargets( spawnPoint, ent );

    // select the highest weapon number available, after any
    // spawn given items have fired
    client->ps.weapon = 1;

    for( i = WP_NUM_WEAPONS - 1; i > 0 ; i-- )
    {
      if( BG_InventoryContainsWeapon( i, client->ps.stats ) )
      {
        client->ps.weapon = i;
        break;
      }
    }
  }

  // run a client frame to drop exactly to the floor,
  // initialize animations and other things
  client->ps.commandTime = level.time - 100;
  ent->client->pers.cmd.serverTime = level.time;
  ClientThink( ent-g_entities );

  // positively link the client, even if the command times are weird
  if( client->sess.spectatorState == SPECTATOR_NOT )
  {
    BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
    VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
    trap_LinkEntity( ent );
  }

  // must do this here so the number of active clients is calculated
  CalculateRanks( );

  // run the presend to set anything else
  ClientEndFrame( ent );

  // clear entity state values
  BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
}
Esempio n. 25
0
/**
Returns qfalse if the move is blocked
*/
static qboolean	G_TryPushingEntity(gentity_t *check, gentity_t *pusher, vec3_t move, vec3_t amove)
{
	vec3_t		matrix[3], transpose[3];
	vec3_t		org, org2, move2;
	gentity_t	*block;

	// EF_MOVER_STOP will just stop when contacting another entity
	// instead of pushing it, but entities can still ride on top of it
	if ((pusher->s.eFlags & EF_MOVER_STOP) &&
		check->s.groundEntityNum != pusher->s.number) {
		return qfalse;
	}

	// save off the old position
	if (pushed_p > &pushed[MAX_GENTITIES]) {
		G_Error("pushed_p > &pushed[MAX_GENTITIES]");
	}
	pushed_p->ent = check;
	VectorCopy(check->s.pos.trBase, pushed_p->origin);
	VectorCopy(check->s.apos.trBase, pushed_p->angles);
	if (check->client) {
		pushed_p->deltayaw = check->client->ps.delta_angles[YAW];
		VectorCopy(check->client->ps.origin, pushed_p->origin);
	}
	pushed_p++;

	// try moving the contacted entity
	// figure movement due to the pusher's amove
	G_CreateRotationMatrix(amove, transpose);
	G_TransposeMatrix(transpose, matrix);
	if (check->client) {
		VectorSubtract(check->client->ps.origin, pusher->r.currentOrigin, org);
	}
	else {
		VectorSubtract(check->s.pos.trBase, pusher->r.currentOrigin, org);
	}
	VectorCopy(org, org2);
	G_RotatePoint(org2, matrix);
	VectorSubtract(org2, org, move2);
	// add movement
	VectorAdd(check->s.pos.trBase, move, check->s.pos.trBase);
	VectorAdd(check->s.pos.trBase, move2, check->s.pos.trBase);
	if (check->client) {
		VectorAdd(check->client->ps.origin, move, check->client->ps.origin);
		VectorAdd(check->client->ps.origin, move2, check->client->ps.origin);
		// make sure the client's view rotates when on a rotating mover
		check->client->ps.delta_angles[YAW] += ANGLE2SHORT(amove[YAW]);
	}

	// may have pushed them off an edge
	if (check->s.groundEntityNum != pusher->s.number) {
		check->s.groundEntityNum = ENTITYNUM_NONE;
	}

	block = G_TestEntityPosition(check);
	if (!block) {
		// pushed ok
		if (check->client) {
			VectorCopy(check->client->ps.origin, check->r.currentOrigin);
		} else {
			VectorCopy(check->s.pos.trBase, check->r.currentOrigin);
		}
		trap_LinkEntity (check);
		return qtrue;
	}

	// if it is ok to leave in the old position, do it
	// this is only relevent for riding entities, not pushed
	// Sliding trapdoors can cause this.
	VectorCopy((pushed_p-1)->origin, check->s.pos.trBase);
	if (check->client) {
		VectorCopy((pushed_p-1)->origin, check->client->ps.origin);
	}
	VectorCopy((pushed_p-1)->angles, check->s.apos.trBase);
	block = G_TestEntityPosition (check);
	if (!block) {
		check->s.groundEntityNum = ENTITYNUM_NONE;
		pushed_p--;
		return qtrue;
	}

	// blocked
	return qfalse;
}
Esempio n. 26
0
/*
===========
G_SelectRandomFurthestSpawnPoint

Chooses a player start, deathmatch start, etc
============
*/
gentity_t *G_SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
{
  gentity_t *spot;
  vec3_t    delta;
  float     dist;
  float     list_dist[ 64 ];
  gentity_t *list_spot[ 64 ];
  int       numSpots, rnd, i, j;

  numSpots = 0;
  spot = NULL;

  while( ( spot = G_Find( spot, FOFS( classname ), "info_player_deathmatch" ) ) != NULL )
  {
    if( SpotWouldTelefrag( spot ) )
      continue;

    VectorSubtract( spot->s.origin, avoidPoint, delta );
    dist = VectorLength( delta );

    for( i = 0; i < numSpots; i++ )
    {
      if( dist > list_dist[ i ] )
      {
        if( numSpots >= 64 )
          numSpots = 64 - 1;

        for( j = numSpots; j > i; j-- )
        {
          list_dist[ j ] = list_dist[ j - 1 ];
          list_spot[ j ] = list_spot[ j - 1 ];
        }

        list_dist[ i ] = dist;
        list_spot[ i ] = spot;
        numSpots++;

        if( numSpots > 64 )
          numSpots = 64;

        break;
      }
    }

    if( i >= numSpots && numSpots < 64 )
    {
      list_dist[ numSpots ] = dist;
      list_spot[ numSpots ] = spot;
      numSpots++;
    }
  }

  if( !numSpots )
  {
    spot = G_Find( NULL, FOFS( classname ), "info_player_deathmatch" );

    if( !spot )
      G_Error( "Couldn't find a spawn point" );

    VectorCopy( spot->s.origin, origin );
    origin[ 2 ] += 9;
    VectorCopy( spot->s.angles, angles );
    return spot;
  }

  // select a random spot from the spawn points furthest away
  rnd = random( ) * ( numSpots / 2 );

  VectorCopy( list_spot[ rnd ]->s.origin, origin );
  origin[ 2 ] += 9;
  VectorCopy( list_spot[ rnd ]->s.angles, angles );

  return list_spot[ rnd ];
}
Esempio n. 27
0
/*
===========
SelectRandomFurthestSpawnPoint

Chooses a player start, deathmatch start, etc
============
*/
gentity_t *SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles, qboolean isbot ) {
	gentity_t	*spot;
	vec3_t		delta;
	float		dist;
	float		list_dist[MAX_SPAWN_POINTS];
	gentity_t	*list_spot[MAX_SPAWN_POINTS];
	int			numSpots, rnd, i, j;

	numSpots = 0;
	spot = NULL;

	while((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
	{
		if(SpotWouldTelefrag(spot))
			continue;

		if(((spot->flags & FL_NO_BOTS) && isbot) ||
		   ((spot->flags & FL_NO_HUMANS) && !isbot))
		{
			// spot is not for this human/bot player
			continue;
		}

		VectorSubtract( spot->s.origin, avoidPoint, delta );
		dist = VectorLength( delta );

		for (i = 0; i < numSpots; i++)
		{
			if(dist > list_dist[i])
			{
				if (numSpots >= MAX_SPAWN_POINTS)
					numSpots = MAX_SPAWN_POINTS - 1;
					
				for(j = numSpots; j > i; j--)
				{
					list_dist[j] = list_dist[j-1];
					list_spot[j] = list_spot[j-1];
				}
				
				list_dist[i] = dist;
				list_spot[i] = spot;
				
				numSpots++;
				break;
			}
		}
		
		if(i >= numSpots && numSpots < MAX_SPAWN_POINTS)
		{
			list_dist[numSpots] = dist;
			list_spot[numSpots] = spot;
			numSpots++;
		}
	}
	
	if(!numSpots)
	{
		spot = G_Find(NULL, FOFS(classname), "info_player_deathmatch");

		if (!spot)
			G_Error( "Couldn't find a spawn point" );

		VectorCopy (spot->s.origin, origin);
		origin[2] += 9;
		VectorCopy (spot->s.angles, angles);
		return spot;
	}

	// select a random spot from the spawn points furthest away
	rnd = random() * (numSpots / 2);

	VectorCopy (list_spot[rnd]->s.origin, origin);
	origin[2] += 9;
	VectorCopy (list_spot[rnd]->s.angles, angles);

	return list_spot[rnd];
}
/*
=================
G_ScriptAction_PlayAnim

  syntax: playanim <startframe> <endframe> [looping <FOREVER/duration>] [rate <FPS>]

  NOTE: all source animations must be at 20fps
=================
*/
qboolean G_ScriptAction_PlayAnim( gentity_t *ent, char *params ) {
    char *pString, *token, tokens[2][MAX_QPATH];
    int i, endtime = 0; // TTimo: init
    qboolean looping = qfalse, forever = qfalse;
    int startframe, endframe, idealframe;
    int rate = 20;

    if ( ( ent->scriptStatus.scriptFlags & SCFL_ANIMATING ) && ( ent->scriptStatus.scriptStackChangeTime == level.time ) ) {
        // this is a new call, so cancel the previous animation
        ent->scriptStatus.scriptFlags &= ~SCFL_ANIMATING;
    }

    pString = params;

    for ( i = 0; i < 2; i++ ) {
        token = COM_ParseExt( &pString, qfalse );
        if ( !token || !token[0] ) {
            G_Printf( "G_Scripting: syntax error\n\nplayanim <startframe> <endframe> [LOOPING <duration>]\n" );
            return qtrue;
        } else {
            Q_strncpyz( tokens[i], token, sizeof( tokens[i] ) );
        }
    }

    startframe = atoi( tokens[0] );
    endframe = atoi( tokens[1] );

    // check for optional parameters
    token = COM_ParseExt( &pString, qfalse );
    if ( token[0] ) {
        if ( !Q_strcasecmp( token, "looping" ) ) {
            looping = qtrue;

            token = COM_ParseExt( &pString, qfalse );
            if ( !token || !token[0] ) {
                G_Printf( "G_Scripting: syntax error\n\nplayanim <startframe> <endframe> [LOOPING <duration>]\n" );
                return qtrue;
            }
            if ( !Q_strcasecmp( token, "untilreachmarker" ) ) {
                if ( level.time < ent->s.pos.trTime + ent->s.pos.trDuration ) {
                    endtime = level.time + 100;
                } else {
                    endtime = 0;
                }
            } else if ( !Q_strcasecmp( token, "forever" ) ) {
                ent->scriptStatus.animatingParams = params;
                ent->scriptStatus.scriptFlags |= SCFL_ANIMATING;
                endtime = level.time + 100;     // we don't care when it ends, since we are going forever!
                forever = qtrue;
            } else {
                endtime = ent->scriptStatus.scriptStackChangeTime + atoi( token );
            }

            token = COM_ParseExt( &pString, qfalse );
        }

        if ( token[0] && !Q_strcasecmp( token, "rate" ) ) {
            token = COM_ParseExt( &pString, qfalse );
            if ( !token[0] ) {
                G_Error( "G_Scripting: playanim has RATE parameter without an actual rate specified" );
            }
            rate = atoi( token );
        }

        if ( !looping ) {
            endtime = ent->scriptStatus.scriptStackChangeTime + ( ( endframe - startframe ) * ( 1000 / 20 ) );
        }
    }

    idealframe = startframe + (int)floor( (float)( level.time - ent->scriptStatus.scriptStackChangeTime ) / ( 1000.0 / (float)rate ) );
    if ( looping ) {
        ent->s.frame = startframe + ( idealframe - startframe ) % ( endframe - startframe );
        ent->s.eFlags |= EF_MOVER_ANIMATE;
    } else {
        if ( idealframe > endframe ) {
            ent->s.frame = endframe;
            ent->s.eFlags &= ~EF_MOVER_ANIMATE; // stop interpolation, since we have gone passed the endframe
        } else {
            ent->s.frame = idealframe;
            ent->s.eFlags |= EF_MOVER_ANIMATE;
        }
    }

    if ( forever ) {
        ent->s.eFlags |= EF_MOVER_ANIMATE;
        return qtrue;   // continue to the next command
    }

    if ( endtime <= level.time ) {
        ent->s.eFlags &= ~EF_MOVER_ANIMATE; // stop animating
        return qtrue;
    } else {
        return qfalse;
    }
};
Esempio n. 29
0
static void EvaluateField(const save_field_t *pField, byte *pbBase, byte *pbOriginalRefData/* may be NULL*/)
{
	void *pv		 = (void *)(pbBase			  + pField->iOffset);
	void *pvOriginal = (void *)(pbOriginalRefData + pField->iOffset);

	switch (pField->eFieldType)
	{
	case F_STRING:
		*(char **)pv = GetStringPtr(*(int *)pv, pbOriginalRefData?*(char**)pvOriginal:NULL);
		break;

	case F_GENTITY:
		*(gentity_t **)pv = GetGEntityPtr(*(int *)pv);
		break;

	case F_GROUP:
		*(AIGroupInfo_t **)pv = GetGroupPtr(*(int *)pv);
		break;

	case F_GCLIENT:
		*(gclient_t **)pv = GetGClientPtr(*(int *)pv);
		break;

	case F_ITEM:
		*(gitem_t **)pv = GetGItemPtr(*(int *)pv);
		break;

	case F_VEHINFO:
		*(vehicleInfo_t **)pv = GetVehicleInfoPtr(*(int *)pv);
		break;

	case F_BEHAVIORSET:
		{
			char **p = (char **) pv;
			char **pO= (char **) pvOriginal;
			for (int i=0; i<NUM_BSETS; i++, p++, pO++)
			{
				*p = GetStringPtr(*(int *)p, pbOriginalRefData?*(char **)pO:NULL);
			}
		}
		break;

/*MCG
	case F_BODYQUEUE:
		{
			gentity_t **p = (gentity_t **) pv;
			for (int i=0; i<BODY_QUEUE_SIZE; i++, p++)
			{
				*p = GetGEntityPtr(*(int *)p);
			}
		}
		break;
*/

	case F_ALERTEVENT:
		{				
			alertEvent_t* p = (alertEvent_t *) pv;

			for (int i=0; i<MAX_ALERT_EVENTS; i++)
			{
				p[i].owner = GetGEntityPtr((intptr_t)(p[i].owner));
			}
		}
		break;

	case F_AIGROUPS:	// convert to ptrs within this into indexes...
		{
			AIGroupInfo_t* p = (AIGroupInfo_t *) pv;

			for (int i=0; i<MAX_FRAME_GROUPS; i++)
			{
				p[i].enemy		= GetGEntityPtr((intptr_t)(p[i].enemy));
				p[i].commander	= GetGEntityPtr((intptr_t)(p[i].commander));
			}
		}
		break;

	case F_ANIMFILESETS:
		{
			animFileSet_t* p = (animFileSet_t *) pv;
			char *pO;
			for (int i=0; i<MAX_ANIM_FILES; i++)
			{
				for ( int j=0; j<MAX_ANIM_EVENTS; j++ )
				{
					pO = pbOriginalRefData ? level.knownAnimFileSets[i].torsoAnimEvents[j].stringData : NULL;
					p[i].torsoAnimEvents[j].stringData = GetStringPtr((intptr_t)p[i].torsoAnimEvents[j].stringData, pO);
					pO = pbOriginalRefData ? level.knownAnimFileSets[i].legsAnimEvents[j].stringData : NULL;
					p[i].legsAnimEvents[j].stringData = GetStringPtr((intptr_t)p[i].legsAnimEvents[j].stringData, pO);
				}
			}
		}
		break;
//	// These fields are patched in when their relevant owners are loaded
	case F_BOOLPTR:
	case F_NULL:
		break;

	case F_IGNORE:
		break;

	default:
		G_Error ("EvaluateField: unknown field type");
		break;
	}
}
Esempio n. 30
0
static void G_AddBot(const char *name, int skill, const char *team, const char *spawnPoint, int playerClass, int playerWeapon, int characerIndex, const char *respawn, const char *scriptName, int rank, int skills[], qboolean pow)
{
#define MAX_BOTNAMES 1024
	int       clientNum;
	char      *botinfo;
	gentity_t *bot;
	char      *key;
	char      *s;
	char      *botname;
//	char			*model;
	char userinfo[MAX_INFO_STRING];

	// get the botinfo from bots.txt
	botinfo = G_GetBotInfoByName("wolfbot");
	if (!botinfo)
	{
		G_Printf(S_COLOR_RED "Error: Bot '%s' not defined\n", name);
		return;
	}

	// create the bot's userinfo
	userinfo[0] = '\0';

	botname = Info_ValueForKey(botinfo, "funname");
	if (!botname[0])
	{
		botname = Info_ValueForKey(botinfo, "name");
	}
	Info_SetValueForKey(userinfo, "name", botname);
	Info_SetValueForKey(userinfo, "rate", "25000");
	Info_SetValueForKey(userinfo, "snaps", "20");
	Info_SetValueForKey(userinfo, "skill", va("%i", skill));

	s = Info_ValueForKey(botinfo, "aifile");
	if (!*s)
	{
		trap_Printf(S_COLOR_RED "Error: bot has no aifile specified\n");
		return;
	}

	// have the server allocate a client slot
	clientNum = trap_BotAllocateClient(0);      // Arnout: 0 means no prefered clientslot
	if (clientNum == -1)
	{
		G_Printf(S_COLOR_RED "Unable to add bot.  All player slots are in use.\n");
		G_Printf(S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n");
		return;
	}

	// initialize the bot settings
	if (!team || !*team)
	{
		if (PickTeam(clientNum) == TEAM_AXIS)
		{
			team = "red";
		}
		else
		{
			team = "blue";
		}
	}
	Info_SetValueForKey(userinfo, "characterfile", Info_ValueForKey(botinfo, "aifile"));
	//Info_SetValueForKey( userinfo, "skill", va( "%i", skill ) );
	Info_SetValueForKey(userinfo, "team", team);

	if (spawnPoint && spawnPoint[0])
	{
		Info_SetValueForKey(userinfo, "spawnPoint", spawnPoint);
	}

	if (scriptName && scriptName[0])
	{
		Info_SetValueForKey(userinfo, "scriptName", scriptName);
	}

/*	if (playerClass > 0) {
        Info_SetValueForKey( userinfo, "pClass", va("%i", playerClass) );
    }

    if (playerWeapon) {
        Info_SetValueForKey( userinfo, "pWeapon", va("%i", playerWeapon) );
    }*/
	// END Mad Doc - TDF

	key = "wolfbot";
	if (!Q_stricmp((char *)name, key))
	{
		// read the botnames file, and pick a name that doesnt exist
		fileHandle_t f;
		int          len, i, j, k;
		qboolean     setname = qfalse;
		char         botnames[8192], *pbotnames, *listbotnames[MAX_BOTNAMES], *token, *oldpbotnames;
		int          lengthbotnames[MAX_BOTNAMES];

		len = trap_FS_FOpenFile("botfiles/botnames.txt", &f, FS_READ);

		if (len >= 0)
		{
			if (len > sizeof(botnames))
			{
				G_Error("botfiles/botnames.txt is too big (max = %i)", (int)sizeof(botnames));
			}
			memset(botnames, 0, sizeof(botnames));
			trap_FS_Read(botnames, len, f);
			pbotnames = botnames;
			// read them in
			i            = 0;
			oldpbotnames = pbotnames;
			while ((token = COM_Parse(&pbotnames)))
			{
				if (!token[0])
				{
					break;
				}
				listbotnames[i]                    = strstr(oldpbotnames, token);
				lengthbotnames[i]                  = strlen(token);
				listbotnames[i][lengthbotnames[i]] = 0;
				oldpbotnames                       = pbotnames;
				if (++i == MAX_BOTNAMES)
				{
					break;
				}
			}
			//
			if (i > 2)
			{
				j = rand() % (i - 1);   // start at a random spot inthe list
				for (k = j + 1; k != j; k++)
				{
					if (k == i)
					{
						k = -1; // gets increased on next loop
						continue;
					}
					if (ClientFromName(listbotnames[k]) == -1)
					{
						// found an unused name
						Info_SetValueForKey(userinfo, "name", listbotnames[k]);
						setname = qtrue;
						break;
					}
				}
			}
			//
			trap_FS_FCloseFile(f);
		}

		if (!setname)
		{
			Info_SetValueForKey(userinfo, "name", va("wolfbot_%i", clientNum + 1));
		}
	}
	else
	{
		Info_SetValueForKey(userinfo, "name", name);
	}

	// if a character was specified, put the index of that character filename in the CS_CHARACTERS table in the userinfo
	if (characerIndex != -1)
	{
		Info_SetValueForKey(userinfo, "ch", va("%i", characerIndex));
	}

	// if a rank was specified, use that
/*	if (rank != -1) {
        Info_SetValueForKey(userinfo, "rank", va("%i", rank));
    }*/

	// END Mad Doc - TDF

	bot             = &g_entities[clientNum];
	bot->r.svFlags |= SVF_BOT;
	if (pow)
	{
		bot->r.svFlags |= SVF_POW;
	}
	bot->inuse  = qtrue;
	bot->aiName = bot->client->pers.netname;

	// register the userinfo
	trap_SetUserinfo(clientNum, userinfo);

	// have it connect to the game as a normal client
	if ((s = ClientConnect(clientNum, qtrue, qtrue)))
	{
		G_Printf(S_COLOR_RED "Unable to add bot: %s\n", s);
		return;
	}

	SetTeam(bot, (char *)team, qtrue, -1, -1, qfalse);

/*	if( skills ) {
        int i;

        for( i = 0; i < SK_NUM_SKILLS; i++ ) {
            bot->client->sess.skill[i] = skills[i];
        }
    }*/
	return;
}