void SP_trigger_push( edict_t *self ) { InitTrigger( self ); if( st.noise && Q_stricmp( st.noise, "default" ) ) { if( Q_stricmp( st.noise, "silent" ) ) { self->moveinfo.sound_start = trap_SoundIndex( st.noise ); G_PureSound( st.noise ); } } else self->moveinfo.sound_start = trap_SoundIndex( S_JUMPPAD ); // gameteam field from editor if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) self->s.team = st.gameteam; else self->s.team = TEAM_SPECTATOR; self->touch = trigger_push_touch; self->think = trigger_push_setup; self->nextThink = level.time + 1; self->r.svflags &= ~SVF_NOCLIENT; self->s.type = ET_PUSH_TRIGGER; self->r.svflags |= SVF_TRANSMITORIGIN2; GClip_LinkEntity( self ); // ET_PUSH_TRIGGER gets exceptions at linking so it's added for prediction self->timeStamp = level.time; if( !self->wait ) self->wait = MIN_TRIGGER_PUSH_REBOUNCE_TIME * 0.001f; }
/* * W_Fire_Rocket */ edict_t *W_Fire_Rocket( 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 mod, int timeDelta ) { edict_t *rocket; if( GS_Instagib() ) damage = 9999; rocket = W_Fire_LinearProjectile( self, start, angles, speed, damage, minKnockback, maxKnockback, stun, minDamage, radius, timeout, timeDelta ); rocket->s.type = ET_ROCKET; //rocket trail sfx if( mod == MOD_ROCKET_S ) { rocket->s.modelindex = trap_ModelIndex( PATH_ROCKET_STRONG_MODEL ); rocket->s.effects |= EF_STRONG_WEAPON; rocket->s.sound = trap_SoundIndex( S_WEAPON_ROCKET_S_FLY ); } else { rocket->s.modelindex = trap_ModelIndex( PATH_ROCKET_WEAK_MODEL ); rocket->s.effects &= ~EF_STRONG_WEAPON; rocket->s.sound = trap_SoundIndex( S_WEAPON_ROCKET_W_FLY ); } rocket->touch = W_Touch_Rocket; rocket->think = G_FreeEdict; rocket->classname = "rocket"; rocket->style = mod; return rocket; }
/* * W_Fire_Plasma */ edict_t *W_Fire_Plasma( edict_t *self, vec3_t start, vec3_t angles, float damage, int minKnockback, int maxKnockback, int stun, int minDamage, int radius, int speed, int timeout, int mod, int timeDelta ) { edict_t *plasma; if( GS_Instagib() ) damage = 9999; plasma = W_Fire_LinearProjectile( self, start, angles, speed, damage, minKnockback, maxKnockback, stun, minDamage, radius, timeout, timeDelta ); plasma->s.type = ET_PLASMA; plasma->classname = "plasma"; plasma->style = mod; plasma->think = W_Think_Plasma; plasma->touch = W_AutoTouch_Plasma; plasma->nextThink = level.time + 1; plasma->timeout = level.time + timeout; if( mod == MOD_PLASMA_S ) { plasma->s.modelindex = trap_ModelIndex( PATH_PLASMA_STRONG_MODEL ); plasma->s.sound = trap_SoundIndex( S_WEAPON_PLASMAGUN_S_FLY ); plasma->s.effects |= EF_STRONG_WEAPON; } else { plasma->s.modelindex = trap_ModelIndex( PATH_PLASMA_WEAK_MODEL ); plasma->s.sound = trap_SoundIndex( S_WEAPON_PLASMAGUN_W_FLY ); plasma->s.effects &= ~EF_STRONG_WEAPON; } return plasma; }
void Touch_ItemSound( edict_t *other, gsitem_t *item ) { if( item->pickup_sound ) { if( item->type & IT_POWERUP ) G_Sound( other, CHAN_ITEM, trap_SoundIndex( item->pickup_sound ), ATTN_NORM ); else G_Sound( other, CHAN_AUTO, trap_SoundIndex( item->pickup_sound ), ATTN_NORM ); } }
void SP_trigger_hurt( edict_t *self ) { InitTrigger( self ); if( self->dmg > 300 ) // HACK: force KILL spawnflag for big damages self->spawnflags |= 32; if( self->spawnflags & 4 ) // SILENT { self->noise_index = 0; } else if( st.noise ) { self->noise_index = trap_SoundIndex( st.noise ); G_PureSound( st.noise ); } else if( self->spawnflags & 32 || self->spawnflags & 64 ) // KILL or FALL { self->noise_index = trap_SoundIndex( S_PLAYER_FALLDEATH ); } else { self->noise_index = 0; } // gameteam field from editor if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) { self->s.team = st.gameteam; } else { self->s.team = TEAM_SPECTATOR; } self->touch = hurt_touch; if( !self->dmg ) self->dmg = 5; if( self->spawnflags & 16 || !self->wait ) self->wait = 0.1f; if( self->spawnflags & 1 ) self->r.solid = SOLID_NOT; else self->r.solid = SOLID_TRIGGER; if( self->spawnflags & 2 ) self->use = hurt_use; }
/* * G_SetClientSound */ static void G_SetClientSound( edict_t *ent ) { if( ent->waterlevel == 3 ) { if( ent->watertype & CONTENTS_LAVA ) ent->s.sound = trap_SoundIndex( S_WORLD_UNDERLAVA ); else if( ent->watertype & CONTENTS_SLIME ) ent->s.sound = trap_SoundIndex( S_WORLD_UNDERSLIME ); else if( ent->watertype & CONTENTS_WATER ) ent->s.sound = trap_SoundIndex( S_WORLD_UNDERWATER ); } else ent->s.sound = 0; }
void G_Gametype_GENERIC_SetUpCountdown( void ) { bool any = false; int team; G_Match_RemoveAllProjectiles(); G_Items_RespawnByType( 0, 0, 0 ); // respawn all items level.gametype.readyAnnouncementEnabled = false; level.gametype.scoreAnnouncementEnabled = false; level.gametype.countdownEnabled = true; level.gametype.pickableItemsMask = 0; // disallow item pickup if( GS_TeamBasedGametype() ) { for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) if( G_Teams_LockTeam( team ) ) any = true; } else { if( G_Teams_LockTeam( TEAM_PLAYERS ) ) any = true; } if( any ) G_PrintMsg( NULL, "Teams locked.\n" ); G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_GET_READY_TO_FIGHT_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); }
void SP_trigger_teleport( edict_t *ent ) { if( !ent->target ) { if( developer->integer ) G_Printf( "teleporter without a target.\n" ); G_FreeEdict( ent ); return; } if( st.noise ) { ent->noise_index = trap_SoundIndex( st.noise ); G_PureSound( st.noise ); } // gameteam field from editor if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) { ent->s.team = st.gameteam; } else { ent->s.team = TEAM_SPECTATOR; } InitTrigger( ent ); ent->touch = old_teleporter_touch; }
/* * G_HideLaser */ void G_HideLaser( edict_t *ent ) { int soundindex; ent->s.modelindex = 0; ent->s.sound = 0; ent->r.svflags = SVF_NOCLIENT; if( ent->s.type == ET_CURVELASERBEAM ) soundindex = trap_SoundIndex( S_WEAPON_LASERGUN_W_STOP ); else soundindex = trap_SoundIndex( S_WEAPON_LASERGUN_S_STOP ); G_Sound( game.edicts + ent->s.ownerNum, CHAN_AUTO, soundindex, ATTN_NORM ); // give it 100 msecs before freeing itself, so we can relink it if we start firing again ent->think = G_FreeEdict; ent->nextThink = level.time + 100; }
void DoRespawn( edict_t *ent ) { if( ent->team ) { edict_t *master; int count; int choice; master = ent->teammaster; assert( master != NULL ); if( master ) { for( count = 0, ent = master; ent; ent = ent->chain, count++ ); choice = rand() % count; for( count = 0, ent = master; count < choice; ent = ent->chain, count++ ); } } ent->r.solid = SOLID_TRIGGER; ent->r.svflags &= ~SVF_NOCLIENT; ent->s.effects &= ~EF_GHOST; GClip_LinkEntity( ent ); // send an effect G_AddEvent( ent, EV_ITEM_RESPAWN, ent->item ? ent->item->tag : 0, true ); // powerups announce their presence with a global sound if( ent->item && ( ent->item->type & IT_POWERUP ) ) { if( ent->item->tag == POWERUP_QUAD ) G_GlobalSound( CHAN_AUTO, trap_SoundIndex( S_ITEM_QUAD_RESPAWN ) ); if( ent->item->tag == POWERUP_SHELL ) G_GlobalSound( CHAN_AUTO, trap_SoundIndex( S_ITEM_WARSHELL_RESPAWN ) ); if( ent->item->tag == POWERUP_REGEN ) G_GlobalSound( CHAN_AUTO, trap_SoundIndex( S_ITEM_REGEN_RESPAWN ) ); } }
/* * G_Match_CheckExtendPlayTime */ bool G_Match_CheckExtendPlayTime( void ) { // check for extended time/sudden death if( GS_MatchState() != MATCH_STATE_PLAYTIME ) return false; if( GS_TeamBasedGametype() && !level.forceExit ) { if( G_Match_Tied() ) { GS_GamestatSetFlag( GAMESTAT_FLAG_MATCHEXTENDED, true ); gs.gameState.stats[GAMESTAT_MATCHSTATE] = MATCH_STATE_PLAYTIME; gs.gameState.longstats[GAMELONG_MATCHSTART] = game.serverTime; if( g_match_extendedtime->value ) { if( !GS_MatchExtended() ) // first one G_AnnouncerSound( NULL, trap_SoundIndex( S_ANNOUNCER_OVERTIME_GOING_TO_OVERTIME ), GS_MAX_TEAMS, true, NULL ); else G_AnnouncerSound( NULL, trap_SoundIndex( S_ANNOUNCER_OVERTIME_OVERTIME ), GS_MAX_TEAMS, true, NULL ); G_PrintMsg( NULL, "Match tied. Timelimit extended by %i minutes!\n", g_match_extendedtime->integer ); G_CenterPrintFormatMsg( NULL, "%s MINUTE OVERTIME\n", va( "%i", g_match_extendedtime->integer ), NULL ); gs.gameState.longstats[GAMELONG_MATCHDURATION] = (unsigned int)( ( fabs( g_match_extendedtime->value ) * 60 ) * 1000 ); } else { G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_OVERTIME_SUDDENDEATH_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); G_PrintMsg( NULL, "Match tied. Sudden death!\n" ); G_CenterPrintMsg( NULL, "SUDDEN DEATH" ); gs.gameState.longstats[GAMELONG_MATCHDURATION] = 0; } return true; } } return false; }
void G_Gametype_GENERIC_SetUpEndMatch( void ) { edict_t *ent; level.gametype.readyAnnouncementEnabled = false; level.gametype.scoreAnnouncementEnabled = false; level.gametype.pickableItemsMask = 0; // disallow item pickup level.gametype.countdownEnabled = false; for( ent = game.edicts + 1; PLAYERNUM( ent ) < gs.maxclients; ent++ ) { if( ent->r.inuse && trap_GetClientState( PLAYERNUM( ent ) ) >= CS_SPAWNED ) G_ClientRespawn( ent, true ); } G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_POSTMATCH_GAMEOVER_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); }
/* * 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; }
/* * W_Fire_GunbladeBlast */ edict_t *W_Fire_GunbladeBlast( edict_t *self, vec3_t start, vec3_t angles, float damage, int minKnockback, int maxKnockback, int stun, int minDamage, int radius, int speed, int timeout, int mod, int timeDelta ) { edict_t *blast; if( GS_Instagib() ) damage = 9999; blast = W_Fire_LinearProjectile( self, start, angles, speed, damage, minKnockback, maxKnockback, stun, minDamage, radius, timeout, timeDelta ); blast->s.modelindex = trap_ModelIndex( PATH_GUNBLADEBLAST_STRONG_MODEL ); blast->s.type = ET_BLASTER; blast->s.effects |= EF_STRONG_WEAPON; blast->touch = W_Touch_GunbladeBlast; blast->classname = "gunblade_blast"; blast->style = mod; blast->s.sound = trap_SoundIndex( S_WEAPON_PLASMAGUN_S_FLY ); return blast; }
/* * Cmd_Timeout_f */ static void Cmd_Timein_f( edict_t *ent ) { int num; if( ent->s.team == TEAM_SPECTATOR ) return; if( !GS_MatchPaused() ) { G_PrintMsg( ent, "No timeout in progress.\n" ); return; } if( level.timeout.endtime - level.timeout.time <= 2 * TIMEIN_TIME ) { G_PrintMsg( ent, "The timeout is about to end already.\n" ); return; } if( GS_TeamBasedGametype() ) num = ent->s.team; else num = ENTNUM( ent )-1; if( level.timeout.caller != num ) { if( GS_TeamBasedGametype() ) G_PrintMsg( ent, "Your team didn't call this timeout.\n" ); else G_PrintMsg( ent, "You didn't call this timeout.\n" ); return; } level.timeout.endtime = level.timeout.time + TIMEIN_TIME + FRAMETIME; G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_TIMEOUT_TIMEIN_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); G_PrintMsg( NULL, "%s%s called a timein\n", ent->r.client->netname, S_COLOR_WHITE ); }
/* * Cmd_Timeout_f */ static void Cmd_Timeout_f( edict_t *ent ) { int num; if( ent->s.team == TEAM_SPECTATOR || GS_MatchState() != MATCH_STATE_PLAYTIME ) return; if( GS_TeamBasedGametype() ) num = ent->s.team; else num = ENTNUM( ent )-1; if( GS_MatchPaused() && ( level.timeout.endtime - level.timeout.time ) >= 2*TIMEIN_TIME ) { G_PrintMsg( ent, "Timeout already in progress\n" ); return; } if( g_maxtimeouts->integer != -1 && level.timeout.used[num] >= g_maxtimeouts->integer ) { if( g_maxtimeouts->integer == 0 ) G_PrintMsg( ent, "Timeouts are not allowed on this server\n" ); else if( GS_TeamBasedGametype() ) G_PrintMsg( ent, "Your team doesn't have any timeouts left\n" ); else G_PrintMsg( ent, "You don't have any timeouts left\n" ); return; } G_PrintMsg( NULL, "%s%s called a timeout\n", ent->r.client->netname, S_COLOR_WHITE ); if( !GS_MatchPaused() ) G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_TIMEOUT_TIMEOUT_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); level.timeout.used[num]++; GS_GamestatSetFlag( GAMESTAT_FLAG_PAUSED, true ); level.timeout.caller = num; level.timeout.endtime = level.timeout.time + TIMEOUT_TIME + FRAMETIME; }
void SP_target_speaker( edict_t *ent ) { char buffer[MAX_QPATH]; if( !st.noise ) { if( developer->integer ) G_Printf( "target_speaker with no noise set at %s\n", vtos( ent->s.origin ) ); return; } Q_strncpyz( buffer, st.noise, sizeof( buffer ) ); ent->noise_index = trap_SoundIndex( buffer ); G_PureSound( buffer ); if( ent->attenuation == -1 || ent->spawnflags & 4 ) // use -1 so 0 defaults to ATTN_NONE ent->attenuation = ATTN_NONE; else if( !ent->attenuation ) ent->attenuation = ATTN_NORM; if( ent->attenuation == ATTN_NONE ) ent->r.svflags |= SVF_BROADCAST; if( ent->spawnflags & 16 ) ent->r.svflags |= SVF_ONLYOWNER; // check for prestarted looping sound if( ent->spawnflags & 1 ) { ent->s.sound = ent->noise_index; ent->r.svflags &= ~SVF_NOCLIENT; } ent->use = Use_Target_Speaker; // must link the entity so we get areas and clusters so // the server can determine who to send updates to GClip_LinkEntity( ent ); }
void G_Gametype_GENERIC_SetUpMatch( void ) { int i; level.gametype.readyAnnouncementEnabled = false; level.gametype.scoreAnnouncementEnabled = true; level.gametype.countdownEnabled = true; level.gametype.pickableItemsMask = ( level.gametype.spawnableItemsMask|level.gametype.dropableItemsMask ); if( GS_Instagib() ) level.gametype.pickableItemsMask &= ~G_INSTAGIB_NEGATE_ITEMMASK; // clear player stats and scores, team scores and respawn clients in team lists for( i = TEAM_PLAYERS; i < GS_MAX_TEAMS; i++ ) { int j; g_teamlist_t *team = &teamlist[i]; memset( &team->stats, 0, sizeof( team->stats ) ); // respawn all clients inside the playing teams for( j = 0; j < team->numplayers; j++ ) { edict_t *ent = &game.edicts[ team->playerIndices[j] ]; G_ClientClearStats( ent ); G_ClientRespawn( ent, false ); } } // set items to be spawned with a delay G_Items_RespawnByType( IT_ARMOR, ARMOR_RA, 15 ); G_Items_RespawnByType( IT_ARMOR, ARMOR_RA, 15 ); G_Items_RespawnByType( IT_HEALTH, HEALTH_MEGA, 15 ); G_Items_RespawnByType( IT_HEALTH, HEALTH_ULTRA, 15 ); G_Items_RespawnByType( IT_POWERUP, 0, brandom( 20, 40 ) ); G_Match_FreeBodyQueue(); G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_FIGHT_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, false, NULL ); G_CenterPrintMsg( NULL, "FIGHT!" ); }
void SP_trigger_multiple( edict_t *ent ) { GClip_SetBrushModel( ent, ent->model ); G_PureModel( ent->model ); if( st.noise ) { ent->noise_index = trap_SoundIndex( st.noise ); G_PureSound( st.noise ); } // gameteam field from editor if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) ent->s.team = st.gameteam; else ent->s.team = TEAM_SPECTATOR; if( !ent->wait ) ent->wait = 0.2f; ent->touch = Touch_Multi; ent->movetype = MOVETYPE_NONE; ent->r.svflags |= SVF_NOCLIENT; if( ent->spawnflags & 4 ) { ent->r.solid = SOLID_NOT; ent->use = trigger_enable; } else { ent->r.solid = SOLID_TRIGGER; ent->use = Use_Multi; } GClip_LinkEntity( ent ); }
/* * ClientThink */ void ClientThink( edict_t *ent, usercmd_t *ucmd, int timeDelta ) { gclient_t *client; int i, j; static pmove_t pm; int delta, count; client = ent->r.client; client->ps.POVnum = ENTNUM( ent ); client->ps.playerNum = PLAYERNUM( ent ); // anti-lag if( ent->r.svflags & SVF_FAKECLIENT ) { client->timeDelta = 0; } else { int nudge; int fixedNudge = ( game.snapFrameTime ) * 0.5; // fixme: find where this nudge comes from. // add smoothing to timeDelta between the last few ucmds and a small fine-tuning nudge. nudge = fixedNudge + g_antilag_timenudge->integer; timeDelta += nudge; clamp( timeDelta, -g_antilag_maxtimedelta->integer, 0 ); // smooth using last valid deltas i = client->timeDeltasHead - 6; if( i < 0 ) i = 0; for( count = 0, delta = 0; i < client->timeDeltasHead; i++ ) { if( client->timeDeltas[i & G_MAX_TIME_DELTAS_MASK] < 0 ) { delta += client->timeDeltas[i & G_MAX_TIME_DELTAS_MASK]; count++; } } if( !count ) client->timeDelta = timeDelta; else { delta /= count; client->timeDelta = ( delta + timeDelta ) * 0.5; } client->timeDeltas[client->timeDeltasHead & G_MAX_TIME_DELTAS_MASK] = timeDelta; client->timeDeltasHead++; #ifdef UCMDTIMENUDGE client->timeDelta += client->pers.ucmdTimeNudge; #endif } clamp( client->timeDelta, -g_antilag_maxtimedelta->integer, 0 ); // update activity if he touched any controls if( ucmd->forwardmove != 0 || ucmd->sidemove != 0 || ucmd->upmove != 0 || ( ucmd->buttons & ~BUTTON_BUSYICON ) != 0 || client->ucmd.angles[PITCH] != ucmd->angles[PITCH] || client->ucmd.angles[YAW] != ucmd->angles[YAW] ) G_Client_UpdateActivity( client ); client->ucmd = *ucmd; // can exit intermission after two seconds, not counting postmatch if( GS_MatchState() == MATCH_STATE_WAITEXIT && ( ucmd->buttons & BUTTON_ATTACK ) && game.serverTime > GS_MatchStartTime() + 2000 ) level.exitNow = true; // (is this really needed?:only if not cared enough about ps in the rest of the code) // refresh player state position from the entity VectorCopy( ent->s.origin, client->ps.pmove.origin ); VectorCopy( ent->velocity, client->ps.pmove.velocity ); VectorCopy( ent->s.angles, client->ps.viewangles ); client->ps.pmove.gravity = level.gravity; if( GS_MatchState() >= MATCH_STATE_POSTMATCH || GS_MatchPaused() || ( ent->movetype != MOVETYPE_PLAYER && ent->movetype != MOVETYPE_NOCLIP ) ) client->ps.pmove.pm_type = PM_FREEZE; else if( ent->s.type == ET_GIB ) client->ps.pmove.pm_type = PM_GIB; else if( ent->movetype == MOVETYPE_NOCLIP || client->isTV ) client->ps.pmove.pm_type = PM_SPECTATOR; else client->ps.pmove.pm_type = PM_NORMAL; // set up for pmove memset( &pm, 0, sizeof( pmove_t ) ); pm.playerState = &client->ps; if( !client->isTV ) pm.cmd = *ucmd; if( memcmp( &client->old_pmove, &client->ps.pmove, sizeof( pmove_state_t ) ) ) pm.snapinitial = true; // perform a pmove Pmove( &pm ); // save results of pmove client->old_pmove = client->ps.pmove; // update the entity with the new position VectorCopy( client->ps.pmove.origin, ent->s.origin ); VectorCopy( client->ps.pmove.velocity, ent->velocity ); VectorCopy( client->ps.viewangles, ent->s.angles ); ent->viewheight = client->ps.viewheight; VectorCopy( pm.mins, ent->r.mins ); VectorCopy( pm.maxs, ent->r.maxs ); ent->waterlevel = pm.waterlevel; ent->watertype = pm.watertype; if( pm.groundentity == -1 ) { ent->groundentity = NULL; } else { G_AwardResetPlayerComboStats( ent ); ent->groundentity = &game.edicts[pm.groundentity]; ent->groundentity_linkcount = ent->groundentity->linkcount; } GClip_LinkEntity( ent ); GS_AddLaserbeamPoint( &ent->r.client->resp.trail, &ent->r.client->ps, ucmd->serverTimeStamp ); // Regeneration if( ent->r.client->ps.inventory[POWERUP_REGEN] > 0 && ent->health < 200) { ent->health += ( game.frametime * 0.001f ) * 10.0f; // Regen expires if health reaches 200 if ( ent->health >= 199.0f ) ent->r.client->ps.inventory[POWERUP_REGEN]--; } // fire touch functions if( ent->movetype != MOVETYPE_NOCLIP ) { edict_t *other; // touch other objects for( i = 0; i < pm.numtouch; i++ ) { other = &game.edicts[pm.touchents[i]]; for( j = 0; j < i; j++ ) { if( &game.edicts[pm.touchents[j]] == other ) break; } if( j != i ) continue; // duplicated // player can't touch projectiles, only projectiles can touch the player G_CallTouch( other, ent, NULL, 0 ); } } ent->s.weapon = GS_ThinkPlayerWeapon( &client->ps, ucmd->buttons, ucmd->msec, client->timeDelta ); if( G_IsDead( ent ) ) { if( ent->deathTimeStamp + g_respawn_delay_min->integer <= level.time ) client->resp.snap.buttons |= ucmd->buttons; } else if( client->ps.pmove.stats[PM_STAT_NOUSERCONTROL] <= 0 ) client->resp.snap.buttons |= ucmd->buttons; // trigger the instashield if( GS_Instagib() && g_instashield->integer ) { if( client->ps.pmove.pm_type == PM_NORMAL && pm.cmd.upmove < 0 && client->resp.instashieldCharge == INSTA_SHIELD_MAX && client->ps.inventory[POWERUP_SHELL] == 0 ) { client->ps.inventory[POWERUP_SHELL] = client->resp.instashieldCharge; G_Sound( ent, CHAN_AUTO, trap_SoundIndex( GS_FindItemByTag( POWERUP_SHELL )->pickup_sound ), ATTN_NORM ); } } // if( client->ps.pmove.pm_type == PM_NORMAL ) client->level.stats.had_playtime = true; // generating plrkeys (optimized for net communication) ClientMakePlrkeys( client, ucmd ); }
void SV_Physics_Step (edict_t *ent) { bool wasonground; bool hitsound = false; float *vel; float speed, newspeed, control; float friction; edict_t *groundentity; int mask; // airborn monsters should always check for ground if (!ent->groundentity) M_CheckGround (ent); groundentity = ent->groundentity; SV_CheckVelocity (ent); if (groundentity) wasonground = true; else wasonground = false; if (ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2]) SV_AddRotationalFriction (ent); // add gravity except: // flying monsters // swimming monsters who are in the water if (! wasonground) if (!(ent->flags & FL_FLY)) if (!((ent->flags & FL_SWIM) && (ent->waterlevel > 2))) { if (ent->velocity[2] < level.gravity*-0.1) hitsound = true; if (ent->waterlevel == 0) SV_AddGravity (ent); } // friction for flying monsters that have been given vertical velocity if ((ent->flags & FL_FLY) && (ent->velocity[2] != 0)) { speed = fabs(ent->velocity[2]); control = speed < sv_stopspeed ? sv_stopspeed : speed; friction = sv_friction/3; newspeed = speed - (FRAMETIME * control * friction); if (newspeed < 0) newspeed = 0; newspeed /= speed; ent->velocity[2] *= newspeed; } // friction for flying monsters that have been given vertical velocity if ((ent->flags & FL_SWIM) && (ent->velocity[2] != 0)) { speed = fabs(ent->velocity[2]); control = speed < sv_stopspeed ? sv_stopspeed : speed; newspeed = speed - (FRAMETIME * control * sv_waterfriction * ent->waterlevel); if (newspeed < 0) newspeed = 0; newspeed /= speed; ent->velocity[2] *= newspeed; } if (ent->velocity[2] || ent->velocity[1] || ent->velocity[0]) { // apply friction // let dead monsters who aren't completely onground slide if ((wasonground) || (ent->flags & (FL_SWIM|FL_FLY))) if (!(ent->health <= 0.0 && !M_CheckBottom(ent))) { vel = ent->velocity; speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]); if (speed) { friction = sv_friction; control = speed < sv_stopspeed ? sv_stopspeed : speed; newspeed = speed - FRAMETIME*control*friction; if (newspeed < 0) newspeed = 0; newspeed /= speed; vel[0] *= newspeed; vel[1] *= newspeed; } } if (ent->r.svflags & SVF_MONSTER) mask = MASK_MONSTERSOLID; else mask = MASK_SOLID; SV_FlyMove (ent, FRAMETIME, mask); GClip_LinkEntity (ent); GClip_TouchTriggers (ent); if (ent->groundentity) if (!wasonground) if (hitsound) G_Sound (ent, 0, trap_SoundIndex( S_LAND ), ATTN_NORM); } }
void G_PrecacheMedia( void ) { // // MODELS // // THIS ORDER MUST MATCH THE DEFINES IN gs_public.h // you can add more, max 255 trap_ModelIndex( "#gunblade/gunblade.md3" ); // WEAP_GUNBLADE trap_ModelIndex( "#machinegun/machinegun.md3" ); // WEAP_MACHINEGUN trap_ModelIndex( "#riotgun/riotgun.md3" ); // WEAP_RIOTGUN trap_ModelIndex( "#glauncher/glauncher.md3" ); // WEAP_GRENADELAUNCHER trap_ModelIndex( "#rlauncher/rlauncher.md3" ); // WEAP_ROCKETLAUNCHER trap_ModelIndex( "#plasmagun/plasmagun.md3" ); // WEAP_PLASMAGUN trap_ModelIndex( "#lasergun/lasergun.md3" ); // WEAP_LASERGUN trap_ModelIndex( "#electrobolt/electrobolt.md3" ); // WEAP_ELECTROBOLT trap_ModelIndex( "#instagun/instagun.md3" ); // WEAP_INSTAGUN //------------------- // precache our basic player models, they are just a very few trap_ModelIndex( "$models/players/bigvic" ); trap_SkinIndex( "models/players/bigvic/default" ); // FIXME: Temporarily use normal gib until the head is fixed trap_ModelIndex( "models/objects/gibs/illuminati1/illuminati1.md3" ); // // SOUNDS // // jalfixme : most of these sounds can be played from the clients trap_SoundIndex( S_WORLD_WATER_IN ); // feet hitting water trap_SoundIndex( S_WORLD_WATER_OUT ); // feet leaving water trap_SoundIndex( S_WORLD_UNDERWATER ); trap_SoundIndex( S_WORLD_SLIME_IN ); trap_SoundIndex( S_WORLD_SLIME_OUT ); trap_SoundIndex( S_WORLD_UNDERSLIME ); trap_SoundIndex( S_WORLD_LAVA_IN ); trap_SoundIndex( S_WORLD_LAVA_OUT ); trap_SoundIndex( S_WORLD_UNDERLAVA ); trap_SoundIndex( va( S_PLAYER_BURN_1_to_2, 1 ) ); trap_SoundIndex( va( S_PLAYER_BURN_1_to_2, 2 ) ); //wsw: pb disable unreferenced sounds //trap_SoundIndex (S_LAND); // landing thud trap_SoundIndex( S_HIT_WATER ); trap_SoundIndex( S_WEAPON_NOAMMO ); // announcer // readyup trap_SoundIndex( S_ANNOUNCER_READY_UP_POLITE ); trap_SoundIndex( S_ANNOUNCER_READY_UP_PISSEDOFF ); // countdown trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_GET_READY_TO_FIGHT_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_GET_READY_TO_FIGHT_1_to_2, 2 ) ); trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_READY_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_READY_1_to_2, 2 ) ); trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_COUNT_1_to_3_SET_1_to_2, 1, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_COUNT_1_to_3_SET_1_to_2, 2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_COUNT_1_to_3_SET_1_to_2, 3, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_COUNT_1_to_3_SET_1_to_2, 1, 2 ) ); trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_COUNT_1_to_3_SET_1_to_2, 2, 2 ) ); trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_COUNT_1_to_3_SET_1_to_2, 3, 2 ) ); trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_FIGHT_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_FIGHT_1_to_2, 2 ) ); // postmatch trap_SoundIndex( va( S_ANNOUNCER_POSTMATCH_GAMEOVER_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_POSTMATCH_GAMEOVER_1_to_2, 2 ) ); // timeout trap_SoundIndex( va( S_ANNOUNCER_TIMEOUT_MATCH_RESUMED_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_TIMEOUT_MATCH_RESUMED_1_to_2, 2 ) ); trap_SoundIndex( va( S_ANNOUNCER_TIMEOUT_TIMEOUT_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_TIMEOUT_TIMEOUT_1_to_2, 2 ) ); trap_SoundIndex( va( S_ANNOUNCER_TIMEOUT_TIMEIN_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_TIMEOUT_TIMEIN_1_to_2, 2 ) ); // callvote trap_SoundIndex( va( S_ANNOUNCER_CALLVOTE_CALLED_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_CALLVOTE_CALLED_1_to_2, 2 ) ); trap_SoundIndex( va( S_ANNOUNCER_CALLVOTE_FAILED_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_CALLVOTE_FAILED_1_to_2, 2 ) ); trap_SoundIndex( va( S_ANNOUNCER_CALLVOTE_PASSED_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_CALLVOTE_PASSED_1_to_2, 2 ) ); trap_SoundIndex( S_ANNOUNCER_CALLVOTE_VOTE_NOW ); // overtime trap_SoundIndex( S_ANNOUNCER_OVERTIME_GOING_TO_OVERTIME ); trap_SoundIndex( S_ANNOUNCER_OVERTIME_OVERTIME ); trap_SoundIndex( va( S_ANNOUNCER_OVERTIME_SUDDENDEATH_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_OVERTIME_SUDDENDEATH_1_to_2, 2 ) ); // score trap_SoundIndex( va( S_ANNOUNCER_SCORE_TAKEN_LEAD_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_SCORE_TAKEN_LEAD_1_to_2, 2 ) ); trap_SoundIndex( va( S_ANNOUNCER_SCORE_LOST_LEAD_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_SCORE_LOST_LEAD_1_to_2, 2 ) ); trap_SoundIndex( va( S_ANNOUNCER_SCORE_TIED_LEAD_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_SCORE_TIED_LEAD_1_to_2, 2 ) ); if( GS_TeamBasedGametype() ) { trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_TAKEN_LEAD_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_TAKEN_LEAD_1_to_2, 2 ) ); trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_LOST_LEAD_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_LOST_LEAD_1_to_2, 2 ) ); trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_TIED_LEAD_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_TIED_LEAD_1_to_2, 2 ) ); trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_TIED_LEAD_1_to_2, 1 ) ); trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_TIED_LEAD_1_to_2, 2 ) ); //trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_1_to_4_TAKEN_LEAD_1_to_2, 3, 1 ) ); //trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_1_to_4_TAKEN_LEAD_1_to_2, 3, 2 ) ); //trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_1_to_4_TAKEN_LEAD_1_to_2, 4, 1 ) ); //trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_1_to_4_TAKEN_LEAD_1_to_2, 4, 2 ) ); } // // LIGHTSTYLES // // light animation tables. 'a' is total darkness, 'z' is doublebright. // 0 normal trap_ConfigString( CS_LIGHTS+0, "m" ); // 1 FLICKER (first variety) trap_ConfigString( CS_LIGHTS+1, "mmnmmommommnonmmonqnmmo" ); // 2 SLOW STRONG PULSE trap_ConfigString( CS_LIGHTS+2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba" ); // 3 CANDLE (first variety) trap_ConfigString( CS_LIGHTS+3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg" ); // 4 FAST STROBE trap_ConfigString( CS_LIGHTS+4, "mamamamamama" ); // 5 GENTLE PULSE 1 trap_ConfigString( CS_LIGHTS+5, "jklmnopqrstuvwxyzyxwvutsrqponmlkj" ); // 6 FLICKER (second variety) trap_ConfigString( CS_LIGHTS+6, "nmonqnmomnmomomno" ); // 7 CANDLE (second variety) trap_ConfigString( CS_LIGHTS+7, "mmmaaaabcdefgmmmmaaaammmaamm" ); // 8 CANDLE (third variety) trap_ConfigString( CS_LIGHTS+8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa" ); // 9 SLOW STROBE (fourth variety) trap_ConfigString( CS_LIGHTS+9, "aaaaaaaazzzzzzzz" ); // 10 FLUORESCENT FLICKER trap_ConfigString( CS_LIGHTS+10, "mmamammmmammamamaaamammma" ); // 11 SLOW PULSE NOT FADE TO BLACK trap_ConfigString( CS_LIGHTS+11, "abcdefghijklmnopqrrqponmlkjihgfedcba" ); // styles 32-62 are assigned by the light program for switchable lights // 63 testing trap_ConfigString( CS_LIGHTS+63, "a" ); }
/* * G_Match_ReadyAnnouncement */ static void G_Match_ReadyAnnouncement( void ) { int i; edict_t *e; int team; bool readyupwarnings = false; int START_TEAM, END_TEAM; if( !level.gametype.readyAnnouncementEnabled ) return; // ready up announcements if( GS_TeamBasedGametype() ) { START_TEAM = TEAM_ALPHA; END_TEAM = GS_MAX_TEAMS; } else { START_TEAM = TEAM_PLAYERS; END_TEAM = TEAM_PLAYERS+1; } for( team = START_TEAM; team < END_TEAM; team++ ) { if( !teamlist[team].numplayers ) continue; for( i = 0; i < teamlist[team].numplayers; i++ ) { e = game.edicts + teamlist[team].playerIndices[i]; if( e->r.svflags & SVF_FAKECLIENT ) continue; if( level.ready[teamlist[team].playerIndices[i]-1] ) { readyupwarnings = true; break; } } } if( !readyupwarnings ) return; // now let's repeat and warn for( team = START_TEAM; team < END_TEAM; team++ ) { if( !teamlist[team].numplayers ) continue; for( i = 0; i < teamlist[team].numplayers; i++ ) { if( !level.ready[teamlist[team].playerIndices[i]-1] ) { e = game.edicts + teamlist[team].playerIndices[i]; if( !e->r.client || trap_GetClientState( PLAYERNUM( e ) ) != CS_SPAWNED ) continue; if( e->r.client->teamstate.readyUpWarningNext < game.realtime ) { e->r.client->teamstate.readyUpWarningNext = game.realtime + G_ANNOUNCER_READYUP_DELAY; e->r.client->teamstate.readyUpWarningCount++; if( e->r.client->teamstate.readyUpWarningCount > 3 ) { G_AnnouncerSound( e, trap_SoundIndex( S_ANNOUNCER_READY_UP_PISSEDOFF ), GS_MAX_TEAMS, true, NULL ); e->r.client->teamstate.readyUpWarningCount = 0; } else { G_AnnouncerSound( e, trap_SoundIndex( S_ANNOUNCER_READY_UP_POLITE ), GS_MAX_TEAMS, true, NULL ); } } } } } }
//QUAKED misc_particles (.6 .7 .7) (-8 -8 -8) (8 8 8) SPHERICAL SOLID GRAVITY LIGHT EXPAND_EFFECT SHRINK_EFFECT START_OFF //-------- KEYS -------- //angles: direction in which particles will be thrown. //shader : particleShader void SP_misc_particles( edict_t *ent ) { ent->r.svflags &= ~SVF_NOCLIENT; ent->r.svflags |= SVF_BROADCAST; ent->r.solid = SOLID_NOT; ent->s.type = ET_PARTICLES; if( st.noise ) { ent->s.sound = trap_SoundIndex( st.noise ); G_PureSound( st.noise ); } if( st.gameteam >= TEAM_ALPHA && st.gameteam < GS_MAX_TEAMS ) ent->s.team = st.gameteam; else ent->s.team = 0; if( ent->speed > 0 ) ent->particlesInfo.speed = ((int)ent->speed) & 255; if( ent->count > 0 ) ent->particlesInfo.frequency = ent->count & 255; if( st.shaderName ) ent->particlesInfo.shaderIndex = trap_ImageIndex( st.shaderName ); else ent->particlesInfo.shaderIndex = trap_ImageIndex( "particle" ); if( st.size ) ent->particlesInfo.size = st.size & 255; else ent->particlesInfo.size = 16; ent->particlesInfo.time = ent->delay; if( !ent->particlesInfo.time ) ent->particlesInfo.time = 4; if( ent->spawnflags & 1 ) // SPHERICAL ent->particlesInfo.spherical = true; if( ent->spawnflags & 2 ) // BOUNCE ent->particlesInfo.bounce = true; if( ent->spawnflags & 4 ) // GRAVITY ent->particlesInfo.gravity = true; if( ent->spawnflags & 8 ) // LIGHT { ent->s.light = COLOR_RGB( (qbyte)(ent->color[0] * 255), (qbyte)(ent->color[1] * 255), (qbyte)(ent->color[2] * 255) ); if( !ent->s.light ) ent->s.light = COLOR_RGB( 255, 255, 255 ); } if( ent->spawnflags & 16 ) // EXPAND_EFFECT ent->particlesInfo.expandEffect = true; if( ent->spawnflags & 32 ) // SHRINK_EFFECT ent->particlesInfo.shrinkEffect = true; if( ent->spawnflags & 64 ) // START_OFF ent->r.svflags |= SVF_NOCLIENT; if( st.radius > 0 ) { ent->particlesInfo.spread = st.radius; clamp( ent->particlesInfo.spread, 0, 255 ); } ent->think = SP_misc_particles_finish; ent->nextThink = level.time + 1; ent->use = SP_misc_particles_use; GClip_LinkEntity( ent ); }
/* * SV_Physics_Toss * * Toss, bounce, and fly movement. When onground, do nothing. * * FIXME: This function needs a serious rewrite */ static void SV_Physics_Toss( edict_t *ent ) { trace_t trace; vec3_t move; float backoff; edict_t *slave; bool wasinwater; bool isinwater; vec3_t old_origin; float oldSpeed; // if not a team captain, so movement will be handled elsewhere if( ent->flags & FL_TEAMSLAVE ) { return; } // refresh the ground entity if( ent->movetype == MOVETYPE_BOUNCE || ent->movetype == MOVETYPE_BOUNCEGRENADE ) { if( ent->velocity[2] > 0.1f ) { ent->groundentity = NULL; } } if( ent->groundentity && ent->groundentity != world && !ent->groundentity->r.inuse ) { ent->groundentity = NULL; } oldSpeed = VectorLength( ent->velocity ); if( ent->groundentity ) { if( !oldSpeed ) { return; } if( ent->movetype == MOVETYPE_TOSS ) { if( ent->velocity[2] >= 8 ) { ent->groundentity = NULL; } else { VectorClear( ent->velocity ); VectorClear( ent->avelocity ); G_CallStop( ent ); return; } } } VectorCopy( ent->s.origin, old_origin ); if( ent->accel != 0 ) { if( ent->accel < 0 && VectorLength( ent->velocity ) < 50 ) { VectorClear( ent->velocity ); } else { vec3_t acceldir; VectorNormalize2( ent->velocity, acceldir ); VectorScale( acceldir, ent->accel * FRAMETIME, acceldir ); VectorAdd( ent->velocity, acceldir, ent->velocity ); } } SV_CheckVelocity( ent ); // add gravity if( ent->movetype != MOVETYPE_FLY && !ent->groundentity ) { SV_AddGravity( ent ); } // move angles VectorMA( ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles ); // move origin VectorScale( ent->velocity, FRAMETIME, move ); trace = SV_PushEntity( ent, move ); if( !ent->r.inuse ) { return; } if( trace.fraction < 1.0f ) { if( ent->movetype == MOVETYPE_BOUNCE ) { backoff = 1.5; } else if( ent->movetype == MOVETYPE_BOUNCEGRENADE ) { backoff = 1.5; } else { backoff = 1; } GS_ClipVelocity( ent->velocity, trace.plane.normal, ent->velocity, backoff ); // stop if on ground if( ent->movetype == MOVETYPE_BOUNCE || ent->movetype == MOVETYPE_BOUNCEGRENADE ) { // stop dead on allsolid // LA: hopefully will fix grenades bouncing down slopes // method taken from Darkplaces sourcecode if( trace.allsolid || ( ISWALKABLEPLANE( &trace.plane ) && fabs( DotProduct( trace.plane.normal, ent->velocity ) ) < 40 ) ) { ent->groundentity = &game.edicts[trace.ent]; ent->groundentity_linkcount = ent->groundentity->linkcount; VectorClear( ent->velocity ); VectorClear( ent->avelocity ); G_CallStop( ent ); } } else { // in movetype_toss things stop dead when touching ground #if 0 G_CheckGround( ent ); if( ent->groundentity ) { #else // walkable or trapped inside solid brush if( trace.allsolid || ISWALKABLEPLANE( &trace.plane ) ) { ent->groundentity = trace.ent < 0 ? world : &game.edicts[trace.ent]; ent->groundentity_linkcount = ent->groundentity->linkcount; #endif VectorClear( ent->velocity ); VectorClear( ent->avelocity ); G_CallStop( ent ); } } } // check for water transition wasinwater = ( ent->watertype & MASK_WATER ) ? true : false; ent->watertype = G_PointContents( ent->s.origin ); isinwater = ent->watertype & MASK_WATER ? true : false; // never allow items in CONTENTS_NODROP if( ent->item && ( ent->watertype & CONTENTS_NODROP ) ) { G_FreeEdict( ent ); return; } if( isinwater ) { ent->waterlevel = 1; } else { ent->waterlevel = 0; } if( !wasinwater && isinwater ) { G_PositionedSound( old_origin, CHAN_AUTO, trap_SoundIndex( S_HIT_WATER ), ATTN_IDLE ); } else if( wasinwater && !isinwater ) { G_PositionedSound( ent->s.origin, CHAN_AUTO, trap_SoundIndex( S_HIT_WATER ), ATTN_IDLE ); } // move teamslaves for( slave = ent->teamchain; slave; slave = slave->teamchain ) { VectorCopy( ent->s.origin, slave->s.origin ); GClip_LinkEntity( slave ); } } //============================================================================ void SV_Physics_LinearProjectile( edict_t *ent ) { vec3_t start, end; int mask; float startFlyTime, endFlyTime; trace_t trace; int old_waterLevel; // if not a team captain movement will be handled elsewhere if( ent->flags & FL_TEAMSLAVE ) { return; } old_waterLevel = ent->waterlevel; mask = ( ent->r.clipmask ) ? ent->r.clipmask : MASK_SOLID; // find it's current position given the starting timeStamp startFlyTime = (float)( max( game.prevServerTime - ent->s.linearMovementTimeStamp, 0 ) ) * 0.001f; endFlyTime = (float)( game.serverTime - ent->s.linearMovementTimeStamp ) * 0.001f; VectorMA( ent->s.linearMovementBegin, startFlyTime, ent->s.linearMovementVelocity, start ); VectorMA( ent->s.linearMovementBegin, endFlyTime, ent->s.linearMovementVelocity, end ); G_Trace4D( &trace, start, ent->r.mins, ent->r.maxs, end, ent, mask, ent->timeDelta ); VectorCopy( trace.endpos, ent->s.origin ); GClip_LinkEntity( ent ); SV_Impact( ent, &trace ); if( !ent->r.inuse ) { // the projectile may be freed if touched something return; } // update some data required for the transmission //VectorCopy( ent->velocity, ent->s.linearMovementVelocity ); GClip_TouchTriggers( ent ); ent->groundentity = NULL; // projectiles never have ground entity ent->waterlevel = ( G_PointContents4D( ent->s.origin, ent->timeDelta ) & MASK_WATER ) ? true : false; if( !old_waterLevel && ent->waterlevel ) { G_PositionedSound( start, CHAN_AUTO, trap_SoundIndex( S_HIT_WATER ), ATTN_IDLE ); } else if( old_waterLevel && !ent->waterlevel ) { G_PositionedSound( ent->s.origin, CHAN_AUTO, trap_SoundIndex( S_HIT_WATER ), ATTN_IDLE ); } }
/* * PrecacheItem * * Precaches all data needed for a given item. * This will be called for each item spawned in a level, * and for each item in each client's inventory. */ void PrecacheItem( gsitem_t *it ) { int i; const char *s, *start; char data[MAX_QPATH]; int len; gsitem_t *ammo; if( !it ) return; if( it->pickup_sound ) trap_SoundIndex( it->pickup_sound ); for( i = 0; i < MAX_ITEM_MODELS; i++ ) { if( it->world_model[i] ) trap_ModelIndex( it->world_model[i] ); } if( it->icon ) trap_ImageIndex( it->icon ); // parse everything for its ammo if( it->ammo_tag ) { ammo = GS_FindItemByTag( it->ammo_tag ); if( ammo != it ) PrecacheItem( ammo ); } // parse the space separated precache string for other items for( i = 0; i < 3; i++ ) { if( i == 0 ) s = it->precache_models; else if( i == 1 ) s = it->precache_sounds; else s = it->precache_images; if( !s || !s[0] ) continue; while( *s ) { start = s; while( *s && *s != ' ' ) s++; len = s-start; if( len >= MAX_QPATH || len < 5 ) G_Error( "PrecacheItem: %s has bad precache string", it->classname ); memcpy( data, start, len ); data[len] = 0; if( *s ) s++; if( i == 0 ) trap_ModelIndex( data ); else if( i == 1 ) trap_SoundIndex( data ); else trap_ImageIndex( data ); } } }
/* * G_Timeout_Update * * Updates the timeout struct and informs clients about the status of the pause */ static void G_Timeout_Update( unsigned int msec ) { static int timeout_printtime = 0; static int timeout_last_endtime = 0; static int countdown_set = 1; if( !GS_MatchPaused() ) return; game.frametime = 0; if( timeout_last_endtime != level.timeout.endtime ) // force print when endtime is changed { timeout_printtime = 0; timeout_last_endtime = level.timeout.endtime; } level.timeout.time += msec; if( level.timeout.endtime && level.timeout.time >= level.timeout.endtime ) { level.timeout.time = 0; level.timeout.caller = -1; GS_GamestatSetFlag( GAMESTAT_FLAG_PAUSED, false ); timeout_printtime = 0; timeout_last_endtime = -1; G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_TIMEOUT_MATCH_RESUMED_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); G_CenterPrintMsg( NULL, "Match resumed" ); G_PrintMsg( NULL, "Match resumed\n" ); } else if( timeout_printtime == 0 || level.timeout.time - timeout_printtime >= 1000 ) { if( level.timeout.endtime ) { int seconds_left = (int)( ( level.timeout.endtime - level.timeout.time ) / 1000.0 + 0.5 ); if( seconds_left == ( TIMEIN_TIME * 2 ) / 1000 ) { G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_READY_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, false, NULL ); countdown_set = ( rand()&1 )+1; } else if( seconds_left >= 1 && seconds_left <= 3 ) { G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_COUNT_1_to_3_SET_1_to_2, seconds_left, countdown_set ) ), GS_MAX_TEAMS, false, NULL ); } if( seconds_left > 1 ) G_CenterPrintFormatMsg( NULL, "Match will resume in %s seconds", va( "%i", seconds_left ), NULL ); else G_CenterPrintMsg( NULL, "Match will resume in 1 second" ); } else { G_CenterPrintMsg( NULL, "Match paused" ); } timeout_printtime = level.timeout.time; } }
/* * G_Match_ScoreAnnouncement */ static void G_Match_ScoreAnnouncement( void ) { int i; edict_t *e, *chased; int num_leaders, team; if( !level.gametype.scoreAnnouncementEnabled ) return; num_leaders = 0; memset( leaders, 0, sizeof( leaders ) ); if( GS_TeamBasedGametype() ) { int score_max = -999999999; for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { if( !teamlist[team].numplayers ) continue; if( teamlist[team].stats.score > score_max ) { score_max = teamlist[team].stats.score; leaders[0] = team; num_leaders = 1; } else if( teamlist[team].stats.score == score_max ) { leaders[num_leaders++] = team; } } leaders[num_leaders] = 0; } else { int score_max = -999999999; for( i = 0; i < MAX_CLIENTS && i < teamlist[TEAM_PLAYERS].numplayers; i++ ) { if( game.clients[teamlist[TEAM_PLAYERS].playerIndices[i]-1].level.stats.score > score_max ) { score_max = game.clients[teamlist[TEAM_PLAYERS].playerIndices[i]-1].level.stats.score; leaders[0] = teamlist[TEAM_PLAYERS].playerIndices[i]; num_leaders = 1; } else if( game.clients[teamlist[TEAM_PLAYERS].playerIndices[i]-1].level.stats.score == score_max ) { leaders[num_leaders++] = teamlist[TEAM_PLAYERS].playerIndices[i]; } } leaders[num_leaders] = 0; } if( !score_announcement_init ) { // copy over to last_leaders memcpy( last_leaders, leaders, sizeof( leaders ) ); score_announcement_init = true; return; } for( e = game.edicts + 1; PLAYERNUM( e ) < gs.maxclients; e++ ) { if( !e->r.client || trap_GetClientState( PLAYERNUM( e ) ) < CS_SPAWNED ) continue; if( e->r.client->resp.chase.active ) chased = &game.edicts[e->r.client->resp.chase.target]; else chased = e; // floating spectator if( chased->s.team == TEAM_SPECTATOR ) { if( !GS_TeamBasedGametype() ) continue; if( last_leaders[1] == 0 && leaders[1] != 0 ) { G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_TIED_LEAD_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); } else if( leaders[1] == 0 && ( last_leaders[0] != leaders[0] || last_leaders[1] != 0 ) ) { //G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_1_to_4_TAKEN_LEAD_1_to_2, // leaders[0]-1, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); } continue; } // in the game or chasing someone who is if( G_WasLeading( chased ) && !G_IsLeading( chased ) ) { if( GS_TeamBasedGametype() && !GS_InvidualGameType() ) G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_LOST_LEAD_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); else G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_LOST_LEAD_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); } else if( ( !G_WasLeading( chased ) || ( last_leaders[1] != 0 ) ) && G_IsLeading( chased ) && ( leaders[1] == 0 ) ) { if( GS_TeamBasedGametype() && !GS_InvidualGameType() ) G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_TAKEN_LEAD_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); else G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_TAKEN_LEAD_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); } else if( ( !G_WasLeading( chased ) || ( last_leaders[1] == 0 ) ) && G_IsLeading( chased ) && ( leaders[1] != 0 ) ) { if( GS_TeamBasedGametype() && !GS_InvidualGameType() ) G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_TIED_LEAD_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); else G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_TIED_LEAD_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); } } // copy over to last_leaders memcpy( last_leaders, leaders, sizeof( leaders ) ); }
/* * G_PlayerWorldEffects */ static void G_PlayerWorldEffects( edict_t *ent ) { int waterlevel, old_waterlevel; int watertype, old_watertype; if( ent->movetype == MOVETYPE_NOCLIP ) { ent->air_finished = level.time + ( 12*1000 ); // don't need air return; } waterlevel = ent->waterlevel; watertype = ent->watertype; old_waterlevel = ent->r.client->resp.old_waterlevel; old_watertype = ent->r.client->resp.old_watertype; ent->r.client->resp.old_waterlevel = waterlevel; ent->r.client->resp.old_watertype = watertype; // // if just entered a water volume, play a sound // if( !old_waterlevel && waterlevel ) { if( ent->watertype & CONTENTS_LAVA ) G_Sound( ent, CHAN_AUTO, trap_SoundIndex( S_WORLD_LAVA_IN ), ATTN_NORM ); else if( ent->watertype & CONTENTS_SLIME ) G_Sound( ent, CHAN_AUTO, trap_SoundIndex( S_WORLD_SLIME_IN ), ATTN_NORM ); else if( ent->watertype & CONTENTS_WATER ) G_Sound( ent, CHAN_AUTO, trap_SoundIndex( S_WORLD_WATER_IN ), ATTN_NORM ); ent->flags |= FL_INWATER; } // // if just completely exited a water volume, play a sound // if( old_waterlevel && !waterlevel ) { if( old_watertype & CONTENTS_LAVA ) G_Sound( ent, CHAN_AUTO, trap_SoundIndex( S_WORLD_LAVA_OUT ), ATTN_NORM ); else if( old_watertype & CONTENTS_SLIME ) G_Sound( ent, CHAN_AUTO, trap_SoundIndex( S_WORLD_SLIME_OUT ), ATTN_NORM ); else if( old_watertype & CONTENTS_WATER ) G_Sound( ent, CHAN_AUTO, trap_SoundIndex( S_WORLD_WATER_OUT ), ATTN_NORM ); ent->flags &= ~FL_INWATER; } // // check for head just coming out of water // if( old_waterlevel == 3 && waterlevel != 3 ) { if( ent->air_finished < level.time ) { // gasp for air // wsw : jal : todo : better variations of gasp sounds G_AddEvent( ent, EV_SEXEDSOUND, 1, true ); } else if( ent->air_finished < level.time + 11000 ) { // just break surface // wsw : jal : todo : better variations of gasp sounds G_AddEvent( ent, EV_SEXEDSOUND, 2, true ); } } // // check for drowning // if( waterlevel == 3 ) { // if out of air, start drowning if( ent->air_finished < level.time ) { // drown! if( ent->r.client->resp.next_drown_time < level.time && !G_IsDead( ent ) ) { ent->r.client->resp.next_drown_time = level.time + 1000; // take more damage the longer underwater ent->r.client->resp.drowningDamage += 2; if( ent->r.client->resp.drowningDamage > 15 ) ent->r.client->resp.drowningDamage = 15; // wsw : jal : todo : better variations of gasp sounds // play a gurp sound instead of a normal pain sound if( HEALTH_TO_INT( ent->health ) - ent->r.client->resp.drowningDamage <= 0 ) G_AddEvent( ent, EV_SEXEDSOUND, 2, true ); else G_AddEvent( ent, EV_SEXEDSOUND, 1, true ); ent->pain_debounce_time = level.time; G_Damage( ent, world, world, vec3_origin, vec3_origin, ent->s.origin, ent->r.client->resp.drowningDamage, 0, 0, DAMAGE_NO_ARMOR, MOD_WATER ); } } } else { ent->air_finished = level.time + 12000; ent->r.client->resp.drowningDamage = 2; } // // check for sizzle damage // if( waterlevel && ( ent->watertype & ( CONTENTS_LAVA|CONTENTS_SLIME ) ) ) { if( ent->watertype & CONTENTS_LAVA ) { // wsw: Medar: We don't have the sounds yet and this seems to overwrite the normal pain sounds //if( !G_IsDead(ent) && ent->pain_debounce_time <= level.time ) //{ // G_Sound( ent, CHAN_BODY, trap_SoundIndex(va(S_PLAYER_BURN_1_to_2, (rand()&1)+1)), 1, ATTN_NORM ); // ent->pain_debounce_time = level.time + 1000; //} G_Damage( ent, world, world, vec3_origin, vec3_origin, ent->s.origin, ( 30 * waterlevel ) * game.snapFrameTime / 1000.0f, 0, 0, 0, MOD_LAVA ); } if( ent->watertype & CONTENTS_SLIME ) { G_Damage( ent, world, world, vec3_origin, vec3_origin, ent->s.origin, ( 10 * waterlevel ) * game.snapFrameTime / 1000.0f, 0, 0, 0, MOD_SLIME ); } } }