/* * G_SpawnQueue_AddClient */ void G_SpawnQueue_AddClient( edict_t *ent ) { g_teamspawnqueue_t *queue; int i; if( !ent || !ent->r.client ) return; if( ENTNUM( ent ) <= 0 || ENTNUM( ent ) > gs.maxclients ) return; if( ent->r.client->team < TEAM_SPECTATOR|| ent->r.client->team >= GS_MAX_TEAMS ) return; queue = &g_spawnQueues[ent->r.client->team]; for( i = queue->start; i < queue->head; i++ ) { if( queue->list[i % MAX_CLIENTS] == ENTNUM( ent ) ) return; } G_SpawnQueue_RemoveClient( ent ); queue->list[queue->head % MAX_CLIENTS] = ENTNUM( ent ); queue->head++; if( queue->spectate_team ) G_ChasePlayer( ent, NULL, true, 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; }
/* * G_Client_DeadView */ static void G_Client_DeadView( edict_t *ent ) { edict_t *body; gclient_t *client; trace_t trace; client = ent->r.client; // find the body for( body = game.edicts + gs.maxclients; ENTNUM( body ) < gs.maxclients + BODY_QUEUE_SIZE + 1; body++ ) { if( !body->r.inuse || body->r.svflags & SVF_NOCLIENT ) continue; if( body->activator == ent ) // this is our body break; } if( body->activator != ent ) { // ran all the list and didn't find our body return; } // move us to body position VectorCopy( body->s.origin, ent->s.origin ); VectorCopy( body->s.origin, ent->s.old_origin ); ent->s.teleported = qtrue; client->ps.viewangles[ROLL] = 0; client->ps.viewangles[PITCH] = 0; // see if our killer is still in view if( body->enemy && ( body->enemy != ent ) ) { G_Trace( &trace, ent->s.origin, vec3_origin, vec3_origin, body->enemy->s.origin, body, MASK_OPAQUE ); if( trace.fraction != 1.0f ) { body->enemy = NULL; } else { client->ps.viewangles[YAW] = LookAtKillerYAW( ent, NULL, body->enemy ); } } else { // nobody killed us, so just circle around the body ? } G_ProjectThirdPersonView( ent->s.origin, client->ps.viewangles, body ); VectorCopy( client->ps.viewangles, ent->s.angles ); VectorCopy( ent->s.origin, client->ps.pmove.origin ); VectorClear( client->ps.pmove.velocity ); GS_SnapPosition( client->ps.pmove.origin, ent->r.mins, ent->r.maxs, ENTNUM( ent ), 0 ); }
static unsigned int G_FindPointedPlayer( edict_t *self ) { trace_t trace; int i, j, bestNum = 0; vec3_t boxpoints[8]; float value, dist, value_best = 0.90f; // if nothing better is found, print nothing edict_t *other; vec3_t vieworg, dir, viewforward; if( G_IsDead( self ) ) { return 0; } // we can't handle the thirdperson modifications in server side :/ VectorSet( vieworg, self->r.client->ps.pmove.origin[0], self->r.client->ps.pmove.origin[1], self->r.client->ps.pmove.origin[2] + self->r.client->ps.viewheight ); AngleVectors( self->r.client->ps.viewangles, viewforward, NULL, NULL ); for( i = 0; i < gs.maxclients; i++ ) { other = PLAYERENT( i ); if( !other->r.inuse ) { continue; } if( !other->r.client ) { continue; } if( other == self ) { continue; } if( !other->r.solid || ( other->r.svflags & SVF_NOCLIENT ) ) { continue; } VectorSubtract( other->s.origin, self->s.origin, dir ); dist = VectorNormalize2( dir, dir ); if( dist > 1000 ) { continue; } value = DotProduct( dir, viewforward ); if( value > value_best ) { BuildBoxPoints( boxpoints, other->s.origin, tv( 4, 4, 4 ), tv( 4, 4, 4 ) ); for( j = 0; j < 8; j++ ) { G_Trace( &trace, vieworg, vec3_origin, vec3_origin, boxpoints[j], self, MASK_SHOT | MASK_OPAQUE ); if( trace.ent && trace.ent == ENTNUM( other ) ) { value_best = value; bestNum = ENTNUM( other ); } } } } return bestNum; }
static int G_Teams_CompareMembers( const void *a, const void *b ) { edict_t *edict_a = game.edicts + *(int *)a; edict_t *edict_b = game.edicts + *(int *)b; int score_a = edict_a->r.client->level.stats.score; int score_b = edict_b->r.client->level.stats.score; int result = ( level.gametype.inverseScore ? -1 : 1 ) * ( score_b - score_a ); if (!result) result = Q_stricmp( edict_a->r.client->netname, edict_b->r.client->netname ); if (!result) result = ENTNUM( edict_a ) - ENTNUM( edict_b ); return result; }
void G_ScoreboardMessage_AddChasers( int entnum, int entnum_self ) { char entry[MAX_TOKEN_CHARS]; int i; edict_t *e; size_t len; len = strlen( scoreboardString ); if( !len ) return; // add personal spectators Q_strncpyz( entry, "&y ", sizeof( entry ) ); ADD_SCOREBOARD_ENTRY( scoreboardString, len, entry ); for( i = 0; i < teamlist[TEAM_SPECTATOR].numplayers; i++ ) { e = game.edicts + teamlist[TEAM_SPECTATOR].playerIndices[i]; if( ENTNUM( e ) == entnum_self ) continue; if( e->r.client->connecting || trap_GetClientState( PLAYERNUM( e ) ) < CS_SPAWNED ) continue; if( !e->r.client->resp.chase.active || e->r.client->resp.chase.target != entnum ) continue; Q_snprintfz( entry, sizeof( entry ), "%i ", PLAYERNUM( e ) ); ADD_SCOREBOARD_ENTRY( scoreboardString, len, entry ); } }
//Sunflower spiral with Fibonacci numbers static void G_Fire_SunflowerPattern( edict_t *self, vec3_t start, vec3_t dir, int *seed, int count, int hspread, int vspread, int range, float damage, int kick, int stun, int dflags, int mod, int timeDelta ) { int i; float r; float u; float fi; trace_t trace; for( i = 0; i < count; i++ ) { fi = i * 2.4; //magic value creating Fibonacci numbers r = cos( (float)*seed + fi ) * hspread * sqrt(fi); u = sin( (float)*seed + fi ) * vspread * sqrt(fi); GS_TraceBullet( &trace, start, dir, r, u, range, ENTNUM( self ), timeDelta ); if( trace.ent != -1 ) { if( game.edicts[trace.ent].takedamage ) { G_Damage( &game.edicts[trace.ent], self, self, dir, dir, trace.endpos, damage, kick, stun, dflags, mod ); } else { if( !( trace.surfFlags & SURF_NOIMPACT ) ) { } } } } }
void G_PlayerAward( edict_t *ent, const char *awardMsg ) { edict_t *other; char cmd[MAX_STRING_CHARS]; gameaward_t *ga; int i, size; score_stats_t *stats; if( !awardMsg || !awardMsg[0] || !ent->r.client ) return; Q_snprintfz( cmd, sizeof( cmd ), "aw \"%s\"", awardMsg ); trap_GameCmd( ent, cmd ); if( dedicated->integer ) G_Printf( "%s", COM_RemoveColorTokens( va( "%s receives a '%s' award.\n", ent->r.client->netname, awardMsg ) ) ); ent->r.client->level.stats.awards++; teamlist[ent->s.team].stats.awards++; G_Gametype_ScoreEvent( ent->r.client, "award", awardMsg ); stats = &ent->r.client->level.stats; if( !stats->awardAllocator ) stats->awardAllocator = LinearAllocator( sizeof( gameaward_t ), 0, _G_LevelMalloc, _G_LevelFree ); // ch : this doesnt work for race right? if( GS_MatchState() == MATCH_STATE_PLAYTIME || GS_MatchState() == MATCH_STATE_POSTMATCH ) { // ch : we store this locally to send to MM // first check if we already have this one on the clients list size = LA_Size( stats->awardAllocator ); ga = NULL; for( i = 0; i < size; i++ ) { ga = ( gameaward_t * )LA_Pointer( stats->awardAllocator, i ); if( !strncmp( ga->name, awardMsg, sizeof(ga->name)-1 ) ) break; } if( i >= size ) { ga = ( gameaward_t * )LA_Alloc( stats->awardAllocator ); memset( ga, 0, sizeof(*ga) ); ga->name = G_RegisterLevelString( awardMsg ); } if( ga ) ga->count++; } // add it to every player who's chasing this player for( other = game.edicts + 1; PLAYERNUM( other ) < gs.maxclients; other++ ) { if( !other->r.client || !other->r.inuse || !other->r.client->resp.chase.active ) continue; if( other->r.client->resp.chase.target == ENTNUM( ent ) ) trap_GameCmd( other, cmd ); } }
/* * GClip_FindBoxInRadius * Returns entities that have their boxes within a spherical area */ edict_t *GClip_FindBoxInRadius4D( edict_t *from, vec3_t org, float rad, int timeDelta ) { int i, j; c4clipedict_t *check; vec3_t mins, maxs; int fromNum; if( !from ) from = world; fromNum = ENTNUM( from ) + 1; for( i = fromNum; i < game.numentities; i++ ) { if( !game.edicts[i].r.inuse ) continue; check = GClip_GetClipEdictForDeltaTime( i, timeDelta ); if( !check->r.inuse ) continue; if( check->r.solid == SOLID_NOT ) continue; // make absolute mins and maxs for( j = 0; j < 3; j++ ) { mins[j] = check->s.origin[j] + check->r.mins[j]; maxs[j] = check->s.origin[j] + check->r.maxs[j]; } if( !BoundsAndSphereIntersect( mins, maxs, org, rad ) ) continue; return &game.edicts[i]; // return realtime entity } return NULL; }
/* * AI_InitEntitiesData */ void AI_InitEntitiesData( void ) { int newlinks, newjumplinks; edict_t *ent; if( !nav.num_nodes ) { if( g_numbots->integer ) trap_Cvar_Set( "g_numbots", "0" ); return; } // create nodes for navigable map entities ( must happen after finding teams ) for( ent = game.edicts + 1 + gs.maxclients; ENTNUM( ent ) < game.numentities; ent++ ) AI_AddNavigableEntity( ent ); // add all clients to goalEntities so they can be tracked as enemies for( ent = game.edicts + 1; PLAYERNUM( ent ) < gs.maxclients; ent++ ) AI_AddGoalEntity( ent ); // link all newly added nodes newlinks = AI_LinkServerNodes( nav.serverNodesStart ); newjumplinks = AI_LinkCloseNodes_JumpPass( nav.serverNodesStart ); if( developer->integer ) { G_Printf( " : added nodes:%i.\n", nav.num_nodes - nav.serverNodesStart ); G_Printf( " : total nodes:%i.\n", nav.num_nodes ); G_Printf( " : added links:%i.\n", newlinks ); G_Printf( " : added jump links:%i.\n", newjumplinks ); } G_Printf( " : AI Navigation Initialized.\n" ); nav.loaded = qtrue; }
/* * W_Touch_Plasma */ static void W_Touch_Plasma( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags ) { int hitType; vec3_t dir; if( surfFlags & SURF_NOIMPACT ) { G_FreeEdict( ent ); return; } hitType = G_Projectile_HitStyle( ent, other ); if( hitType == PROJECTILE_TOUCH_NOT ) return; if( other->takedamage ) { VectorNormalize2( ent->velocity, dir ); if( hitType == PROJECTILE_TOUCH_DIRECTSPLASH ) // use hybrid direction from splash and projectile { G_SplashFrac4D( ENTNUM( other ), ent->s.origin, ent->projectileInfo.radius, dir, NULL, NULL, ent->timeDelta ); } else { VectorNormalize2( ent->velocity, dir ); } G_Damage( other, ent, ent->r.owner, dir, ent->velocity, ent->s.origin, ent->projectileInfo.maxDamage, ent->projectileInfo.maxKnockback, ent->projectileInfo.stun, DAMAGE_KNOCKBACK_SOFT, ent->style ); } W_Plasma_Explosion( ent, other, plane, surfFlags ); }
/* * 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; } }
void G_Fire_SpiralPattern( edict_t *self, vec3_t start, vec3_t dir, int *seed, int count, int spread, int range, float damage, int kick, int stun, int dflags, int mod, int timeDelta ) { int i; float r; float u; trace_t trace; for( i = 0; i < count; i++ ) { r = cos( *seed + i ) * spread * i; u = sin( *seed + i ) * spread * i; GS_TraceBullet( &trace, start, dir, r, u, range, ENTNUM( self ), timeDelta ); if( trace.ent != -1 ) { if( game.edicts[trace.ent].takedamage ) { G_Damage( &game.edicts[trace.ent], self, self, dir, dir, trace.endpos, damage, kick, stun, dflags, mod ); } else { if( !( trace.surfFlags & SURF_NOIMPACT ) ) { } } } } }
/* * W_Touch_Rocket */ static void W_Touch_Rocket( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags ) { int mod_splash; vec3_t dir; int hitType; if( surfFlags & SURF_NOIMPACT ) { G_FreeEdict( ent ); return; } hitType = G_Projectile_HitStyle( ent, other ); if( hitType == PROJECTILE_TOUCH_NOT ) { return; } if( other->takedamage ) { int directHitDamage = ent->projectileInfo.maxDamage; VectorNormalize2( ent->velocity, dir ); if( hitType == PROJECTILE_TOUCH_DIRECTSPLASH ) { // use hybrid direction from splash and projectile G_SplashFrac4D( ENTNUM( other ), ent->s.origin, ent->projectileInfo.radius, dir, NULL, NULL, ent->timeDelta ); } else { VectorNormalize2( ent->velocity, dir ); if( hitType == PROJECTILE_TOUCH_DIRECTAIRHIT ) { directHitDamage += DIRECTAIRTHIT_DAMAGE_BONUS; } else if( hitType == PROJECTILE_TOUCH_DIRECTHIT ) { directHitDamage += DIRECTHIT_DAMAGE_BONUS; } } G_Damage( other, ent, ent->r.owner, dir, ent->velocity, ent->s.origin, directHitDamage, ent->projectileInfo.maxKnockback, ent->projectileInfo.stun, 0, ent->style ); } if( ent->s.effects & EF_STRONG_WEAPON ) { mod_splash = MOD_ROCKET_SPLASH_S; } else { mod_splash = MOD_ROCKET_SPLASH_W; } G_RadiusDamage( ent, ent->r.owner, plane, other, mod_splash ); // spawn the explosion if( !( surfFlags & SURF_NOIMPACT ) ) { edict_t *event; vec3_t explosion_origin; VectorMA( ent->s.origin, -0.02, ent->velocity, explosion_origin ); event = G_SpawnEvent( EV_ROCKET_EXPLOSION, DirToByte( plane ? plane->normal : NULL ), explosion_origin ); event->s.firemode = ( ent->s.effects & EF_STRONG_WEAPON ) ? FIRE_MODE_STRONG : FIRE_MODE_WEAK; event->s.weapon = ( ( ent->projectileInfo.radius * 1 / 8 ) > 255 ) ? 255 : ( ent->projectileInfo.radius * 1 / 8 ); } // free the rocket at next frame G_FreeEdict( ent ); }
/* * W_Fire_Lasergun */ edict_t *W_Fire_Lasergun( edict_t *self, vec3_t start, vec3_t angles, float damage, int knockback, int stun, int range, int mod, int timeDelta ) { edict_t *laser; qboolean newLaser; trace_t tr; vec3_t dir; if( GS_Instagib() ) damage = 9999; laser = _FindOrSpawnLaser( self, ET_LASERBEAM, &newLaser ); if( newLaser ) { // the quad start sound is added from the server if( self->r.client && self->r.client->ps.inventory[POWERUP_QUAD] > 0 ) G_Sound( self, CHAN_AUTO, trap_SoundIndex( S_QUAD_FIRE ), ATTN_NORM ); } laser_damage = damage; laser_knockback = knockback; laser_stun = stun; laser_attackerNum = ENTNUM( self ); laser_mod = mod; laser_missed = qtrue; GS_TraceLaserBeam( &tr, start, angles, range, ENTNUM( self ), timeDelta, _LaserImpact ); laser->r.svflags |= SVF_FORCEOWNER; VectorCopy( start, laser->s.origin ); AngleVectors( angles, dir, NULL, NULL ); VectorMA( laser->s.origin, range, dir, laser->s.origin2 ); laser->think = G_Laser_Think; laser->nextThink = level.time + 100; if( laser_missed && self->r.client ) G_AwardPlayerMissedLasergun( self, mod ); // calculate laser's mins and maxs for linkEntity G_SetBoundsForSpanEntity( laser, 8 ); GClip_LinkEntity( laser ); return laser; }
/* * G_Teams_InvitePlayer */ static void G_Teams_InvitePlayer( int team, edict_t *ent ) { int i; if( team < TEAM_PLAYERS || team >= GS_MAX_TEAMS ) return; if( !ent->r.inuse || !ent->r.client ) return; for( i = 0; teamlist[team].invited[i] && i < MAX_CLIENTS; i++ ) { if( teamlist[team].invited[i] == ENTNUM( ent ) ) return; } teamlist[team].invited[i] = ENTNUM( ent ); }
/* * W_Fire_Bullet */ void W_Fire_Bullet( edict_t *self, vec3_t start, vec3_t angles, int seed, int range, int spread, float damage, int knockback, int stun, int mod, int timeDelta ) { vec3_t dir; edict_t *event; float r, u; double alpha, s; trace_t trace; int dmgflags = DAMAGE_STUN_CLAMP|DAMAGE_KNOCKBACK_SOFT; if( GS_Instagib() ) damage = 9999; AngleVectors( angles, dir, NULL, NULL ); // send the event event = G_SpawnEvent( EV_FIRE_BULLET, seed, start ); event->s.ownerNum = ENTNUM( self ); event->r.svflags = SVF_TRANSMITORIGIN2; VectorScale( dir, 4096, event->s.origin2 ); // DirToByte is too inaccurate event->s.weapon = WEAP_MACHINEGUN; if( mod == MOD_MACHINEGUN_S ) event->s.weapon |= EV_INVERSE; // circle shape alpha = M_PI * Q_crandom( &seed ); // [-PI ..+PI] s = fabs( Q_crandom( &seed ) ); // [0..1] r = s * cos( alpha ) * spread; u = s * sin( alpha ) * spread; GS_TraceBullet( &trace, start, dir, r, u, range, ENTNUM( self ), timeDelta ); if( trace.ent != -1 ) { if( game.edicts[trace.ent].takedamage ) { G_Damage( &game.edicts[trace.ent], self, self, dir, dir, trace.endpos, damage, knockback, stun, dmgflags, mod ); } else { if( !( trace.surfFlags & SURF_NOIMPACT ) ) { } } } }
nav_ents_t *AI_GetGoalentForEnt( edict_t *target ) { int entnum; if( !target ) return NULL; entnum = ENTNUM( target ); return nav.entsGoals[entnum]; }
/* * W_Fire_LinearProjectile - Spawn a generic linear projectile without a model, touch func, sound nor mod */ static edict_t *W_Fire_LinearProjectile( edict_t *self, vec3_t start, vec3_t angles, int speed, float damage, int minKnockback, int maxKnockback, int stun, int minDamage, int radius, int timeout, int timeDelta ) { edict_t *projectile; vec3_t dir; projectile = G_Spawn(); VectorCopy( start, projectile->s.origin ); VectorCopy( start, projectile->s.old_origin ); VectorCopy( start, projectile->olds.origin ); VectorCopy( angles, projectile->s.angles ); AngleVectors( angles, dir, NULL, NULL ); VectorScale( dir, speed, projectile->velocity ); GS_SnapVelocity( projectile->velocity ); projectile->movetype = MOVETYPE_LINEARPROJECTILE; projectile->s.linearProjectile = qtrue; projectile->r.solid = SOLID_YES; projectile->r.clipmask = ( !GS_RaceGametype() ) ? MASK_SHOT : MASK_SOLID; projectile->r.svflags = SVF_PROJECTILE; // enable me when drawing exception is added to cgame projectile->r.svflags |= SVF_TRANSMITORIGIN2; VectorClear( projectile->r.mins ); VectorClear( projectile->r.maxs ); projectile->s.modelindex = 0; projectile->r.owner = self; projectile->s.ownerNum = ENTNUM( self ); projectile->touch = W_Touch_Projectile; //generic one. Should be replaced after calling this func projectile->nextThink = level.time + timeout; projectile->think = G_FreeEdict; projectile->classname = NULL; // should be replaced after calling this func. projectile->style = 0; projectile->s.sound = 0; projectile->timeStamp = level.time; projectile->s.linearProjectileTimeStamp = game.serverTime; projectile->timeDelta = timeDelta; projectile->projectileInfo.minDamage = min( minDamage, damage ); projectile->projectileInfo.maxDamage = damage; projectile->projectileInfo.minKnockback = min( minKnockback, maxKnockback ); projectile->projectileInfo.maxKnockback = maxKnockback; projectile->projectileInfo.stun = stun; projectile->projectileInfo.radius = radius; GClip_LinkEntity( projectile ); // update some data required for the transmission VectorCopy( projectile->velocity, projectile->s.linearProjectileVelocity ); projectile->s.team = self->s.team; projectile->s.modelindex2 = ( abs( timeDelta ) > 255 ) ? 255 : (unsigned int)abs( timeDelta ); return projectile; }
/* * G_Match_RemoveAllProjectiles */ void G_Match_RemoveAllProjectiles( void ) { edict_t *ent; for( ent = game.edicts + gs.maxclients; ENTNUM( ent ) < game.numentities; ent++ ) { if( ent->r.inuse && !ent->r.client && ent->r.svflags & SVF_PROJECTILE && ent->r.solid != SOLID_NOT ) { G_FreeEdict( ent ); } } }
/* * G_RunFrame * Advances the world */ void G_RunFrame( unsigned int msec, unsigned int serverTime ) { G_CheckCvars(); game.localTime = time( NULL ); game.serverTime = serverTime; G_UpdateFrameTime( msec ); if( !g_snapStarted ) G_StartFrameSnap(); G_CallVotes_Think(); // "freeze" match clock if( GS_MatchWaiting() || GS_MatchPaused() ) { gs.gameState.longstats[GAMELONG_MATCHSTART] += msec; } if( GS_MatchPaused() ) { edict_t *ent; // "freeze" linear projectiles for( ent = game.edicts + gs.maxclients; ENTNUM( ent ) < game.numentities; ent++ ) { if( ent->s.linearProjectile ) ent->s.linearProjectileTimeStamp += msec; } G_RunClients(); G_RunGametype(); G_LevelGarbageCollect(); return; } level.framenum++; level.time += msec; level.think_client_entity = G_GetNextThinkClient( level.think_client_entity ); G_SpawnQueue_Think(); // run the world G_asCallMapPreThink(); G_RunClients(); G_RunEntities(); G_RunGametype(); G_asCallMapPostThink(); GClip_BackUpCollisionFrame(); G_LevelGarbageCollect(); }
/* * W_Touch_Grenade */ static void W_Touch_Grenade( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags ) { int hitType; vec3_t dir; if( surfFlags & SURF_NOIMPACT ) { G_FreeEdict( ent ); return; } hitType = G_Projectile_HitStyle( ent, other ); if( hitType == PROJECTILE_TOUCH_NOT ) return; // don't explode on doors and plats that take damage if( !other->takedamage || ISBRUSHMODEL( other->s.modelindex ) ) { G_AddEvent( ent, EV_GRENADE_BOUNCE, ( ent->s.effects & EF_STRONG_WEAPON ) ? FIRE_MODE_STRONG : FIRE_MODE_WEAK, true ); return; } if( other->takedamage ) { int directHitDamage = ent->projectileInfo.maxDamage; VectorNormalize2( ent->velocity, dir ); if( hitType == PROJECTILE_TOUCH_DIRECTSPLASH ) // use hybrid direction from splash and projectile { G_SplashFrac4D( ENTNUM( other ), ent->s.origin, ent->projectileInfo.radius, dir, NULL, NULL, ent->timeDelta ); } else { VectorNormalize2( ent->velocity, dir ); // no direct hit bonuses for grenades /* if( hitType == PROJECTILE_TOUCH_DIRECTAIRHIT ) directHitDamage += DIRECTAIRTHIT_DAMAGE_BONUS; else if( hitType == PROJECTILE_TOUCH_DIRECTHIT ) directHitDamage += DIRECTHIT_DAMAGE_BONUS; */ } G_Damage( other, ent, ent->r.owner, dir, ent->velocity, ent->s.origin, directHitDamage, ent->projectileInfo.maxKnockback, ent->projectileInfo.stun, 0, ent->style ); } ent->enemy = other; W_Grenade_ExplodeDir( ent, plane ? plane->normal : NULL ); }
static edict_t *G_ClosestFlagBase( edict_t *ent ) { int i; edict_t *t, *best; float dist, best_dist; static qboolean firstTime = qtrue; static unsigned int lastLevelSpawnCount; static edict_t *flagBases[GS_MAX_TEAMS]; // store pointers to flag bases if called for the first time in this level spawn if( firstTime || lastLevelSpawnCount != game.levelSpawnCount ) { for( t = game.edicts + 1 + gs.maxclients; ENTNUM( t ) < game.numentities; t++ ) { if( t->s.type != ET_FLAG_BASE ) continue; flagBases[t->s.team] = t; } // ok, remember last time we were called firstTime = qfalse; lastLevelSpawnCount = game.levelSpawnCount; } best = NULL; best_dist = 9999999; // find the closest flag base starting from TEAM_ALPHA for( i = TEAM_ALPHA; i < GS_MAX_TEAMS; i++ ) { t = flagBases[i]; if( !t ) continue; // if equally distant from two bases, consider this item neutral dist = Distance( ent->s.origin, t->s.origin ); if( best && fabs( dist - best_dist ) < 10 ) { best = NULL; break; } if( dist < best_dist ) { best_dist = dist; best = t; } } return best; }
/* * W_Fire_Bullet */ void W_Fire_Bullet( edict_t *self, vec3_t start, vec3_t fv, vec3_t rv, vec3_t uv, int seed, int range, int hspread, int vspread, float damage, int knockback, int stun, int mod, int timeDelta ) { edict_t *event; float r, u; double alpha, s; trace_t trace; int dmgflags = DAMAGE_STUN_CLAMP | DAMAGE_KNOCKBACK_SOFT; if( GS_Instagib() ) { damage = 9999; } // send the event event = G_SpawnEvent( EV_FIRE_BULLET, seed, start ); event->s.ownerNum = ENTNUM( self ); VectorCopy( fv, event->s.origin2 ); VectorCopy( rv, event->s.origin3 ); event->s.weapon = WEAP_MACHINEGUN; event->s.firemode = ( mod == MOD_MACHINEGUN_S ) ? FIRE_MODE_STRONG : FIRE_MODE_WEAK; // circle shape alpha = M_PI * Q_crandom( &seed ); // [-PI ..+PI] s = fabs( Q_crandom( &seed ) ); // [0..1] r = s * cos( alpha ) * hspread; u = s * sin( alpha ) * vspread; GS_TraceBullet( &trace, start, fv, rv, uv, r, u, range, ENTNUM( self ), timeDelta ); if( trace.ent != -1 ) { if( game.edicts[trace.ent].takedamage ) { G_Damage( &game.edicts[trace.ent], self, self, fv, fv, trace.endpos, damage, knockback, stun, dmgflags, mod ); } else { if( !( trace.surfFlags & SURF_NOIMPACT ) ) { } } } }
/* * W_Touch_GunbladeBlast */ static void W_Touch_GunbladeBlast( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags ) { vec3_t dir; int hitType; if( surfFlags & SURF_NOIMPACT ) { G_FreeEdict( ent ); return; } hitType = G_Projectile_HitStyle( ent, other ); if( hitType == PROJECTILE_TOUCH_NOT ) return; if( other->takedamage ) { VectorNormalize2( ent->velocity, dir ); if( hitType == PROJECTILE_TOUCH_DIRECTSPLASH ) // use hybrid direction from splash and projectile { G_SplashFrac4D( ENTNUM( other ), ent->s.origin, ent->projectileInfo.radius, dir, NULL, NULL, ent->timeDelta ); } else { VectorNormalize2( ent->velocity, dir ); } G_Damage( other, ent, ent->r.owner, dir, ent->velocity, ent->s.origin, ent->projectileInfo.maxDamage, ent->projectileInfo.maxKnockback, ent->projectileInfo.stun, 0, ent->style ); } G_RadiusDamage( ent, ent->r.owner, plane, other, MOD_GUNBLADE_S ); // add explosion event if( ( !other->takedamage || ISBRUSHMODEL( other->s.modelindex ) ) ) { edict_t *event; event = G_SpawnEvent( EV_GUNBLADEBLAST_IMPACT, DirToByte( plane ? plane->normal : NULL ), ent->s.origin ); event->s.weapon = ( ( ent->projectileInfo.radius * 1/8 ) > 127 ) ? 127 : ( ent->projectileInfo.radius * 1/8 ); event->s.skinnum = ( ( ent->projectileInfo.maxKnockback * 1/8 ) > 255 ) ? 255 : ( ent->projectileInfo.maxKnockback * 1/8 ); } // free at next frame G_FreeEdict( ent ); }
/* * G_Trace * * Moves the given mins/maxs volume through the world from start to end. * * Passedict and edicts owned by passedict are explicitly not checked. * ------------------------------------------------------------------ * mins and maxs are relative * if the entire move stays in a solid volume, trace.allsolid will be set, * trace.startsolid will be set, and trace.fraction will be 0 * if the starting point is in a solid, it will be allowed to move out * to an open area * passedict is explicitly excluded from clipping checks (normally NULL) */ static void GClip_Trace( trace_t *tr, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *passedict, int contentmask, int timeDelta ) { moveclip_t clip; if( !tr ) return; if( !mins ) mins = vec3_origin; if( !maxs ) maxs = vec3_origin; if( passedict == world ) { memset( tr, 0, sizeof( trace_t ) ); tr->fraction = 1; tr->ent = -1; } else { // clip to world trap_CM_TransformedBoxTrace( tr, start, end, mins, maxs, NULL, contentmask, NULL, NULL ); tr->ent = tr->fraction < 1.0 ? world->s.number : -1; if( tr->fraction == 0 ) return; // blocked by the world } memset( &clip, 0, sizeof( moveclip_t ) ); clip.trace = tr; clip.contentmask = contentmask; clip.start = start; clip.end = end; clip.mins = mins; clip.maxs = maxs; clip.passent = passedict ? ENTNUM( passedict ) : -1; VectorCopy( mins, clip.mins2 ); VectorCopy( maxs, clip.maxs2 ); // create the bounding box of the entire move GClip_TraceBounds( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs ); // clip to other solid entities GClip_ClipMoveToEntities( &clip, timeDelta ); }
/* * TVM_ClientEndSnapFrame * * Called for each player at the end of the server frame * and right after spawning */ void TVM_ClientEndSnapFrame( edict_t *ent ) { edict_t *spec; tvm_relay_t *relay = ent->relay; assert( ent && ent->local && ent->r.client && !ent->r.client->chase.active ); if( relay->playernum < 0 ) spec = NULL; else spec = relay->edicts + relay->playernum + 1; if( relay->frame.valid && !relay->frame.multipov ) { assert( spec ); ent->r.client->ps = spec->r.client->ps; ent->r.client->ps.pmove.pm_type = PM_CHASECAM; ent->r.client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; ent->r.client->ps.POVnum = PLAYERNUM( spec ) + 1; ent->s = spec->s; ent->s.number = ENTNUM( ent ); return; } if( spec && spec->r.inuse && spec->r.client ) { memcpy( ent->r.client->ps.stats, spec->r.client->ps.stats, sizeof( ent->r.client->ps.stats ) ); memcpy( ent->r.client->ps.inventory, spec->r.client->ps.inventory, sizeof( ent->r.client->ps.inventory ) ); } else { memset( ent->r.client->ps.stats, 0, sizeof( ent->r.client->ps.stats ) ); memset( ent->r.client->ps.inventory, 0, sizeof( ent->r.client->ps.inventory ) ); } ent->r.client->ps.viewheight = ent->viewheight; if( relay->playernum < 0 ) ent->r.client->ps.POVnum = 255; // FIXME else ent->r.client->ps.POVnum = relay->playernum + 1; if( TVM_ClientIsZoom( ent ) ) ent->r.client->ps.fov = ent->r.client->pers.zoomfov; else ent->r.client->ps.fov = ent->r.client->pers.fov; }
/* * G_SpawnQueue_RemoveClient - Check all queues for this client and remove it */ void G_SpawnQueue_RemoveClient( edict_t *ent ) { g_teamspawnqueue_t *queue; int i, team; if( !ent->r.client ) return; for( team = TEAM_SPECTATOR; team < GS_MAX_TEAMS; team++ ) { queue = &g_spawnQueues[team]; for( i = queue->start; i < queue->head; i++ ) { if( queue->list[i % MAX_CLIENTS] == ENTNUM( ent ) ) queue->list[i % MAX_CLIENTS] = -1; } } }
/* * G_Teams_PlayerIsInvited */ static bool G_Teams_PlayerIsInvited( int team, edict_t *ent ) { int i; if( team < TEAM_PLAYERS || team >= GS_MAX_TEAMS ) return false; if( !ent->r.inuse || !ent->r.client ) return false; for( i = 0; teamlist[team].invited[i] && i < MAX_CLIENTS; i++ ) { if( teamlist[team].invited[i] == ENTNUM( ent ) ) return true; } return false; }
/* * W_Fire_Electrobolt_Weak */ edict_t *W_Fire_Electrobolt_Weak( edict_t *self, vec3_t start, vec3_t angles, float speed, float damage, int minKnockback, int maxKnockback, int stun, int timeout, int mod, int timeDelta ) { edict_t *bolt; if( GS_Instagib() ) damage = 9999; // projectile, weak mode bolt = W_Fire_LinearProjectile( self, start, angles, speed, damage, minKnockback, maxKnockback, stun, 0, 0, timeout, timeDelta ); bolt->s.modelindex = trap_ModelIndex( PATH_ELECTROBOLT_WEAK_MODEL ); bolt->s.type = ET_ELECTRO_WEAK; //add particle trail and light bolt->s.ownerNum = ENTNUM( self ); bolt->touch = W_Touch_Bolt; bolt->classname = "bolt"; bolt->style = mod; bolt->s.effects &= ~EF_STRONG_WEAPON; return bolt; }