Пример #1
0
/*
* G_RunEntities
* treat each object in turn
* even the world and clients get a chance to think
*/
static void G_RunEntities( void )
{
	edict_t	*ent;

	for( ent = &game.edicts[0]; ENTNUM( ent ) < game.numentities; ent++ )
	{
		if( !ent->r.inuse )
			continue;
		if( ISEVENTENTITY( &ent->s ) )
			continue; // events do not think

		level.current_entity = ent;

		// backup oldstate ( for world frame ).
		ent->olds = ent->s;

		// if the ground entity moved, make sure we are still on it
		if( !ent->r.client )
		{
			if( ( ent->groundentity ) && ( ent->groundentity->linkcount != ent->groundentity_linkcount ) )
				G_CheckGround( ent );
		}

		G_RunEntity( ent );

		if( ent->takedamage )
			ent->s.effects |= EF_TAKEDAMAGE;
		else
			ent->s.effects &= ~EF_TAKEDAMAGE;
	}
}
Пример #2
0
/*
* G_RunEntity
*
*/
void G_RunEntity( edict_t *ent ) {
	edict_t *part;

	if( !level.canSpawnEntities ) { // don't try to think before map entities are spawned
		return;
	}

	if( ISEVENTENTITY( &ent->s ) ) { // events do not think
		return;
	}

	if( ent->timeDelta && !( ent->r.svflags & SVF_PROJECTILE ) ) {
		G_Printf( "Warning: G_RunEntity 'Fixing timeDelta on non projectile entity\n" );
		ent->timeDelta = 0;
	}

	// only team captains decide the think, and they make think their team members when they do
	if( !( ent->flags & FL_TEAMSLAVE ) ) {
		for( part = ent; part; part = part->teamchain ) {
			SV_RunThink( part );
		}
	}

	switch( (int)ent->movetype ) {
		case MOVETYPE_NONE:
		case MOVETYPE_NOCLIP: // only used for clients, that use pmove
			SV_Physics_None( ent );
			break;
		case MOVETYPE_PLAYER:
			SV_Physics_None( ent );
			break;
		case MOVETYPE_PUSH:
		case MOVETYPE_STOP:
			SV_Physics_Pusher( ent );
			break;
		case MOVETYPE_BOUNCE:
		case MOVETYPE_BOUNCEGRENADE:
			SV_Physics_Toss( ent );
			break;
		case MOVETYPE_TOSS:
			SV_Physics_Toss( ent );
			break;
		case MOVETYPE_FLY:
			SV_Physics_Toss( ent );
			break;
		case MOVETYPE_LINEARPROJECTILE:
			SV_Physics_LinearProjectile( ent );
			break;
		case MOVETYPE_TOSSSLIDE:
			G_BoxSlideMove( ent, ent->r.clipmask ? ent->r.clipmask : MASK_PLAYERSOLID, 1.01f, 10 );
			break;
		case MOVETYPE_STEP:
			SV_Physics_Step( ent );
			break;
		default:
			G_Error( "SV_Physics: bad movetype %i", (int)ent->movetype );
	}
}
Пример #3
0
/*
* G_ClearSnap
* We just run G_SnapFrame, the server just sent the snap to the clients,
* it's now time to clean up snap specific data to start the next snap from clean.
*/
void G_ClearSnap( void )
{
	edict_t	*ent;

	game.realtime = trap_Milliseconds(); // level.time etc. might not be real time

	// clear gametype's clock override
	gs.gameState.longstats[GAMELONG_CLOCKOVERRIDE] = 0;

	// clear all events in the snap
	for( ent = &game.edicts[0]; ENTNUM( ent ) < game.numentities; ent++ )
	{
		if( ISEVENTENTITY( &ent->s ) )  // events do not persist after a snapshot
		{
			G_FreeEdict( ent );
			continue;
		}

		// events only last for a single message
		ent->s.events[0] = ent->s.events[1] = 0;
		ent->s.eventParms[0] = ent->s.eventParms[1] = 0;
		ent->numEvents = 0;
		ent->eventPriority[0] = ent->eventPriority[1] = false;
		ent->s.teleported = qfalse; // remove teleported bit.

		// remove effect bits that are (most likely) added from gametypes
		ent->s.effects = ( ent->s.effects & (EF_TAKEDAMAGE|EF_CARRIER|EF_FLAG_TRAIL|EF_ROTATE_AND_BOB|EF_STRONG_WEAPON|EF_GHOST) );
	}

	// recover some info, let players respawn and finally clear the snap structures
	for( ent = &game.edicts[0]; ENTNUM( ent ) < game.numentities; ent++ )
	{
		if( !GS_MatchPaused() )
		{
			// copy origin to old origin ( this old_origin is for snaps )
			if( !( ent->r.svflags & SVF_TRANSMITORIGIN2 ) )
				VectorCopy( ent->s.origin, ent->s.old_origin );

			G_CheckClientRespawnClick( ent );
		}

		if( GS_MatchPaused() )
			ent->s.sound = entity_sound_backup[ENTNUM( ent )];

		// clear the snap temp info
		memset( &ent->snap, 0, sizeof( ent->snap ) );
		if( ent->r.client && trap_GetClientState( PLAYERNUM( ent ) ) >= CS_SPAWNED )
		{
			memset( &ent->r.client->resp.snap, 0, sizeof( ent->r.client->resp.snap ) );

			// set race stats to invisible
			RS_clearHUDStats( ent->r.client ); // racesow - clear with our function
		}
	}

	g_snapStarted = false;
}
Пример #4
0
/*
* SV_RunThink
*
* Runs thinking code for this frame if necessary
*/
static void SV_RunThink( edict_t *ent ) {
	int64_t thinktime;

	thinktime = ent->nextThink;
	if( thinktime <= 0 ) {
		return;
	}
	if( thinktime > level.time ) {
		return;
	}

	ent->nextThink = 0;

	if( ISEVENTENTITY( &ent->s ) ) { // events do not think
		return;
	}

	G_CallThink( ent );
}
Пример #5
0
/*
* G_SnapFrame
* It's time to send a new snap, so set the world up for sending
*/
void G_SnapFrame( void )
{
	edict_t	*ent;
	game.realtime = trap_Milliseconds(); // level.time etc. might not be real time

	//others
	G_UpdateServerInfo();

	// exit level
	if( level.exitNow )
	{
		G_ExitLevel();
		return;
	}

	AITools_Frame(); //MbotGame //give think time to AI debug tools

	// finish snap
	G_SnapClients(); // build the playerstate_t structures for all players
	G_SnapEntities(); // add effects based on accumulated info along the frame

	// set entity bits (prepare entities for being sent in the snap)
	for( ent = &game.edicts[0]; ENTNUM( ent ) < game.numentities; ent++ )
	{
		if( ent->s.number != ENTNUM( ent ) )
		{
			if( developer->integer )
				G_Printf( "fixing ent->s.number (etype:%i, classname:%s)\n", ent->s.type, ent->classname ? ent->classname : "noclassname" );
			ent->s.number = ENTNUM( ent );
		}

		// temporary filter (Q2 system to ensure reliability)
		// ignore ents without visible models unless they have an effect
		if( !ent->r.inuse )
		{
			ent->r.svflags |= SVF_NOCLIENT;
			continue;
		}
		else if( ent->s.type >= ET_TOTAL_TYPES || ent->s.type < 0 )
		{
			if( developer->integer )
				G_Printf( "'G_SnapFrame': Inhibiting invalid entity type %i\n", ent->s.type );
			ent->r.svflags |= SVF_NOCLIENT;
			continue;
		}
		else if( !( ent->r.svflags & SVF_NOCLIENT ) && !ent->s.modelindex && !ent->s.effects 
			&& !ent->s.sound && !ISEVENTENTITY( &ent->s ) && !ent->s.light && !ent->r.client )
		{
			if( developer->integer )
				G_Printf( "'G_SnapFrame': fixing missing SVF_NOCLIENT flag (no effect)\n" );
			ent->r.svflags |= SVF_NOCLIENT;
			continue;
		}

		ent->s.effects &= ~EF_TAKEDAMAGE;
		if( ent->takedamage )
			ent->s.effects |= EF_TAKEDAMAGE;

		if( GS_MatchPaused() )
		{
			// when in timeout, we don't send entity sounds
			entity_sound_backup[ENTNUM( ent )] = ent->s.sound;
			ent->s.sound = 0;
		}
	}
}
Пример #6
0
/*
* G_EdictsAddSnapEffects
* add effects based on accumulated info along the server frame
*/
static void G_SnapEntities( void )
{
	edict_t *ent;
	int i;
	vec3_t dir, origin;

	for( i = 0, ent = &game.edicts[0]; i < game.numentities; i++, ent++ )
	{
		if( !ent->r.inuse || ( ent->r.svflags & SVF_NOCLIENT ) )
			continue;

		if( ent->s.type == ET_PARTICLES ) // particles use a special configuration
		{
			ent->s.frame = ent->particlesInfo.speed;
			ent->s.modelindex = ent->particlesInfo.shaderIndex;
			ent->s.modelindex2 = ent->particlesInfo.spread;
			ent->s.counterNum = ent->particlesInfo.time;
			ent->s.weapon = ent->particlesInfo.frequency;

			ent->s.effects = ent->particlesInfo.size & 0xFF;
			if( ent->particlesInfo.spherical )
				ent->s.effects |= ( 1<<8 );
			if( ent->particlesInfo.bounce )
				ent->s.effects |= ( 1<<9 );
			if( ent->particlesInfo.gravity )
				ent->s.effects |= ( 1<<10 );
			if( ent->particlesInfo.expandEffect )
				ent->s.effects |= ( 1<<11 );
			if( ent->particlesInfo.shrinkEffect )
				ent->s.effects |= ( 1<<11 );

			GClip_LinkEntity( ent );

			continue;
		}

		if( ent->s.type == ET_PLAYER || ent->s.type == ET_CORPSE )
		{
			// this is pretty hackish. We exploit the fact that 0.5 servers *do not* transmit
			// origin2/old_origin for ET_PLAYER/ET_CORPSE entities, and we use it for sending the player velocity
			if( !G_ISGHOSTING( ent ) )
			{
				ent->r.svflags |= SVF_TRANSMITORIGIN2;
				VectorCopy( ent->velocity, ent->s.origin2 );
			}
			else
				ent->r.svflags &= ~SVF_TRANSMITORIGIN2;
		}

		if( ISEVENTENTITY( ent ) || G_ISGHOSTING( ent ) || !ent->takedamage )
			continue;

		// types which can have accumulated damage effects
		if( ( ent->s.type == ET_GENERIC || ent->s.type == ET_PLAYER || ent->s.type == ET_CORPSE ) ) // doors don't bleed
		{
			// Until we get a proper damage saved effect, we accumulate both into the blood fx
			// so, at least, we don't send 2 entities where we can send one
			ent->snap.damage_taken += ent->snap.damage_saved;
			//ent->snap.damage_saved = 0;

			//spawn accumulated damage
			if( ent->snap.damage_taken && !( ent->flags & FL_GODMODE ) && HEALTH_TO_INT( ent->health ) > 0 )
			{
				edict_t *event;
				float damage = ent->snap.damage_taken;
				if( damage > 120 )
					damage = 120;

				VectorCopy( ent->snap.damage_dir, dir );
				VectorNormalize( dir );
				VectorAdd( ent->s.origin, ent->snap.damage_at, origin );

				if( ent->s.type == ET_PLAYER || ent->s.type == ET_CORPSE )
				{
					event = G_SpawnEvent( EV_BLOOD, DirToByte( dir ), origin );
					event->s.damage = HEALTH_TO_INT( damage );
					event->s.ownerNum = i; // set owner

					// ET_PLAYERS can also spawn sound events
					if( ent->s.type == ET_PLAYER )
					{
						// play an apropriate pain sound
						if( level.time >= ent->pain_debounce_time )
						{
							// see if it should pain for a FALL or for damage received
							if( ent->snap.damage_fall )
							{
								ent->pain_debounce_time = level.time + 200;
							}
							else if( !G_IsDead( ent ) )
							{
								if( ent->r.client->ps.inventory[POWERUP_SHELL] > 0 )
									G_AddEvent( ent, EV_PAIN, PAIN_WARSHELL, true );
								else if( ent->health <= 20 )
									G_AddEvent( ent, EV_PAIN, PAIN_20, true );
								else if( ent->health <= 35 )
									G_AddEvent( ent, EV_PAIN, PAIN_30, true );
								else if( ent->health <= 60 )
									G_AddEvent( ent, EV_PAIN, PAIN_60, true );
								else
									G_AddEvent( ent, EV_PAIN, PAIN_100, true );

								ent->pain_debounce_time = level.time + 400;
							}
						}
					}
				}
				else
				{
					event = G_SpawnEvent( EV_SPARKS, DirToByte( dir ), origin );
					event->s.damage = HEALTH_TO_INT( damage );
				}
			}
		}
	}
}