void ACEND_SetGoal(gentity_t * self, int goalNode) { int node; self->bs.goalNode = goalNode; node = ACEND_FindClosestReachableNode(self, NODE_DENSITY * 3, NODE_ALL); if(node == INVALID) return; if(ace_debug.integer) trap_SendServerCommand(-1, va("print \"%s: new start node selected %d\n\"", self->client->pers.netname, node)); self->bs.currentNode = node; self->bs.nextNode = self->bs.currentNode; // make sure we get to the nearest node first self->bs.node_timeout = 0; if(ace_showPath.integer) { // draw path to LR goal ACEND_DrawPath(self->bs.currentNode, self->bs.goalNode); } }
/* ================== CheckVote ================== */ void CheckVote( void ) { if ( level.voteExecuteTime && level.voteExecuteTime < level.time ) { level.voteExecuteTime = 0; trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", level.voteString ) ); } if ( !level.voteTime ) { return; } if ( level.time - level.voteTime >= VOTE_TIME ) { if(g_dmflags.integer & DF_LIGHT_VOTING) { //Let pass if there was at least twice as many for as against if ( level.voteYes > level.voteNo*2 ) { trap_SendServerCommand( -1, "print \"Vote passed. At least 2 of 3 voted yes\n\"" ); level.voteExecuteTime = level.time + 3000; } else { //Let pass if there is more yes than no and at least 2 yes votes and at least 30% yes of all on the server if ( level.voteYes > level.voteNo && level.voteYes >= 2 && (level.voteYes*10)>(level.numVotingClients*3) ) { trap_SendServerCommand( -1, "print \"Vote passed. More yes than no.\n\"" ); level.voteExecuteTime = level.time + 3000; } else trap_SendServerCommand( -1, "print \"Vote failed.\n\"" ); } } else { trap_SendServerCommand( -1, "print \"Vote failed.\n\"" ); } } else { // ATVI Q3 1.32 Patch #9, WNF if ( level.voteYes > (level.numVotingClients)/2 ) { // execute the command, then remove the vote trap_SendServerCommand( -1, "print \"Vote passed.\n\"" ); level.voteExecuteTime = level.time + 3000; } else if ( level.voteNo >= (level.numVotingClients)/2 ) { // same behavior as a timeout trap_SendServerCommand( -1, "print \"Vote failed.\n\"" ); } else { // still waiting for a majority return; } } level.voteTime = 0; trap_SetConfigstring( CS_VOTE_TIME, "" ); }
/* ================== TeamplayLocationsMessage Format: clientNum location health armor weapon powerups ================== */ void TeamplayInfoMessage( gentity_t *ent ) { char entry[1024]; char string[8192]; int stringlength; int i, j; gentity_t *player; int cnt; int h, a; int clients[TEAM_MAXOVERLAY]; int team; if ( ! ent->client->pers.teamInfo ) return; // send team info to spectator for team of followed client if (ent->client->sess.sessionTeam == TEAM_SPECTATOR) { if ( ent->client->sess.spectatorState != SPECTATOR_FOLLOW || ent->client->sess.spectatorClient < 0 ) { return; } team = g_entities[ ent->client->sess.spectatorClient ].client->sess.sessionTeam; } else { team = ent->client->sess.sessionTeam; } if (team != TEAM_RED && team != TEAM_BLUE) { return; } // figure out what client should be on the display // we are limited to 8, but we want to use the top eight players // but in client order (so they don't keep changing position on the overlay) for (i = 0, cnt = 0; i < sv_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++) { player = g_entities + level.sortedClients[i]; if (player->inuse && player->client->sess.sessionTeam == team ) { clients[cnt++] = level.sortedClients[i]; } } // We have the top eight players, sort them by clientNum qsort( clients, cnt, sizeof( clients[0] ), SortClients ); // send the latest information on all clients string[0] = 0; stringlength = 0; for (i = 0, cnt = 0; i < sv_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++) { player = g_entities + i; if (player->inuse && player->client->sess.sessionTeam == team ) { h = player->client->ps.stats[STAT_HEALTH]; a = player->client->ps.stats[STAT_ARMOR]; if (h < 0) h = 0; if (a < 0) a = 0; Com_sprintf (entry, sizeof(entry), " %i %i %i %i %i %i", // level.sortedClients[i], player->client->pers.teamState.location, h, a, i, player->client->pers.teamState.location, h, a, player->client->ps.weapon, player->s.powerups); j = strlen(entry); if (stringlength + j >= sizeof(string)) break; strcpy (string + stringlength, entry); stringlength += j; cnt++; } } trap_SendServerCommand( ent-g_entities, va("tinfo %i %s", cnt, string) ); }
/* ================== player_die ================== */ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int meansOfDeath ) { gentity_t *ent; int anim; int killer; int i; const char *killerName, *obit; if ( self->client->ps.pm_type == PM_DEAD ) { return; } if ( level.intermissiontime ) { return; } self->client->ps.pm_type = PM_DEAD; self->suicideTime = 0; if ( attacker ) { killer = attacker->s.number; if ( attacker->client ) { killerName = attacker->client->pers.netname; } else { killerName = "<world>"; } } else { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( meansOfDeath < 0 || meansOfDeath >= ARRAY_LEN( modNames ) ) { // fall back on the number obit = va( "%d", meansOfDeath ); } else { obit = modNames[ meansOfDeath ]; } G_LogPrintf( "Die: %d %d %s: %s" S_COLOR_WHITE " killed %s\n", killer, ( int )( self - g_entities ), obit, killerName, self->client->pers.netname ); // deactivate all upgrades for ( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ ) { BG_DeactivateUpgrade( i, self->client->ps.stats ); } // broadcast the death event to everyone ent = G_NewTempEntity( self->r.currentOrigin, EV_OBITUARY ); ent->s.eventParm = meansOfDeath; ent->s.otherEntityNum = self->s.number; ent->s.otherEntityNum2 = killer; ent->r.svFlags = SVF_BROADCAST; // send to everyone if ( attacker && attacker->client ) { if ( ( attacker == self || OnSameTeam( self, attacker ) ) ) { //punish team kills and suicides if ( attacker->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) { G_AddCreditToClient( attacker->client, -ALIEN_TK_SUICIDE_PENALTY, qtrue ); G_AddCreditsToScore( attacker, -ALIEN_TK_SUICIDE_PENALTY ); } else if ( attacker->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { G_AddCreditToClient( attacker->client, -HUMAN_TK_SUICIDE_PENALTY, qtrue ); G_AddCreditsToScore( attacker, -HUMAN_TK_SUICIDE_PENALTY ); } } else if ( g_showKillerHP.integer ) { trap_SendServerCommand( self - g_entities, va( "print_tr %s %s %3i", QQ( N_("Your killer, $1$^7, had $2$ HP.\n") ), Quote( killerName ), attacker->health ) ); } } else if ( attacker->s.eType != ET_BUILDABLE ) { if ( self->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) { G_AddCreditsToScore( self, -ALIEN_TK_SUICIDE_PENALTY ); } else if ( self->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { G_AddCreditsToScore( self, -HUMAN_TK_SUICIDE_PENALTY ); } } // give credits for killing this player G_RewardAttackers( self ); ScoreboardMessage( self ); // show scores // send updated scores to any clients that are following this one, // or they would get stale scoreboards for ( i = 0; i < level.maxclients; i++ ) { gclient_t *client; client = &level.clients[ i ]; if ( client->pers.connected != CON_CONNECTED ) { continue; } if ( client->sess.spectatorState == SPECTATOR_NOT ) { continue; } if ( client->sess.spectatorClient == self->s.number ) { ScoreboardMessage( g_entities + i ); } } VectorCopy( self->s.origin, self->client->pers.lastDeathLocation ); self->takedamage = qfalse; // can still be gibbed self->s.weapon = WP_NONE; if ( self->client->noclip ) { self->client->cliprcontents = CONTENTS_CORPSE; } else { self->r.contents = CONTENTS_CORPSE; } self->s.angles[ PITCH ] = 0; self->s.angles[ ROLL ] = 0; self->s.angles[ YAW ] = self->s.apos.trBase[ YAW ]; LookAtKiller( self, inflictor, attacker ); VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; self->r.maxs[ 2 ] = -8; // don't allow respawn until the death anim is done // g_forcerespawn may force spawning at some later time self->client->respawnTime = level.time + 1700; // clear misc memset( self->client->ps.misc, 0, sizeof( self->client->ps.misc ) ); { static int i; if ( !( self->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL ) ) { switch ( i ) { case 0: anim = BOTH_DEATH1; break; case 1: anim = BOTH_DEATH2; break; case 2: default: anim = BOTH_DEATH3; break; } } else { switch ( i ) { case 0: anim = NSPA_DEATH1; break; case 1: anim = NSPA_DEATH2; break; case 2: default: anim = NSPA_DEATH3; break; } } self->client->ps.legsAnim = ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; if ( !( self->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL ) ) { self->client->ps.torsoAnim = ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; } // use own entityid if killed by non-client to prevent uint8_t overflow G_AddEvent( self, EV_DEATH1 + i, ( killer < MAX_CLIENTS ) ? killer : self - g_entities ); // globally cycle through the different death animations i = ( i + 1 ) % 3; } trap_LinkEntity( self ); self->client->pers.infoChangeTime = level.time; }
/*QUAKED target_print (1 0 0) (-8 -8 -8) (8 8 8) redteam blueteam private "message" text to print "wait" don't fire off again if triggered within this many milliseconds ago If "private", only the activator gets the message. If no checks, all clients get the message. */ void Use_Target_Print (gentity_t *ent, gentity_t *other, gentity_t *activator) { if (!ent || !ent->inuse) { Com_Printf("ERROR: Bad ent in Use_Target_Print"); return; } if (ent->wait) { if (ent->genericValue14 >= level.time) { return; } ent->genericValue14 = level.time + ent->wait; } #ifndef FINAL_BUILD if (!ent || !ent->inuse) { Com_Error(ERR_DROP, "Bad ent in Use_Target_Print"); } else if (!activator || !activator->inuse) { Com_Error(ERR_DROP, "Bad activator in Use_Target_Print"); } /*if (ent->genericValue15 > level.time) // lol wtf is this bullshit { Com_Printf("TARGET PRINT ERRORS:\n"); if (activator && activator->classname && activator->classname[0]) { Com_Printf("activator classname: %s\n", activator->classname); } if (activator && activator->target && activator->target[0]) { Com_Printf("activator target: %s\n", activator->target); } if (activator && activator->targetname && activator->targetname[0]) { Com_Printf("activator targetname: %s\n", activator->targetname); } if (ent->targetname && ent->targetname[0]) { Com_Printf("print targetname: %s\n", ent->targetname); } Com_Error(ERR_DROP, "target_print used in quick succession, fix it! See the console for details."); } ent->genericValue15 = level.time + 5000;*/ #endif G_ActivateBehavior(ent,BSET_USE); if ( ( ent->spawnflags & 4 ) ) {//private, to one client only if (!activator || !activator->inuse) { Com_Printf("ERROR: Bad activator in Use_Target_Print"); } if ( activator && activator->client ) {//make sure there's a valid client ent to send it to if (ent->message[0] == '@' && ent->message[1] != '@') { trap_SendServerCommand( activator-g_entities, va("cps \"%s\"", ent->message )); } else { trap_SendServerCommand( activator-g_entities, va("cp \"%s\"", ent->message )); } } //NOTE: change in functionality - if there *is* no valid client ent, it won't send it to anyone at all return; } if ( ent->spawnflags & 3 ) { if ( ent->spawnflags & 1 ) { if (ent->message[0] == '@' && ent->message[1] != '@') { G_TeamCommand( TEAM_RED, va("cps \"%s\"", ent->message) ); } else { G_TeamCommand( TEAM_RED, va("cp \"%s\"", ent->message) ); } } if ( ent->spawnflags & 2 ) { if (ent->message[0] == '@' && ent->message[1] != '@') { G_TeamCommand( TEAM_BLUE, va("cps \"%s\"", ent->message) ); } else { G_TeamCommand( TEAM_BLUE, va("cp \"%s\"", ent->message) ); } } return; } if (ent->message[0] == '@' && ent->message[1] != '@') { trap_SendServerCommand( -1, va("cps \"%s\"", ent->message )); } else { trap_SendServerCommand( -1, va("cp \"%s\"", ent->message )); } }
void Svcmd_CamCmd( void ) { char buf[MAX_TOKEN_CHARS]; char cmd[MAX_TOKEN_CHARS]; char name[MAX_TOKEN_CHARS]; int i; gclient_t* cl; if( !level.cammode ) { return; } if( trap_Argc() < 2 ) { return; } trap_Argv( 1, cmd, sizeof(cmd) ); if ( !Q_stricmp (cmd, "print") ) { trap_Argv( 2, buf, sizeof(buf) ); trap_SendServerCommand( -1, va("cp \"%s\n\"", buf ) ); } else if( !Q_stricmp (cmd, "setclientpos") ) { vec3_t newOrigin; if( trap_Argc() != 8 && trap_Argc() != 6 ) { Com_Printf("usage: camcmd setclientpos name/id x y z (a b) \na = PITCH-angle, b = YAW-angle\n"); return; } trap_Argv( 2, name, sizeof( name ) ); cl = ClientForString( name ) ; if(!cl) return; for( i=0; i<3; i++) { trap_Argv( i+3, buf, sizeof( buf) ); newOrigin[i] = atof( buf ); } G_SetOrigin( &g_entities[cl->ps.clientNum], newOrigin ); VectorCopy( newOrigin, cl->ps.origin ); if(trap_Argc() == 8) { vec3_t newAngles; memset(newAngles,0,sizeof(newAngles)); trap_Argv( 6, buf, sizeof( buf ) ); newAngles[PITCH] = atoi(buf); trap_Argv( 7, buf, sizeof( buf ) ); newAngles[YAW] = atoi(buf); SetClientViewAngle( &g_entities[cl->ps.clientNum], newAngles ); } } else if( !Q_stricmp (cmd, "setspawn") ) { if( trap_Argc() != 8 ) { Com_Printf("usage: camcmd setspawn x y z a b c \n"); return; } for( i=0; i<3; i++) { trap_Argv( i+2, buf, sizeof(buf) ); level.cam_spawnpos[i] = atof(buf); } for( i=0; i<3; i++) { trap_Argv( i+5, buf, sizeof(buf) ); level.cam_spawnangles[i] = atof(buf); } } else if( !Q_stricmp (cmd, "botmove") ) { vec3_t pos; if( trap_Argc() != 6 ) { Com_Printf("usage: camcmd botmove name x y z \n"); return; } trap_Argv( 2, name, sizeof( name ) ); for( i=0; i<3; i++) { trap_Argv( i+3, buf, sizeof( buf) ); pos[i] = atof( buf ); } cl = ClientForString( name ) ; if(!cl) return; BotCamMoveTo( cl->ps.clientNum, pos ); } else if( !Q_stricmp (cmd, "botviewangles") ) { vec3_t angles; if( trap_Argc() != 5 ) { Com_Printf("usage: camcmd botviewangles name x y \n"); return; } trap_Argv( 2, name, sizeof( name ) ); cl = ClientForString( name ) ; if(!cl) return; trap_Argv( 3, buf, sizeof( buf ) ); angles[PITCH] = atoi(buf); trap_Argv( 4, buf, sizeof( buf ) ); angles[YAW] = atoi(buf); BotCamViewangles( cl->ps.clientNum, angles ); } else if( !Q_stricmp (cmd, "botviewtarget") ) { vec3_t target; if( trap_Argc() != 6 ) { Com_Printf("usage: camcmd botviewtarget name x y z \n"); return; } trap_Argv( 2, name, sizeof( name ) ); cl = ClientForString( name ) ; if(!cl) return; trap_Argv( 3, buf, sizeof( buf ) ); target[0] = atoi(buf); trap_Argv( 4, buf, sizeof( buf ) ); target[1] = atoi(buf); trap_Argv( 5, buf, sizeof( buf ) ); target[2] = atoi(buf); BotCamViewTarget( cl->ps.clientNum, target ); } else if( !Q_stricmp (cmd, "botviewentity") ) { // int target_bot; gclient_t* target_cl; if( trap_Argc() != 4 ) { Com_Printf("usage: camcmd botviewentity name targetname \n"); return; } trap_Argv( 2, name, sizeof( name ) ); cl = ClientForString( name ) ; if(!cl) return; trap_Argv( 3, buf, sizeof( buf ) ); target_cl = ClientForString( buf ); if(!target_cl) return; BotCamViewEntitiy( cl->ps.clientNum, target_cl->ps.clientNum ); } else if( !Q_stricmp (cmd, "boteditinv") ) { if( trap_Argc() < 3) return; trap_Argv( 2, name, sizeof(name) ); cl = ClientForString( name ); if( !cl ) return; EditPlayerInventory( &g_entities[cl->ps.clientNum], 3 ); } else if( !Q_stricmp (cmd, "botchooseweap") ) { if( trap_Argc() != 4 ) { Com_Printf("usage: camcmd botchooseweap name weapID \n"); return; } trap_Argv( 2, name, sizeof( name ) ); cl = ClientForString( name ) ; if(!cl) return; trap_Argv( 3, buf, sizeof( buf ) ); BotChooseWeap( cl->ps.clientNum, atoi(buf) ); } else if( !Q_stricmp (cmd, "bottaunt") ) { if( trap_Argc() != 3 ) { Com_Printf("usage: camcmd bottaunt name \n"); return; } trap_Argv( 2, name, sizeof( name ) ); cl = ClientForString( name ) ; if(!cl) return; BotCamTaunt( cl->ps.clientNum ); } else if( !Q_stricmp(cmd, "botfire") ) { if( trap_Argc() != 3 ) { Com_Printf("usage: camcmd botfire name \n"); return; } trap_Argv( 2, name, sizeof( name ) ); cl = ClientForString( name ) ; if(!cl) return; BotCamFire( cl->ps.clientNum ); } else if( !Q_stricmp(cmd, "freeze") ) { int on; if( trap_Argc() != 3 ) { Com_Printf("usage: camcmd freeze [0|1] \n"); return; } trap_Argv( 2, name, sizeof( name ) ); on = atoi( name ); FreezePlayers( on ); } }
/* ============== ClientThink This will be called once for each client frame, which will usually be a couple times for each server frame on fast clients. If "g_synchronousClients 1" is set, this will be called exactly once for each server frame, which makes for smooth demo recording. ============== */ void ClientThink_real(gentity_t *ent) { int msec, oldEventSequence, speed, i, counter, Zaccel; pmove_t pm; usercmd_t *ucmd; gclient_t *client = ent->client; // don't think if the client is not yet connected (and thus not yet spawned in) if (client->pers.connected != CON_CONNECTED) { return; } if (ent->s.eFlags & EF_MOUNTEDTANK) { client->pmext.centerangles[YAW] = ent->tagParent->r.currentAngles[YAW]; client->pmext.centerangles[PITCH] = ent->tagParent->r.currentAngles[PITCH]; } // mark the time, so the connection sprite can be removed ucmd = &ent->client->pers.cmd; ent->client->ps.identifyClient = ucmd->identClient; // NERVE - SMF // sanity check the command time to prevent speedup cheating if (ucmd->serverTime > level.time + 200) { ucmd->serverTime = level.time + 200; } if (ucmd->serverTime < level.time - 1000) { ucmd->serverTime = level.time - 1000; } msec = ucmd->serverTime - client->ps.commandTime; // following others may result in bad times, but we still want // to check for follow toggles if (msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW) { return; } if (msec > 200) { msec = 200; } // Nico, pmove_fixed if (client->pers.pmoveFixed) { ucmd->serverTime = ((ucmd->serverTime + pmove_msec.integer - 1) / pmove_msec.integer) * pmove_msec.integer; } if (client->wantsscore) { G_SendScore(ent); client->wantsscore = qfalse; } // check for inactivity timer, but never drop the local client of a non-dedicated server // OSP - moved here to allow for spec inactivity checks as well if (!ClientInactivityTimer(client)) { return; } if (!(ucmd->flags & 0x01) || ucmd->forwardmove || ucmd->rightmove || ucmd->upmove || ucmd->wbuttons || ucmd->doubleTap) { ent->r.svFlags &= ~(SVF_SELF_PORTAL_EXCLUSIVE | SVF_SELF_PORTAL); } // spectators don't do much // DHM - Nerve :: In limbo use SpectatorThink // suburb, check for noclip not to override client->ps.speed if ((client->sess.sessionTeam == TEAM_SPECTATOR || client->ps.pm_flags & PMF_LIMBO) && !client->noclip) { SpectatorThink(ent, ucmd); return; } if (client->ps.eFlags & EF_VIEWING_CAMERA) { ucmd->buttons = 0; ucmd->forwardmove = 0; ucmd->rightmove = 0; ucmd->upmove = 0; ucmd->wbuttons = 0; ucmd->doubleTap = 0; // freeze player client->ps.pm_type = PM_FREEZE; } else if (client->noclip) { client->ps.pm_type = PM_NOCLIP; } else if (client->ps.stats[STAT_HEALTH] <= 0) { client->ps.pm_type = PM_DEAD; } else { client->ps.pm_type = PM_NORMAL; } client->ps.aiState = AISTATE_COMBAT; client->ps.gravity = DEFAULT_GRAVITY; client->ps.speed = DEFAULT_SPEED; if (client->speedScale) { // Goalitem speed scale client->ps.speed *= (client->speedScale * 0.01); } // set up for pmove oldEventSequence = client->ps.eventSequence; client->currentAimSpreadScale = (float)client->ps.aimSpreadScale / 255.0; memset(&pm, 0, sizeof (pm)); pm.ps = &client->ps; pm.pmext = &client->pmext; pm.character = client->pers.character; pm.cmd = *ucmd; pm.oldcmd = client->pers.oldcmd; // MrE: always use capsule for AI and player pm.trace = trap_TraceCapsule; // Nico, ghost players pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; if (pm.ps->pm_type == PM_DEAD) { pm.ps->eFlags |= EF_DEAD; } else if (pm.ps->pm_type == PM_SPECTATOR) { pm.trace = trap_TraceCapsuleNoEnts; } // Nico, end of ghost players //DHM - Nerve :: We've gone back to using normal bbox traces pm.pointcontents = trap_PointContents; pm.debugLevel = g_debugMove.integer; pm.noFootsteps = qfalse; // Nico, pmove_fixed // pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed; pm.pmove_fixed = client->pers.pmoveFixed; pm.pmove_msec = pmove_msec.integer; // suburb, Noclip speed scale pm.noclipSpeed = client->pers.noclipSpeed; // Nico, game physics pm.physics = physics.integer; pm.isTimerun = isTimerun.integer; pm.timerunActive = client->sess.timerunActive; pm.timerunStartTime = client->sess.timerunStartTime + 500; // Nico, store logins status in pmove if (client->sess.logged) { pm.isLogged = 1; } else { pm.isLogged = 0; } pm.noWeapClips = qfalse; VectorCopy(client->ps.origin, client->oldOrigin); // NERVE - SMF pm.ltChargeTime = level.lieutenantChargeTime[client->sess.sessionTeam - 1]; pm.soldierChargeTime = level.soldierChargeTime[client->sess.sessionTeam - 1]; pm.engineerChargeTime = level.engineerChargeTime[client->sess.sessionTeam - 1]; pm.medicChargeTime = level.medicChargeTime[client->sess.sessionTeam - 1]; // -NERVE - SMF client->pmext.airleft = ent->client->airOutTime - level.time; pm.covertopsChargeTime = level.covertopsChargeTime[client->sess.sessionTeam - 1]; // Gordon: bit hacky, stop the slight lag from client -> server even on locahost, switching back to the weapon you were holding // and then back to what weapon you should have, became VERY noticible for the kar98/carbine + gpg40, esp now i've added the // animation locking if (level.time - client->pers.lastSpawnTime < 1000) { pm.cmd.weapon = client->ps.weapon; } Pmove(&pm); // Gordon: thx to bani for this // ikkyo - fix leaning players bug VectorCopy(client->ps.velocity, ent->s.pos.trDelta); SnapVector(ent->s.pos.trDelta); // end // server cursor hints if (ent->lastHintCheckTime < level.time) { G_CheckForCursorHints(ent); ent->lastHintCheckTime = level.time + FRAMETIME; } // DHM - Nerve :: Set animMovetype to 1 if ducking if (ent->client->ps.pm_flags & PMF_DUCKED) { ent->s.animMovetype = 1; } else { ent->s.animMovetype = 0; } // save results of pmove if (ent->client->ps.eventSequence != oldEventSequence) { ent->eventTime = level.time; ent->r.eventTime = level.time; } // Ridah, fixes jittery zombie movement if (g_smoothClients.integer) { BG_PlayerStateToEntityStateExtraPolate(&ent->client->ps, &ent->s, level.time, qfalse); } else { BG_PlayerStateToEntityState(&ent->client->ps, &ent->s, qfalse); } if (!(ent->client->ps.eFlags & EF_FIRING)) { client->fireHeld = qfalse; // for grapple } // // use the snapped origin for linking so it matches client predicted versions VectorCopy(ent->s.pos.trBase, ent->r.currentOrigin); VectorCopy(pm.mins, ent->r.mins); VectorCopy(pm.maxs, ent->r.maxs); ent->waterlevel = pm.waterlevel; ent->watertype = pm.watertype; // execute client events ClientEvents(ent, oldEventSequence); // link entity now, after any personal teleporters have been used trap_LinkEntity(ent); if (!ent->client->noclip) { G_TouchTriggers(ent); } // NOTE: now copy the exact origin over otherwise clients can be snapped into solid VectorCopy(ent->client->ps.origin, ent->r.currentOrigin); // touch other objects ClientImpacts(ent, &pm); // save results of triggers and client events if (ent->client->ps.eventSequence != oldEventSequence) { ent->eventTime = level.time; } // swap and latch button actions client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; client->latched_buttons = client->buttons & ~client->oldbuttons; //----(SA) added client->oldwbuttons = client->wbuttons; client->wbuttons = ucmd->wbuttons; client->latched_wbuttons = client->wbuttons & ~client->oldwbuttons; // suburb, return here for noclippers in spec to avoid following checks if (client->sess.sessionTeam == TEAM_SPECTATOR && client->noclip) { return; } // Rafael - Activate // Ridah, made it a latched event (occurs on keydown only) if (client->latched_buttons & BUTTON_ACTIVATE) { Cmd_Activate_f(ent); } if (g_entities[ent->client->ps.identifyClient].team != ent->team || !g_entities[ent->client->ps.identifyClient].client) { ent->client->ps.identifyClient = -1; } // check for respawning if (client->ps.stats[STAT_HEALTH] <= 0) { // Nico, forcing respawn limbo(ent); return; } // perform once-a-second actions ClientTimerActions(ent, msec); // Nico, check ping if (client->ps.ping > MAX_PLAYER_PING) { if (!client->pers.loadKillNeeded) { CP(va("cpm \"%s^w: ^1Too high ping detected, load or kill required.\n\"", GAME_VERSION_COLORED)); // suburb, prevent trigger bug client->pers.loadKillNeeded = qtrue; } } // Nico, pmove_fixed if (!client->pers.pmoveFixed) { CP(va("cpm \"%s^w: ^1You were removed from teams because you can not use pmove_fixed 0.\n\"", GAME_VERSION_COLORED)); trap_SendServerCommand(ent - g_entities, "pmoveon"); SetTeam(ent, "s", -1, -1, qfalse); } // Nico, check rate if (client->pers.rate < MIN_PLAYER_RATE_VALUE || client->pers.rate > MAX_PLAYER_RATE_VALUE) { CP(va("cpm \"%s^w: ^1You were removed from teams because you must use %d <= rate <= %d.\n\"", GAME_VERSION_COLORED, MIN_PLAYER_RATE_VALUE, MAX_PLAYER_RATE_VALUE)); trap_SendServerCommand(ent - g_entities, "resetRate"); SetTeam(ent, "s", -1, -1, qfalse); } // Nico, check snaps (unsigned int) if (client->pers.snaps > MAX_PLAYER_SNAPS_VALUE) { CP(va("cpm \"%s^w: ^1You were removed from teams because you must use %d <= snaps <= %d.\n\"", GAME_VERSION_COLORED, MIN_PLAYER_SNAPS_VALUE, MAX_PLAYER_SNAPS_VALUE)); trap_SendServerCommand(ent - g_entities, "resetSnaps"); SetTeam(ent, "s", -1, -1, qfalse); } // Nico, check timenudge if (client->pers.clientTimeNudge != FORCED_PLAYER_TIMENUDGE_VALUE) { CP(va("cpm \"%s^w: ^1You were removed from teams because you must use cl_timenudge %d.\n\"", GAME_VERSION_COLORED, FORCED_PLAYER_TIMENUDGE_VALUE)); trap_SendServerCommand(ent - g_entities, "resetTimeNudge"); SetTeam(ent, "s", -1, -1, qfalse); } // Nico, check maxpackets if (client->pers.clientMaxPackets < MIN_PLAYER_MAX_PACKETS_VALUE || client->pers.clientMaxPackets > MAX_PLAYER_MAX_PACKETS_VALUE) { CP(va("cpm \"%s^w: ^1You were removed from teams because you must use %d <= cl_maxpackets <= %d.\n\"", GAME_VERSION_COLORED, MIN_PLAYER_MAX_PACKETS_VALUE, MAX_PLAYER_MAX_PACKETS_VALUE)); trap_SendServerCommand(ent - g_entities, "resetMaxPackets"); SetTeam(ent, "s", -1, -1, qfalse); } // Nico, check max FPS if (client->pers.maxFPS < MIN_PLAYER_FPS_VALUE || client->pers.maxFPS > MAX_PLAYER_FPS_VALUE) { CP(va("cpm \"%s^w: ^1You were removed from teams because you must use %d <= com_maxfps <= %d.\n\"", GAME_VERSION_COLORED, MIN_PLAYER_FPS_VALUE, MAX_PLAYER_FPS_VALUE)); trap_SendServerCommand(ent - g_entities, "resetMaxFPS"); SetTeam(ent, "s", -1, -1, qfalse); } // suburb, force yawspeed 0 if (client->pers.yawspeed != 0) { CP(va("cpm \"%s^w: ^1You were removed from teams because you must use cl_yawspeed 0.\n\"", GAME_VERSION_COLORED)); trap_SendServerCommand(ent - g_entities, "resetYawspeed"); SetTeam(ent, "s", -1, -1, qfalse); } // suburb, force pitchspeed 0 if (client->pers.pitchspeed != 0) { CP(va("cpm \"%s^w: ^1You were removed from teams because you must use cl_pitchspeed 0.\n\"", GAME_VERSION_COLORED)); trap_SendServerCommand(ent - g_entities, "resetPitchspeed"); SetTeam(ent, "s", -1, -1, qfalse); } // Nico, force auto demo record in cup mode if (g_cupMode.integer != 0 && client->pers.autoDemo == 0) { CP(va("cpm \"%s^w: ^1You were removed from teams because you must use cg_autoDemo 1.\n\"", GAME_VERSION_COLORED)); trap_SendServerCommand(ent - g_entities, "autoDemoOn"); SetTeam(ent, "s", -1, -1, qfalse); } // Nico, force hide me in cup mode if (g_cupMode.integer != 0 && client->pers.hideme == 0) { CP(va("cpm \"%s^w: ^1You were removed from teams because you must use cg_hideMe 1.\n\"", GAME_VERSION_COLORED)); trap_SendServerCommand(ent - g_entities, "hideMeOn"); SetTeam(ent, "s", -1, -1, qfalse); } // suburb, force CGaz if disabled on server if (g_disableCGaz.integer != 0 && client->pers.cgaz != 0) { CP(va("cpm \"%s^w: ^1You were removed from teams because you must use cg_drawCGaz 0.\n\"", GAME_VERSION_COLORED)); trap_SendServerCommand(ent - g_entities, "CGazOff"); SetTeam(ent, "s", -1, -1, qfalse); } // suburb, force snapping hud if disabled on server or logged in if ((g_disableSnappingHUD.integer != 0 || (client->sess.logged && (client->sess.sessionTeam == TEAM_AXIS || client->sess.sessionTeam == TEAM_ALLIES))) && client->pers.snapping != 0) { CP(va("cpm \"%s^w: ^1You were removed from teams because you must use cg_drawVelocitySnapping 0.\n\"", GAME_VERSION_COLORED)); trap_SendServerCommand(ent - g_entities, "SnappingOff"); SetTeam(ent, "s", -1, -1, qfalse); } // suburb, prevent pronebug & wallbug counter = 0; for (i = 0; i < 3; ++i) { if (client->pers.oldPosition[i] == (int) pm.ps->origin[i]) { counter++; } } Zaccel = (int) pm.ps->velocity[2] - client->pers.oldZvelocity; speed = sqrt(pm.ps->velocity[0] * pm.ps->velocity[0] + pm.ps->velocity[1] * pm.ps->velocity[1]); if (client->sess.logged && ((!client->sess.timerunActive && speed > BUGGING_MAX_SPEED && counter == 3) || // prevent accelerating in brushes (pm.ps->eFlags & EF_PRONE && Zaccel > -6 && Zaccel < 0 && client->ps.groundEntityNum == ENTITYNUM_NONE && !client->pers.isTouchingJumppad))) { // prevent accelerating on steep slopes if (!client->pers.buggedLastFrame) { // only do something the second frame not to break jumppads client->pers.buggedLastFrame = qtrue; return; } if (!client->pers.loadKillNeeded) { CP(va("cpm \"%s^w: ^1Bugging detected, load or kill required.\n\"", GAME_VERSION_COLORED)); client->pers.loadKillNeeded = qtrue; } } // checking acceleration in brushes every frame would break corner skimming if (level.time - client->pers.lastBuggingCheck > BUGGING_CHECK_FREQUENCY) { for (i = 0; i < 3; ++i) { client->pers.oldPosition[i] = (int) pm.ps->origin[i]; client->pers.lastBuggingCheck = level.time; } } client->pers.oldZvelocity = (int) pm.ps->velocity[2]; client->pers.buggedLastFrame = qfalse; }
/* =========== ClientUserInfoChanged Called from ClientConnect when the player first connects and directly by the server system when the player updates a userinfo variable. The game can override any of the settings and call trap_SetUserinfo if desired. ============ */ char *ClientUserinfoChanged( int clientNum, qboolean forceName ) { gentity_t *ent; char *s; char model[ MAX_QPATH ]; char buffer[ MAX_QPATH ]; char filename[ MAX_QPATH ]; char oldname[ MAX_NAME_LENGTH ]; char newname[ MAX_NAME_LENGTH ]; char err[ MAX_STRING_CHARS ]; qboolean revertName = qfalse; gclient_t *client; char userinfo[ MAX_INFO_STRING ]; ent = g_entities + clientNum; client = ent->client; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); // check for malformed or illegal info strings if( !Info_Validate(userinfo) ) { trap_SendServerCommand( ent - g_entities, "disconnect \"illegal or malformed userinfo\n\"" ); trap_DropClient( ent - g_entities, "dropped: illegal or malformed userinfo"); return "Illegal or malformed userinfo"; } // If their userinfo overflowed, tremded is in the process of disconnecting them. // If we send our own disconnect, it won't work, so just return to prevent crashes later // in this function. This check must come after the Info_Validate call. else if( !userinfo[ 0 ] ) return "Empty (overflowed) userinfo"; // stickyspec toggle s = Info_ValueForKey( userinfo, "cg_stickySpec" ); client->pers.stickySpec = atoi( s ) != 0; // set name Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey( userinfo, "name" ); G_ClientCleanName( s, newname, sizeof( newname ) ); if( strcmp( oldname, newname ) ) { if( !forceName && client->pers.namelog->nameChangeTime && level.time - client->pers.namelog->nameChangeTime <= g_minNameChangePeriod.value * 1000 ) { trap_SendServerCommand( ent - g_entities, va( "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"", g_minNameChangePeriod.integer ) ); revertName = qtrue; } else if( !forceName && g_maxNameChanges.integer > 0 && client->pers.namelog->nameChanges >= g_maxNameChanges.integer ) { trap_SendServerCommand( ent - g_entities, va( "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"", g_maxNameChanges.integer ) ); revertName = qtrue; } else if( !forceName && client->pers.namelog->muted ) { trap_SendServerCommand( ent - g_entities, "print \"You cannot change your name while you are muted\n\"" ); revertName = qtrue; } else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) ); revertName = qtrue; } if( revertName ) { Q_strncpyz( client->pers.netname, *oldname ? oldname : "UnnamedPlayer", sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", oldname ); trap_SetUserinfo( clientNum, userinfo ); } else { G_CensorString( client->pers.netname, newname, sizeof( client->pers.netname ), ent ); if( !forceName && client->pers.connected == CON_CONNECTED ) { client->pers.namelog->nameChangeTime = level.time; client->pers.namelog->nameChanges++; } if( *oldname ) { G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\" \"%c%s%c^7\"\n", clientNum, client->pers.ip.str, client->pers.guid, oldname, client->pers.netname, DECOLOR_OFF, client->pers.netname, DECOLOR_ON ); } } G_namelog_update_name( client ); } if( client->pers.classSelection == PCL_NONE ) { //This looks hacky and frankly it is. The clientInfo string needs to hold different //model details to that of the spawning class or the info change will not be //registered and an axis appears instead of the player model. There is zero chance //the player can spawn with the battlesuit, hence this choice. Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( PCL_HUMAN_BSUIT )->modelName, BG_ClassConfig( PCL_HUMAN_BSUIT )->skinName ); } else { Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( client->pers.classSelection )->modelName, BG_ClassConfig( client->pers.classSelection )->skinName ); //model segmentation Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", BG_ClassConfig( client->pers.classSelection )->modelName ); if( G_NonSegModel( filename ) ) client->ps.persistant[ PERS_STATE ] |= PS_NONSEGMODEL; else client->ps.persistant[ PERS_STATE ] &= ~PS_NONSEGMODEL; } Q_strncpyz( model, buffer, sizeof( model ) ); // wallwalk follow s = Info_ValueForKey( userinfo, "cg_wwFollow" ); if( atoi( s ) ) client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGFOLLOW; else client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGFOLLOW; // wallwalk toggle s = Info_ValueForKey( userinfo, "cg_wwToggle" ); if( atoi( s ) ) client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGTOGGLE; else client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGTOGGLE; // always sprint s = Info_ValueForKey( userinfo, "cg_sprintToggle" ); if( atoi( s ) ) client->ps.persistant[ PERS_STATE ] |= PS_SPRINTTOGGLE; else client->ps.persistant[ PERS_STATE ] &= ~PS_SPRINTTOGGLE; // fly speed s = Info_ValueForKey( userinfo, "cg_flySpeed" ); if( *s ) client->pers.flySpeed = atoi( s ); else client->pers.flySpeed = BG_Class( PCL_NONE )->speed; // disable blueprint errors s = Info_ValueForKey( userinfo, "cg_disableBlueprintErrors" ); if( atoi( s ) ) client->pers.disableBlueprintErrors = qtrue; else client->pers.disableBlueprintErrors = qfalse; // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if( atoi( s ) != 0 ) { // teamoverlay was enabled so we need an update if( client->pers.teamInfo == 0 ) client->pers.teamInfo = 1; } else client->pers.teamInfo = 0; s = Info_ValueForKey( userinfo, "cg_unlagged" ); if( !s[0] || atoi( s ) != 0 ) client->pers.useUnlagged = qtrue; else client->pers.useUnlagged = qfalse; Q_strncpyz( client->pers.voice, Info_ValueForKey( userinfo, "voice" ), sizeof( client->pers.voice ) ); // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds Com_sprintf( userinfo, sizeof( userinfo ), "n\\%s\\t\\%i\\model\\%s\\ig\\%16s\\v\\%s", client->pers.netname, client->pers.teamSelection, model, Com_ClientListString( &client->sess.ignoreList ), client->pers.voice ); trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo ); /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/ return NULL; }
void CheckTeamStatus( void ) { int i; gentity_t *loc, *ent; if ( level.time - level.lastTeamLocationTime > TEAM_LOCATION_UPDATE_TIME ) { level.lastTeamLocationTime = level.time; for ( i = 0; i < g_maxclients.integer; i++ ) { ent = g_entities + i; if ( ent->client->pers.connected != CON_CONNECTED ) { continue; } if ( ent->inuse && ( ent->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS || ent->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) ) { loc = Team_GetLocation( ent ); if ( loc ) { ent->client->pers.location = loc->s.generic1; } else { ent->client->pers.location = 0; } } } for ( i = 0; i < g_maxclients.integer; i++ ) { ent = g_entities + i; if ( ent->client->pers.connected != CON_CONNECTED ) { continue; } if ( ent->inuse ) { TeamplayInfoMessage( ent ); } } } // Warn on imbalanced teams if ( g_teamImbalanceWarnings.integer && !level.intermissiontime && ( level.time - level.lastTeamImbalancedTime > ( g_teamImbalanceWarnings.integer * 1000 ) ) && level.numTeamImbalanceWarnings < 3 && !level.restarted ) { level.lastTeamImbalancedTime = level.time; if ( level.numAlienSpawns > 0 && level.numHumanClients - level.numAlienClients > 2 ) { trap_SendServerCommand( -1, "print_tr \"" N_("Teams are imbalanced. " "Humans have more players.\n") "\"" ); level.numTeamImbalanceWarnings++; } else if ( level.numHumanSpawns > 0 && level.numAlienClients - level.numHumanClients > 2 ) { trap_SendServerCommand( -1, "print_tr \"" N_("Teams are imbalanced. " "Aliens have more players.\n") "\"" ); level.numTeamImbalanceWarnings++; } else { level.numTeamImbalanceWarnings = 0; } } }
/* ================== TeamplayInfoMessage Format: clientNum location health weapon upgrade ================== */ void TeamplayInfoMessage( gentity_t *ent ) { char entry[ 19 ], string[ 1143 ]; int i, j; int team, stringlength; int sent = 0; gentity_t *player; gclient_t *cl; upgrade_t upgrade = UP_NONE; int curWeaponClass = WP_NONE; // sends weapon for humans, class for aliens char *tmp; if ( !g_allowTeamOverlay.integer ) { return; } if ( !ent->client->pers.teamInfo ) { return; } if ( ent->client->pers.teamSelection == TEAM_NONE ) { if ( ent->client->sess.spectatorState == SPECTATOR_FREE || ent->client->sess.spectatorClient < 0 ) { return; } team = g_entities[ ent->client->sess.spectatorClient ].client-> pers.teamSelection; } else { team = ent->client->pers.teamSelection; } string[ 0 ] = '\0'; stringlength = 0; for ( i = 0; i < MAX_CLIENTS; i++ ) { player = g_entities + i; cl = player->client; if ( ent == player || !cl || team != cl->pers.teamSelection || !player->inuse ) { continue; } if ( cl->sess.spectatorState != SPECTATOR_NOT ) { curWeaponClass = WP_NONE; upgrade = UP_NONE; } else if ( cl->pers.teamSelection == TEAM_HUMANS ) { curWeaponClass = cl->ps.weapon; if ( BG_InventoryContainsUpgrade( UP_BATTLESUIT, cl->ps.stats ) ) { upgrade = UP_BATTLESUIT; } else if ( BG_InventoryContainsUpgrade( UP_JETPACK, cl->ps.stats ) ) { upgrade = UP_JETPACK; } else if ( BG_InventoryContainsUpgrade( UP_BATTPACK, cl->ps.stats ) ) { upgrade = UP_BATTPACK; } else if ( BG_InventoryContainsUpgrade( UP_HELMET, cl->ps.stats ) ) { upgrade = UP_HELMET; } else if ( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, cl->ps.stats ) ) { upgrade = UP_LIGHTARMOUR; } else { upgrade = UP_NONE; } } else if ( cl->pers.teamSelection == TEAM_ALIENS ) { curWeaponClass = cl->ps.stats[ STAT_CLASS ]; upgrade = UP_NONE; } tmp = va( "%i %i %i %i", player->client->pers.location, player->client->ps.stats[ STAT_HEALTH ] < 1 ? 0 : player->client->ps.stats[ STAT_HEALTH ], curWeaponClass, upgrade ); if ( !strcmp( ent->client->pers.cinfo[ i ], tmp ) ) { continue; } Q_strncpyz( ent->client->pers.cinfo[ i ], tmp, sizeof( ent->client->pers.cinfo[ i ] ) ); Com_sprintf( entry, sizeof( entry ), " %i %s", i, tmp ); j = strlen( entry ); if ( stringlength + j >= sizeof( string ) ) { break; } strcpy( string + stringlength, entry ); stringlength += j; sent++; } if ( !sent ) { return; } trap_SendServerCommand( ent - g_entities, va( "tinfo%s", string ) ); }
/* ======================================================================================================================================= Team_TouchOurFlag ======================================================================================================================================= */ int Team_TouchOurFlag(gentity_t *ent, gentity_t *other, int team) { int i; gentity_t *player; gclient_t *cl = other->client; int enemy_flag; if (g_gametype.integer == GT_1FCTF) { enemy_flag = PW_NEUTRALFLAG; } else { if (cl->sess.sessionTeam == TEAM_RED) { enemy_flag = PW_BLUEFLAG; } else { enemy_flag = PW_REDFLAG; } if (ent->flags & FL_DROPPED_ITEM) { // hey, it's not home. return it by teleporting it back PrintMsg(NULL, "%s" S_COLOR_WHITE " returned the %s flag!\n", cl->pers.netname, TeamName(team)); AddScore(other, ent->r.currentOrigin, CTF_RECOVERY_BONUS); other->client->pers.teamState.lastreturnedflag = level.time; // 'ResetFlag' will remove this entity! We must return zero Team_ReturnFlagSound(Team_ResetFlag(team), team); return 0; } } // the flag is at home base. if the player has the enemy flag, he's just won! if (!cl->ps.powerups[enemy_flag]) { return 0; // we don't have the flag } if (g_gametype.integer == GT_1FCTF) { trap_SendServerCommand(-1, va("cp \"%s" S_COLOR_WHITE "\ncaptured the flag!\n\"", cl->pers.netname)); } else { trap_SendServerCommand(-1, va("cp \"%s" S_COLOR_WHITE "\ncaptured the %s flag!\n\"", cl->pers.netname, TeamName(OtherTeam(team)))); } cl->ps.powerups[enemy_flag] = 0; other->client->rewardTime = level.time + REWARD_TIME; other->client->ps.persistant[PERS_CAPTURES]++; // other gets another 10 frag bonus AddScore(other, ent->r.currentOrigin, CTF_CAPTURE_BONUS); // ok, let's do the player loop, hand out the bonuses for (i = 0; i < g_maxclients.integer; i++) { player = &g_entities[i]; // also make sure we don't award assist bonuses to the flag carrier himself. if (!player->inuse || player == other) { continue; } if (player->client->sess.sessionTeam != cl->sess.sessionTeam) { player->client->pers.teamState.lasthurtcarrier = -5; } else if (player->client->sess.sessionTeam == cl->sess.sessionTeam) { AddScore(player, ent->r.currentOrigin, CTF_TEAM_BONUS); // award extra points for capture assists if (player->client->pers.teamState.lastreturnedflag + CTF_RETURN_FLAG_ASSIST_TIMEOUT > level.time) { AddScore(player, ent->r.currentOrigin, CTF_RETURN_FLAG_ASSIST_BONUS); player->client->ps.persistant[PERS_ASSIST_COUNT]++; player->client->rewardTime = level.time + REWARD_TIME; } if (player->client->pers.teamState.lastfraggedcarrier + CTF_FRAG_CARRIER_ASSIST_TIMEOUT > level.time) { AddScore(player, ent->r.currentOrigin, CTF_FRAG_CARRIER_ASSIST_BONUS); player->client->ps.persistant[PERS_ASSIST_COUNT]++; player->client->rewardTime = level.time + REWARD_TIME; } } } teamgame.last_flag_capture = level.time; teamgame.last_capture_team = team; // increase the team's score AddTeamScore(ent->s.pos.trBase, other->client->sess.sessionTeam, 1); CalculateRanks(); Team_ResetFlags(); Team_CaptureFlagSound(ent, team); Team_ForceGesture(other->client->sess.sessionTeam); return 0; // do not respawn this automatically }
/* ================= ConsoleCommand ================= */ qboolean ConsoleCommand( void ) { char cmd[MAX_TOKEN_CHARS]; trap_Argv( 0, cmd, sizeof( cmd ) ); if ( Q_stricmp( cmd, "entitylist" ) == 0 ) { Svcmd_EntityList_f(); return qtrue; } if ( Q_stricmp( cmd, "forceteam" ) == 0 ) { Svcmd_ForceTeam_f(); return qtrue; } if ( Q_stricmp( cmd, "game_memory" ) == 0 ) { Svcmd_GameMem_f(); return qtrue; } if ( Q_stricmp( cmd, "addbot" ) == 0 ) { Svcmd_AddBot_f(); return qtrue; } if ( Q_stricmp( cmd, "addip" ) == 0 ) { Svcmd_AddIP_f(); return qtrue; } if ( Q_stricmp( cmd, "removeip" ) == 0 ) { Svcmd_RemoveIP_f(); return qtrue; } if ( Q_stricmp( cmd, "listip" ) == 0 ) { trap_SendConsoleCommand( EXEC_INSERT, "g_banIPs\n" ); return qtrue; } if ( Q_stricmp( cmd, "listmaxlivesip" ) == 0 ) { PrintMaxLivesGUID(); return qtrue; } // NERVE - SMF if ( Q_stricmp( cmd, "start_match" ) == 0 ) { Svcmd_StartMatch_f(); return qtrue; } if ( Q_stricmp( cmd, "reset_match" ) == 0 ) { Svcmd_ResetMatch_f(); return qtrue; } if ( Q_stricmp( cmd, "swap_teams" ) == 0 ) { Svcmd_SwapTeams_f(); return qtrue; } // -NERVE - SMF if ( g_dedicated.integer ) { if ( Q_stricmp( cmd, "say" ) == 0 ) { trap_SendServerCommand( -1, va( "print \"server:[lof] %s\"", ConcatArgs( 1 ) ) ); return qtrue; } // everything else will also be printed as a say command trap_SendServerCommand( -1, va( "print \"server:[lof] %s\"", ConcatArgs( 0 ) ) ); return qtrue; } return qfalse; }
/* ================= G_TimeShiftClient Move a client back to where he was at the specified "time" ================= */ void G_TimeShiftClient( gentity_t *ent, int time, qboolean debug, gentity_t *debugger ) { int j, k; char msg[2048]; // this will dump out the head index, and the time for all the stored positions /* if ( debug ) { char str[MAX_STRING_CHARS]; Com_sprintf(str, sizeof(str), "print \"head: %d, %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n\"", ent->client->historyHead, ent->client->history[0].leveltime, ent->client->history[1].leveltime, ent->client->history[2].leveltime, ent->client->history[3].leveltime, ent->client->history[4].leveltime, ent->client->history[5].leveltime, ent->client->history[6].leveltime, ent->client->history[7].leveltime, ent->client->history[8].leveltime, ent->client->history[9].leveltime, ent->client->history[10].leveltime, ent->client->history[11].leveltime, ent->client->history[12].leveltime, ent->client->history[13].leveltime, ent->client->history[14].leveltime, ent->client->history[15].leveltime, ent->client->history[16].leveltime); trap_SendServerCommand( debugger - g_entities, str ); } */ // find two entries in the history whose times sandwich "time" // assumes no two adjacent records have the same timestamp j = k = ent->client->historyHead; do { if ( ent->client->history[j].leveltime <= time ) break; k = j; j--; if ( j < 0 ) { j = NUM_CLIENT_HISTORY - 1; } } while ( j != ent->client->historyHead ); // if we got past the first iteration above, we've sandwiched (or wrapped) if ( j != k ) { // make sure it doesn't get re-saved if ( ent->client->saved.leveltime != level.time ) { // save the current origin and bounding box VectorCopy( ent->r.mins, ent->client->saved.mins ); VectorCopy( ent->r.maxs, ent->client->saved.maxs ); VectorCopy( ent->r.currentOrigin, ent->client->saved.currentOrigin ); ent->client->saved.leveltime = level.time; } // if we haven't wrapped back to the head, we've sandwiched, so // we shift the client's position back to where he was at "time" if ( j != ent->client->historyHead ) { float frac = (float)(time - ent->client->history[j].leveltime) / (float)(ent->client->history[k].leveltime - ent->client->history[j].leveltime); // interpolate between the two origins to give position at time index "time" TimeShiftLerp( frac, ent->client->history[j].currentOrigin, ent->client->history[k].currentOrigin, ent->r.currentOrigin ); // lerp these too, just for fun (and ducking) TimeShiftLerp( frac, ent->client->history[j].mins, ent->client->history[k].mins, ent->r.mins ); TimeShiftLerp( frac, ent->client->history[j].maxs, ent->client->history[k].maxs, ent->r.maxs ); if ( debug && debugger != NULL ) { // print some debugging stuff exactly like what the client does // it starts with "Rec:" to let you know it backward-reconciled Com_sprintf( msg, sizeof(msg), "print \"^1Rec: time: %d, j: %d, k: %d, origin: %0.2f %0.2f %0.2f\n" "^2frac: %0.4f, origin1: %0.2f %0.2f %0.2f, origin2: %0.2f %0.2f %0.2f\n" "^7level.time: %d, est time: %d, level.time delta: %d, est real ping: %d\n\"", time, ent->client->history[j].leveltime, ent->client->history[k].leveltime, ent->r.currentOrigin[0], ent->r.currentOrigin[1], ent->r.currentOrigin[2], frac, ent->client->history[j].currentOrigin[0], ent->client->history[j].currentOrigin[1], ent->client->history[j].currentOrigin[2], ent->client->history[k].currentOrigin[0], ent->client->history[k].currentOrigin[1], ent->client->history[k].currentOrigin[2], level.time, level.time + debugger->client->frameOffset, level.time - time, level.time + debugger->client->frameOffset - time); trap_SendServerCommand( debugger - g_entities, msg ); } // this will recalculate absmin and absmax trap_LinkEntity( ent ); } else { // we wrapped, so grab the earliest VectorCopy( ent->client->history[k].currentOrigin, ent->r.currentOrigin ); VectorCopy( ent->client->history[k].mins, ent->r.mins ); VectorCopy( ent->client->history[k].maxs, ent->r.maxs ); // this will recalculate absmin and absmax trap_LinkEntity( ent ); } } else { // this only happens when the client is using a negative timenudge, because that // number is added to the command time // print some debugging stuff exactly like what the client does // it starts with "No rec:" to let you know it didn't backward-reconcile if ( debug && debugger != NULL ) { Com_sprintf( msg, sizeof(msg), "print \"^1No rec: time: %d, j: %d, k: %d, origin: %0.2f %0.2f %0.2f\n" "^2frac: %0.4f, origin1: %0.2f %0.2f %0.2f, origin2: %0.2f %0.2f %0.2f\n" "^7level.time: %d, est time: %d, level.time delta: %d, est real ping: %d\n\"", time, level.time, level.time, ent->r.currentOrigin[0], ent->r.currentOrigin[1], ent->r.currentOrigin[2], 0.0f, ent->r.currentOrigin[0], ent->r.currentOrigin[1], ent->r.currentOrigin[2], ent->r.currentOrigin[0], ent->r.currentOrigin[1], ent->r.currentOrigin[2], level.time, level.time + debugger->client->frameOffset, level.time - time, level.time + debugger->client->frameOffset - time); trap_SendServerCommand( debugger - g_entities, msg ); } } }
void ThinkBalloonzone( gentity_t *self ) { //"(team == 0) ? TEAM_RED : TEAM_BLUE" => ! in this code red=0, blue=1 ! (unlike TEAM_RED(1), TEAM_BLUE(2) from team_t) // FIXME: Remove that offset team uglyness or at least make it readable!!1! int team, opponent; int numPlayers; team_t tteam; char *msg; if ( !self->message ) { msg = "Balloon"; } else { msg = self->message; } if ( self->target_ent->s.frame ) { // get teams team = ( self->target_ent->s.generic1 - 1 ); opponent = ( team ^ 1 ); tteam = ( team + 1 ); // capturing if ( ( self->target_ent->s.frame < 11 ) && ( self->teamMask & ( 1 << team ) ) && !( self->teamMask & (1 << opponent ) ) ) { numPlayers = NumPlayersAtBalloon( self, tteam ); if ( numPlayers <= 0 ) { numPlayers = 1; //... so, we must not check this later } self->teamTime[team] += ( BALLOON_THINKTIME * numPlayers ); self->target_ent->s.frame = ( 1 + self->teamTime[team] / ( 100 * self->speed ) ); if ( self->target_ent->s.frame >= 11 ) { // captured self->last_move_time = 0; self->teamTime[team] = 0; level.balloonState[self->count] = ( '1' + team ); trap_SetConfigstring( CS_BALLOONS, level.balloonState ); // TODO: Give more points for capturing than for owning? // Need to test balance! AddTeamScore( self->s.pos.trBase, tteam, ( BalloonScore() * 2 ), SCORE_BONUS_CAPTURE_S ); AddBalloonScores( self, tteam, 1 ); trap_SendServerCommand( -1, va( "mp \"%s captured by %s Team\"", msg, TeamName( tteam ) ) ); } } // balloon is fully raised if ( self->target_ent->s.frame >= 11 ) { // animate self->last_move_time += BALLOON_THINKTIME; if ( self->last_move_time >= ( 700 * self->speed ) ) { self->last_move_time -= ( 700 * self->speed ); } self->target_ent->s.frame = ( 11 + self->last_move_time / ( 100 * self->speed ) ); // give points if ( !level.intermissiontime ) { self->teamTime[team] += BALLOON_THINKTIME; while ( self->teamTime[team] >= BALLOON_POINTTIME ) { self->teamTime[team] -= BALLOON_POINTTIME; AddTeamScore( self->s.pos.trBase, tteam, ( BalloonScore() * 2 ), SCORE_BONUS_CAPTURE_TEAM_S ); } } } // countering capture if ( self->teamMask & (1 << opponent ) ) { numPlayers = NumPlayersAtBalloon( self, tteam ); if ( numPlayers <= 0 ) { numPlayers = 1; //... so, we must not check this later } if ( !self->teamTime[opponent] ) { self->teamTime[opponent] = level.time; } // FIXME: If some players come "later", they will also be calculated for the full time. else if ( level.time > ( self->teamTime[opponent] + ( self->wait * 1000 / numPlayers ) ) ) { // countered self->teamTime[0] = 0; self->teamTime[1] = 0; self->target_ent->s.frame = 0; level.balloonState[self->count] = '0'; trap_SetConfigstring( CS_BALLOONS, level.balloonState ); // TODO: Also give players//&team points for destroying a balloon? trap_SendServerCommand( -1, va( "mp \"%s destroyed by %s Team\"", msg, TeamName( OtherTeam( tteam ) ) ) ); } } else { self->teamTime[opponent] = 0; } } else { if ( ( self->teamMask & BT_RED ) && ( self->teamMask & BT_BLUE ) ) { // reset timer if both teams are trying to capture self->teamTime[0] = 0; self->teamTime[1] = 0; } else { for ( team = 0; team < 2; team++ ) { if ( self->teamMask & ( 1 << team ) ) { // start capture timer or test for capture if ( !self->teamTime[team] ) { self->teamTime[team] = level.time; } else if ( level.time > ( self->teamTime[team] + 1000 ) ) { self->teamTime[team] = 0; self->teamTime[team^1] = level.time; self->target_ent->s.generic1 = ( team + 1 ); self->target_ent->s.frame = 1; level.balloonState[self->count] = ( 'a' + team ); trap_SetConfigstring( CS_BALLOONS, level.balloonState ); trap_SendServerCommand( -1, va( "mp \"%s under attack by %s Team\"", msg, TeamName( team + 1 ) ) ); } } else { self->teamTime[team] = 0; } } } } // prepare next think //#@070329: some delay ... i think there isn't always a touch-call (with laging clients) // "TEAM_RED ? 0 : 1" @ teamTime[...] if ( ( self->target_ent->teamTime[0] + BALLOON_TOUCHDELAY ) < level.time ) { self->teamMask &= ~BT_RED; } if ( ( self->target_ent->teamTime[1] + BALLOON_TOUCHDELAY ) < level.time ) { self->teamMask &= ~BT_BLUE; } self->nextthink = ( level.time + BALLOON_THINKTIME ); }
void trigger_teleporter_touch( gentity_t *self, gentity_t *other, trace_t *trace ) { gentity_t *dest; if ( !other->client ) { return; } if ( other->client->ps.pm_type == PM_DEAD ) { return; } // Spectators only? if ( ( self->spawnflags & 1 ) && ( ( other->client->sess.sessionTeam != TEAM_SPECTATOR ) && !LPSDeadSpec( other->client ) ) ) { return; } // FIXME: Use defines for spawnflags if ( ( self->spawnflags & 0x2 ) && !IsSyc() ) { // No need to check for sprayroom teleporter out return; } if ( ( other->client->sess.sessionTeam != TEAM_SPECTATOR ) && !LPSDeadSpec( other->client ) ) { // sprayroom teleporter in if ( self->spawnflags & 0x2 ) { if ( other->client->ps.ammo[WP_SPRAYPISTOL] <= 0 ) { return; } other->client->logocounter = 0; other->client->sprayroomleavetime = ( ( level.maxsprayroomtime * 1000 ) + level.time ); other->client->sprayroomsoundflags = 0; other->client->ps.stats[STAT_SPRAYROOMSECS] = ( level.maxsprayroomtime + 1 ); if ( other->client->ps.weapon != WP_SPRAYPISTOL ) { other->client->last_nonspray_weapon = other->client->ps.weapon; } if ( other->client->ps.weaponstate == WEAPON_CHARGING ) { other->client->ps.weaponstate = WEAPON_READY; other->client->ps.weaponTime = 0; } trap_SendServerCommand( other->client->ps.clientNum, va( "srwc %i", WP_SPRAYPISTOL ) ); other->client->pers.cmd.weapon = WP_SPRAYPISTOL; other->client->ps.weapon = WP_SPRAYPISTOL; G_BackupPowerups( other->client ); } // sprayroom teleporter out else if ( self->spawnflags & 0x4 ) { other->client->ps.stats[STAT_SPRAYROOMSECS] = 0; trap_SendServerCommand( other->client->ps.clientNum, va( "srwc %i", other->client->last_nonspray_weapon ) ); other->client->pers.cmd.weapon = other->client->last_nonspray_weapon; other->client->ps.weapon = other->client->last_nonspray_weapon; G_RestorePowerups( other->client ); } } dest = G_PickTarget( self->target ); if (!dest) { G_Printf ("Couldn't find teleporter destination\n"); return; } TeleportPlayer( other, dest->s.origin, dest->s.angles ); }
/* ================= ConsoleCommand ================= */ qboolean ConsoleCommand( void ) { char cmd[MAX_TOKEN_CHARS]; trap_Argv( 0, cmd, sizeof( cmd ) ); // all commands prefixed with st_ are destined for the rules engine. if ( cmd[ 0 ] == 's' && cmd[ 1 ] == 't' && cmd[ 2 ] == '_' ) { if ( G_ST_exec( ST_CONSOLECOMMAND, cmd+3 ) ) return qtrue; } switch( SWITCHSTRING( cmd ) ) { #ifdef DEVELOPER case CS('b','u','y',0): case CS('s','e','l','l'): case CS('v','i','s','i'): case CS('b','e','g','i'): case CS('a','s','s','e'): case CS('i','n','t','e'): case CS('e','n','d','i'): case CS('l','o','o','k'): case CS('p','l','a','n'): G_ST_exec( ST_CLIENTCOMMAND, 0, cmd ); trap_SendConsoleCommand( EXEC_INSERT, "wait 50 ;" ); return qtrue; // cl0_ case CS('c','l','0','_'): ClientCommand( 0, cmd + 4 ); return 0; #endif // entitylist case CS('e','n','t','i'): Svcmd_EntityList_f(); return qtrue; // forceteam case CS('f','o','r','c'): Svcmd_ForceTeam_f(); return qtrue; // game_memory case CS('g','a','m','e'): Svcmd_GameMem_f(); return qtrue; // addbot case CS('a','d','d','b'): Svcmd_AddBot_f(); return qtrue; // botlist case CS('b','o','t','l'): Svcmd_BotList_f(); return qtrue; // addip case CS('a','d','d','i'): Svcmd_AddIP_f(); return qtrue; // removeip case CS('r','e','m','o'): Svcmd_RemoveIP_f(); return qtrue; // listip case CS('l','i','s','t'): trap_SendConsoleCommand( EXEC_NOW, "g_banIPs\n" ); return qtrue; // abort case CS('a','b','o','r'): G_ST_exec( ST_CONSOLECOMMAND, cmd ); return qtrue; } if (g_dedicated.integer) { if (Q_stricmp (cmd, "say") == 0) { trap_SendServerCommand( -1, va("print \"server: %s\"", ConcatArgs(1) ) ); return qtrue; } // everything else will also be printed as a say command trap_SendServerCommand( -1, va("print \"server: %s\"", ConcatArgs(0) ) ); return qtrue; } return qfalse; }
/* =========== ClientConnect Called when a player begins connecting to the server. Called again for every map change or tournement restart. The session information will be valid after exit. Return NULL if the client should be allowed, otherwise return a string with the reason for denial. Otherwise, the client will be sent the current gamestate and will eventually get to ClientBegin. firstTime will be qtrue the very first time a client connects to the server machine, but qfalse on map changes and tournement restarts. ============ */ char *ClientConnect( int clientNum, qboolean firstTime ) { char *value; char *userInfoError; gclient_t *client; char userinfo[ MAX_INFO_STRING ]; gentity_t *ent; char reason[ MAX_STRING_CHARS ] = {""}; int i; ent = &g_entities[ clientNum ]; client = &level.clients[ clientNum ]; // ignore if client already connected if( client->pers.connected != CON_DISCONNECTED ) return NULL; ent->client = client; memset( client, 0, sizeof( *client ) ); trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); value = Info_ValueForKey( userinfo, "cl_guid" ); Q_strncpyz( client->pers.guid, value, sizeof( client->pers.guid ) ); value = Info_ValueForKey( userinfo, "ip" ); // check for local client if( !strcmp( value, "localhost" ) ) client->pers.localClient = qtrue; G_AddressParse( value, &client->pers.ip ); client->pers.admin = G_admin_admin( client->pers.guid ); // check for admin ban if( G_admin_ban_check( ent, reason, sizeof( reason ) ) ) { return va( "%s", reason ); } // check for a password value = Info_ValueForKey( userinfo, "password" ); if( g_password.string[ 0 ] && Q_stricmp( g_password.string, "none" ) && strcmp( g_password.string, value ) != 0 ) return "Invalid password"; // add guid to session so we don't have to keep parsing userinfo everywhere for( i = 0; i < sizeof( client->pers.guid ) - 1 && isxdigit( client->pers.guid[ i ] ); i++ ); if( i < sizeof( client->pers.guid ) - 1 ) return "Invalid GUID"; for( i = 0; i < level.maxclients; i++ ) { if( level.clients[ i ].pers.connected == CON_DISCONNECTED ) continue; if( !Q_stricmp( client->pers.guid, level.clients[ i ].pers.guid ) ) { if( !G_ClientIsLagging( level.clients + i ) ) { trap_SendServerCommand( i, "cp \"Your GUID is not secure\"" ); return "Duplicate GUID"; } trap_DropClient( i, "Ghost" ); } } client->pers.connected = CON_CONNECTING; // read or initialize the session data if( firstTime || level.newSession ) G_InitSessionData( client, userinfo ); G_ReadSessionData( client ); // get and distribute relevent paramters G_namelog_connect( client ); userInfoError = ClientUserinfoChanged( clientNum, qfalse ); if( userInfoError != NULL ) return userInfoError; G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s^7\" \"%c%s%c^7\"\n", clientNum, client->pers.ip.str, client->pers.guid, client->pers.netname, DECOLOR_OFF, client->pers.netname, DECOLOR_ON ); // don't do the "xxx connected" messages if they were caried over from previous level if( firstTime ) trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname ) ); if( client->pers.admin ) G_admin_authlog( ent ); // count current clients and rank for scoreboard CalculateRanks( ); // if this is after !restart keepteams or !restart switchteams, apply said selection if ( client->sess.restartTeam != TEAM_NONE ) { G_ChangeTeam( ent, client->sess.restartTeam ); client->sess.restartTeam = TEAM_NONE; } return NULL; }
void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { gentity_t *ent; // TTimo might be used uninitialized int contents = 0; int killer; int i; const char *killerName, *obit; qboolean nogib = qtrue; gitem_t *item = NULL; // JPW NERVE for flag drop vec3_t launchvel,launchspot; // JPW NERVE gentity_t *flag; // JPW NERVE if ( self->client->ps.pm_type == PM_DEAD ) { return; } if ( level.intermissiontime ) { return; } self->client->ps.pm_type = PM_DEAD; G_AddEvent( self, EV_STOPSTREAMINGSOUND, 0 ); if ( attacker ) { killer = attacker->s.number; if ( attacker->client ) { killerName = attacker->client->pers.netname; } else { killerName = "<non-client>"; } } else { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( killer < 0 || killer >= MAX_CLIENTS ) { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) { obit = "<bad obituary>"; } else { obit = modNames[ meansOfDeath ]; } G_LogPrintf( "Kill: %i %i %i: %s killed %s by %s\n", killer, self->s.number, meansOfDeath, killerName, self->client->pers.netname, obit ); // broadcast the death event to everyone ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY ); ent->s.eventParm = meansOfDeath; ent->s.otherEntityNum = self->s.number; ent->s.otherEntityNum2 = killer; ent->r.svFlags = SVF_BROADCAST; // send to everyone self->enemy = attacker; self->client->ps.persistant[PERS_KILLED]++; // JPW NERVE -- if player is holding ticking grenade, drop it if ( g_gametype.integer != GT_SINGLE_PLAYER ) { if ( ( self->client->ps.grenadeTimeLeft ) && ( self->s.weapon != WP_DYNAMITE ) ) { launchvel[0] = crandom(); launchvel[1] = crandom(); launchvel[2] = random(); VectorScale( launchvel, 160, launchvel ); VectorCopy( self->r.currentOrigin, launchspot ); launchspot[2] += 40; fire_grenade( self, launchspot, launchvel, self->s.weapon ); } } // jpw if ( attacker && attacker->client ) { if ( attacker == self || OnSameTeam( self, attacker ) ) { // DHM - Nerve :: Complaint lodging if ( attacker != self && level.warmupTime <= 0 ) { if ( attacker->client->pers.localClient ) { trap_SendServerCommand( self - g_entities, "complaint -4" ); } else { trap_SendServerCommand( self - g_entities, va( "complaint %i", attacker->s.number ) ); self->client->pers.complaintClient = attacker->s.clientNum; self->client->pers.complaintEndTime = level.time + 20500; } } // dhm // JPW NERVE if ( g_gametype.integer >= GT_WOLF ) { // high penalty to offset medic heal AddScore( attacker, WOLF_FRIENDLY_PENALTY ); } else { // jpw AddScore( attacker, -1 ); } } else { // JPW NERVE -- mostly added as conveneience so we can tweak from the #defines all in one place if ( g_gametype.integer >= GT_WOLF ) { AddScore( attacker, WOLF_FRAG_BONUS ); } else { // jpw AddScore( attacker, 1 ); } attacker->client->lastKillTime = level.time; } } else { AddScore( self, -1 ); } // Add team bonuses Team_FragBonuses( self, inflictor, attacker ); // if client is in a nodrop area, don't drop anything // JPW NERVE new drop behavior if ( g_gametype.integer == GT_SINGLE_PLAYER ) { // only drop here in single player; in multiplayer, drop @ limbo contents = trap_PointContents( self->r.currentOrigin, -1 ); if ( !( contents & CONTENTS_NODROP ) ) { TossClientItems( self ); } } // drop flag regardless if ( g_gametype.integer != GT_SINGLE_PLAYER ) { if ( self->client->ps.powerups[PW_REDFLAG] ) { item = BG_FindItem( "Red Flag" ); if ( !item ) { item = BG_FindItem( "Objective" ); } self->client->ps.powerups[PW_REDFLAG] = 0; } if ( self->client->ps.powerups[PW_BLUEFLAG] ) { item = BG_FindItem( "Blue Flag" ); if ( !item ) { item = BG_FindItem( "Objective" ); } self->client->ps.powerups[PW_BLUEFLAG] = 0; } if ( item ) { launchvel[0] = crandom() * 20; launchvel[1] = crandom() * 20; launchvel[2] = 10 + random() * 10; flag = LaunchItem( item,self->r.currentOrigin,launchvel,self->s.number ); flag->s.modelindex2 = self->s.otherEntityNum2; // JPW NERVE FIXME set player->otherentitynum2 with old modelindex2 from flag and restore here flag->message = self->message; // DHM - Nerve :: also restore item name // Clear out player's temp copies self->s.otherEntityNum2 = 0; self->message = NULL; } // send a fancy "MEDIC!" scream. Sissies, ain' they? if ( self->client != NULL ) { if ( self->health > GIB_HEALTH && meansOfDeath != MOD_SUICIDE ) { if ( self->client->sess.sessionTeam == TEAM_RED ) { if ( random() > 0.5 ) { G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/axis/g-medic2.wav" ) ); } else { G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/axis/g-medic3.wav" ) ); } } else { if ( random() > 0.5 ) { G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/allies/a-medic3.wav" ) ); } else { G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/allies/a-medic2.wav" ) ); } } } } } // jpw Cmd_Score_f( self ); // show scores // send updated scores to any clients that are following this one, // or they would get stale scoreboards for ( i = 0 ; i < level.maxclients ; i++ ) { gclient_t *client; client = &level.clients[i]; if ( client->pers.connected != CON_CONNECTED ) { continue; } if ( client->sess.sessionTeam != TEAM_SPECTATOR ) { continue; } if ( client->sess.spectatorClient == self->s.number ) { Cmd_Score_f( g_entities + i ); } } self->takedamage = qtrue; // can still be gibbed self->r.contents = CONTENTS_CORPSE; self->s.powerups = 0; // JPW NERVE -- only corpse in SP; in MP, need CONTENTS_BODY so medic can operate if ( g_gametype.integer == GT_SINGLE_PLAYER ) { self->s.weapon = WP_NONE; self->s.angles[0] = 0; } else { self->client->limboDropWeapon = self->s.weapon; // store this so it can be dropped in limbo } // jpw self->s.angles[2] = 0; LookAtKiller( self, inflictor, attacker ); VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; trap_UnlinkEntity( self ); self->r.maxs[2] = 0; self->client->ps.maxs[2] = 0; trap_LinkEntity( self ); // don't allow respawn until the death anim is done // g_forcerespawn may force spawning at some later time self->client->respawnTime = level.time + 800; // remove powerups memset( self->client->ps.powerups, 0, sizeof( self->client->ps.powerups ) ); // never gib in a nodrop if ( self->health <= GIB_HEALTH && !( contents & CONTENTS_NODROP ) ) { GibEntity( self, killer ); nogib = qfalse; } if ( nogib ) { // normal death // for the no-blood option, we need to prevent the health // from going to gib level if ( self->health <= GIB_HEALTH ) { self->health = GIB_HEALTH + 1; } // JPW NERVE for medic self->client->medicHealAmt = 0; // jpw // DHM - Play death animation, and set pm_time to delay 'fallen' animation self->client->ps.pm_time = BG_AnimScriptEvent( &self->client->ps, ANIM_ET_DEATH, qfalse, qtrue ); G_AddEvent( self, EV_DEATH1 + 1, killer ); // the body can still be gibbed self->die = body_die; } trap_LinkEntity( self ); if ( g_gametype.integer >= GT_WOLF && meansOfDeath == MOD_SUICIDE ) { limbo( self, qtrue ); } }
void G_RadarUpdateCS(void) { int i, valid_count; gentity_t *ent; playerState_t *ps; char cmd[MAX_TOKEN_CHARS]; // make sure our command string is // large enough for all the data // do we need to update the positions yet? if (level.time - level.lastRadarUpdateTime > RADAR_UPDATE_TIME) { //store the current time so we know when to update next level.lastRadarUpdateTime = level.time; //for each possible client valid_count = 0; for (i = 0; i < g_maxclients.integer; i++) { //get a pointer to the entity ent = g_entities + i; //see if we have a valid entry if ( ent->client->pers.connected != CON_CONNECTED ) { g_playerOrigins[i].valid = qfalse; } else if ( !(ent->inuse) ) { g_playerOrigins[i].valid = qfalse; } else if( ent->client->ps.powerLevel[plCurrent] <= 0 ) { g_playerOrigins[i].valid = qfalse; } else { // get the client's player info ps = &ent->client->ps; //get and store the client position and information VectorCopy( ps->origin, g_playerOrigins[i].pos ); g_playerOrigins[i].pl = ps->powerLevel[plCurrent]; g_playerOrigins[i].plMax = ps->powerLevel[plMaximum]; g_playerOrigins[i].clientNum = ps->clientNum; g_playerOrigins[i].properties = 0; if ( ( ps->stats[stChargePercentPrimary] >= 50 ) || ( ps->stats[stChargePercentSecondary] >= 50 ) ) { g_playerOrigins[i].properties |= RADAR_WARN; } if ( ( ps->eFlags & EF_AURA ) || ps->powerups[PW_BOOST] ) { g_playerOrigins[i].properties |= RADAR_BURST; } g_playerOrigins[i].team = ps->persistant[ PERS_TEAM ]; if ( g_playerOrigins[i].team >= TEAM_SPECTATOR ) { // mark as invalid entry for a spectator g_playerOrigins[i].valid = qfalse; } else { //mark as valid entry g_playerOrigins[i].valid = qtrue; //increase the valid counter valid_count++; } } } //build the command string to send Com_sprintf( cmd, sizeof(cmd), "radar %i", valid_count ); for( i = 0; i < g_maxclients.integer; i++ ) { //if weve got a valid entry then add the position to the command string if( g_playerOrigins[i].valid ) { strcat(cmd, va(" %i,", g_playerOrigins[i].clientNum)); strcat(cmd, va("%i,", g_playerOrigins[i].pl)); strcat(cmd, va("%i,", g_playerOrigins[i].plMax)); strcat(cmd, va("%i,", g_playerOrigins[i].team)); strcat(cmd, va("%i,", g_playerOrigins[i].properties)); strcat(cmd, va("%i,", (int)ceil(g_playerOrigins[i].pos[0]))); strcat(cmd, va("%i,", (int)ceil(g_playerOrigins[i].pos[1]))); strcat(cmd, va("%i", (int)ceil(g_playerOrigins[i].pos[2]))); } } // broadcast the command seperately to only connected clients. // FIXME: Does this prevent overflows that otherwise have to // wait for a time-out message from unconnected clients? // NOTE: Yep, seems to fix it. for ( i = 0; i < g_maxclients.integer; i++ ) { ent = g_entities + i; if ( ent->client->pers.connected != CON_CONNECTED ) { continue; } if ( ent->inuse ) { trap_SendServerCommand( ent-g_entities, cmd ); } } /* //finally broadcast the command trap_SendServerCommand( -1, cmd ); */ } }
/* * Format: * clientNum location health armor weapon powerups */ void TeamplayInfoMessage(Gentity *ent) { char entry[1024]; char string[8192]; int stringlength; int i, j; Gentity *player; int cnt; int h, a; int clients[TEAM_MAXOVERLAY]; int team; if(!ent->client->pers.teamInfo) return; /* send info about followed client's team to spectator */ if(ent->client->sess.team == TEAM_SPECTATOR){ if(ent->client->sess.specclient != SPECTATOR_FOLLOW || ent->client->sess.specclient < 0) return; team = g_entities[ent->client->sess.specclient].client->sess.team; }else team = ent->client->sess.team; if(team != TEAM_RED && team != TEAM_BLUE) return; /* * figure out what client should be on the display * we are limited to 8, but we want to use the top eight players * but in client order (so they don't keep changing position on the overlay) */ for(i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++){ player = g_entities + level.sortedClients[i]; if(player->inuse && player->client->sess.team == team) clients[cnt++] = level.sortedClients[i]; } /* We have the top eight players, sort them by clientNum */ qsort(clients, cnt, sizeof(clients[0]), SortClients); /* send the latest information on all clients */ string[0] = 0; stringlength = 0; for(i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++){ player = g_entities + i; if(player->inuse && player->client->sess.team == team){ h = player->client->ps.stats[STAT_HEALTH]; a = player->client->ps.stats[STAT_SHIELD]; if(h < 0) h = 0; if(a < 0) a = 0; Q_sprintf (entry, sizeof(entry), " %i %i %i %i %i %i", /* level.sortedClients[i], player->client->pers.teamState.location, h, a, */ i, player->client->pers.teamState.location, h, a, player->client->ps.weap[WSpri], player->s.powerups); j = strlen(entry); if(stringlength + j >= sizeof(string)) break; strcpy (string + stringlength, entry); stringlength += j; cnt++; } } trap_SendServerCommand(ent-g_entities, va("tinfo %i %s", cnt, string)); }
void AICast_ScriptParse( cast_state_t *cs ) { #define MAX_SCRIPT_EVENTS 64 gentity_t *ent; char *pScript; char *token; qboolean wantName; qboolean inScript; int eventNum; cast_script_event_t events[MAX_SCRIPT_EVENTS]; int numEventItems; cast_script_event_t *curEvent; char params[MAX_QPATH]; cast_script_stack_action_t *action; int i; int bracketLevel; qboolean buildScript; //----(SA) added if ( !level.scriptAI ) { return; } ent = &g_entities[cs->entityNum]; if ( !ent->aiName ) { return; } buildScript = trap_Cvar_VariableIntegerValue( "com_buildScript" ); buildScript = qtrue; pScript = level.scriptAI; wantName = qtrue; inScript = qfalse; COM_BeginParseSession( "AICast_ScriptParse" ); bracketLevel = 0; numEventItems = 0; memset( events, 0, sizeof( events ) ); while ( 1 ) { token = COM_Parse( &pScript ); if ( !token[0] ) { if ( !wantName ) { G_Error( "AICast_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( "AICast_ScriptParse(), Error (line %d): '}' found, but not expected.\n", COM_GetCurrentParseLine() ); } wantName = qtrue; } else if ( token[0] == '{' ) { if ( wantName ) { G_Error( "AICast_ScriptParse(), Error (line %d): '{' found, NAME expected.\n", COM_GetCurrentParseLine() ); } } else if ( wantName ) { if ( !Q_strcasecmp( ent->aiName, token ) ) { inScript = qtrue; numEventItems = 0; } wantName = qfalse; } else if ( inScript ) { if ( !Q_strcasecmp( token, "attributes" ) ) { // read in all the attributes AICast_CheckLevelAttributes( cs, ent, &pScript ); continue; } eventNum = AICast_EventForString( token ); if ( eventNum < 0 ) { G_Error( "AICast_ScriptParse(), Error (line %d): unknown event: %s.\n", COM_GetCurrentParseLine(), token ); } if ( numEventItems >= MAX_SCRIPT_EVENTS ) { G_Error( "AICast_ScriptParse(), Error (line %d): MAX_SCRIPT_EVENTS reached (%d)\n", COM_GetCurrentParseLine(), MAX_SCRIPT_EVENTS ); } // if this is a "friendlysightcorpse" event, then disable corpse vis sharing if ( !Q_stricmp( token, "friendlysightcorpse" ) ) { cs->aiFlags &= ~AIFL_CORPSESIGHTING; } 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 ) ) && ( token[0] != '{' ) ) { if ( !token[0] ) { G_Error( "AICast_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() ); } if ( eventNum == 13 ) { // statechange event, check params if ( strlen( token ) > 1 ) { if ( BG_IndexForString( token, animStateStr, qtrue ) < 0 ) { G_Error( "AICast_ScriptParse(), Error (line %d): unknown state type '%s'.\n", COM_GetCurrentParseLine(), token ); } } } 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 ) ) && ( token[0] != '}' ) ) { if ( !token[0] ) { G_Error( "AICast_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() ); } action = AICast_ActionForString( cs, token ); if ( !action ) { G_Error( "AICast_ScriptParse(), Error (line %d): unknown action: %s.\n", COM_GetCurrentParseLine(), token ); } curEvent->stack.items[curEvent->stack.numItems].action = action; memset( params, 0, sizeof( params ) ); 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 ); } //----(SA) added a bit more 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" ) || !Q_stricmp( action->actionString, "startcamblack" ) ) ) { if ( strlen( token ) ) { // we know there's a [0], but don't know if it's '0' trap_SendServerCommand( cs->entityNum, va( "addToBuild %s\n", token ) ); } } if ( !Q_stricmp( action->actionString, "giveweapon" ) ) { // register weapon for client pre-loading gitem_t *weap = BG_FindItem2( token ); // (SA) FIXME: rats, need to fix this for weapon names with spaces: 'mauser rifle' // if(weap) RegisterItem( weap ); // don't be nice, just do it. if it can't find it, you'll bomb out to the error menu } //----(SA) end } 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 >= AICAST_MAX_SCRIPT_STACK_ITEMS ) { G_Error( "AICast_ScriptParse(): script exceeded MAX_SCRIPT_ITEMS (%d), line %d\n", AICAST_MAX_SCRIPT_STACK_ITEMS, COM_GetCurrentParseLine() ); } } numEventItems++; } else // skip this character completely { // TTimo: gcc: suggest () around assignment used as truth value while ( ( token = COM_Parse( &pScript ) ) ) { if ( !token[0] ) { G_Error( "AICast_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 cast_state_t for this cast if ( numEventItems > 0 ) { cs->castScriptEvents = G_Alloc( sizeof( cast_script_event_t ) * numEventItems ); memcpy( cs->castScriptEvents, events, sizeof( cast_script_event_t ) * numEventItems ); cs->numCastScriptEvents = numEventItems; cs->castScriptStatus.castScriptEventIndex = -1; } }
void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod ) { gclient_t *client; int take; int save; int knockback; qboolean headShot; qboolean wasAlive; hitRegion_t hr = HR_NUM_HITREGIONS; if (!targ->takedamage) { return; } #ifdef SAVEGAME_SUPPORT if( g_gametype.integer == GT_SINGLE_PLAYER && ( g_reloading.integer || saveGamePending ) ) return; #endif // SAVEGAME_SUPPORT // trap_SendServerCommand( -1, va("print \"%i\n\"\n", targ->health) ); // the intermission has allready been qualified for, so don't // allow any extra scoring if ( level.intermissionQueued || (g_gamestate.integer != GS_PLAYING && match_warmupDamage.integer == 0)) { return; } if ( !inflictor ) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if ( !attacker ) { attacker = &g_entities[ENTITYNUM_WORLD]; } // Arnout: invisible entities can't be damaged if( targ->entstate == STATE_INVISIBLE || targ->entstate == STATE_UNDERCONSTRUCTION ) { return; } // xkan, 12/23/2002 - was the bot alive before applying any damage? wasAlive = (targ->health > 0); // Arnout: combatstate if( targ->client && attacker && attacker->client && attacker != targ ) { /*vec_t dist = -1.f; if( targ->client->combatState < COMBATSTATE_HOT ) { vec3_t shotvec; VectorSubtract( targ->r.currentOrigin, attacker->r.currentOrigin, shotvec ); dist = VectorLengthSquared( shotvec ); if( dist < Square(1500.f) && targ->client->combatState == COMBATSTATE_WARM ) targ->client->combatState = COMBATSTATE_HOT; } if( attacker->client->combatState < COMBATSTATE_HOT ) { if( dist < 0.f ) { vec3_t shotvec; VectorSubtract( targ->r.currentOrigin, attacker->r.currentOrigin, shotvec ); dist = VectorLengthSquared( shotvec ); } if( dist > Square(1500.f) ) attacker->client->combatState = COMBATSTATE_WARM; else if( attacker->client->combatState == COMBATSTATE_WARM ) attacker->client->combatState = COMBATSTATE_HOT; }*/ if( g_gamestate.integer == GS_PLAYING ) { if( !OnSameTeam( attacker, targ ) ) { targ->client->combatState |= (1<<COMBATSTATE_DAMAGERECEIVED); attacker->client->combatState |= (1<<COMBATSTATE_DAMAGEDEALT); } } } // JPW NERVE if ((targ->waterlevel >= 3) && (mod == MOD_FLAMETHROWER)) return; // jpw // shootable doors / buttons don't actually have any health if ( targ->s.eType == ET_MOVER && !(targ->isProp) && !targ->scriptName) { if ( targ->use && targ->moverState == MOVER_POS1 ) { G_UseEntity( targ, inflictor, attacker ); } return; } // TAT 11/22/2002 // In the old code, this check wasn't done for props, so I put that check back in to make props_statue properly work // 4 means destructible if ( targ->s.eType == ET_MOVER && (targ->spawnflags & 4) && !targ->isProp ) { /*switch (mod) { case MOD_GRENADE: case MOD_GRENADE_LAUNCHER: case MOD_ROCKET: case MOD_AIRSTRIKE: case MOD_ARTY: case MOD_GRENADE_PINEAPPLE: case MOD_MAPMORTAR: case MOD_EXPLOSIVE: case MOD_DYNAMITE: case MOD_LANDMINE: case MOD_GPG40: case MOD_M7: case MOD_TELEFRAG: case MOD_PANZERFAUST: case MOD_SATCHEL: break; default: return; // no damage from other weapons }*/ if( !G_WeaponIsExplosive( mod ) ) { return; } // check for team if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } } else if ( targ->s.eType == ET_EXPLOSIVE ) { /*// 32 Explosive // 64 Dynamite only // 256 Airstrike/artillery only // 512 Satchel only if ((targ->spawnflags & 32) || (targ->spawnflags & 64) || (targ->spawnflags & 256) || (targ->spawnflags & 512)) { switch (mod) { case MOD_GRENADE: case MOD_GRENADE_LAUNCHER: case MOD_ROCKET: case MOD_GRENADE_PINEAPPLE: case MOD_MAPMORTAR: case MOD_EXPLOSIVE: case MOD_LANDMINE: case MOD_GPG40: case MOD_M7: if( !(targ->spawnflags & 32) ) return; break; case MOD_SATCHEL: if( !(targ->spawnflags & 512) ) return; break; case MOD_ARTY: case MOD_AIRSTRIKE: if( !(targ->spawnflags & 256) ) return; break; case MOD_DYNAMITE: if( !(targ->spawnflags & 64) ) return; break; default: return; } // check for team if( targ->s.teamNum == inflictor->s.teamNum ) { return; } }*/ if( targ->parent && G_GetWeaponClassForMOD( mod ) == 2 ) { return; } // check for team // if( G_GetWeaponClassForMOD( mod ) != -1 && targ->s.teamNum == inflictor->s.teamNum ) { // return; // } if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } if( G_GetWeaponClassForMOD( mod ) < targ->constructibleStats.weaponclass ) { return; } } else if ( targ->s.eType == ET_MISSILE && targ->methodOfDeath == MOD_LANDMINE ) { if( targ->s.modelindex2 ) { if( G_WeaponIsExplosive( mod ) ) { mapEntityData_t *mEnt; if((mEnt = G_FindMapEntityData(&mapEntityData[0], targ-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[0], mEnt ); } if((mEnt = G_FindMapEntityData(&mapEntityData[1], targ-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[1], mEnt ); } if( attacker && attacker->client ) { AddScore( attacker, 1 ); //G_AddExperience( attacker, 1.f ); } G_ExplodeMissile(targ); } } return; } else if ( targ->s.eType == ET_CONSTRUCTIBLE ) { if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } if( G_GetWeaponClassForMOD( mod ) < targ->constructibleStats.weaponclass ) { return; } } client = targ->client; if ( client ) { if ( client->noclip || client->ps.powerups[PW_INVULNERABLE] ) { return; } } // check for godmode if ( targ->flags & FL_GODMODE ) { return; } if ( !dir ) { dflags |= DAMAGE_NO_KNOCKBACK; } else { VectorNormalize(dir); } knockback = damage; if ( knockback > 200 ) { knockback = 200; } if ( targ->flags & FL_NO_KNOCKBACK ) { knockback = 0; } if ( dflags & DAMAGE_NO_KNOCKBACK ) { knockback = 0; } else if( dflags & DAMAGE_HALF_KNOCKBACK ) { knockback *= 0.5f; } // ydnar: set weapons means less knockback if( client && (client->ps.weapon == WP_MORTAR_SET || client->ps.weapon == WP_MOBILE_MG42_SET) ) knockback *= 0.5; if( targ->client && g_friendlyFire.integer && OnSameTeam(targ, attacker) ) { knockback = 0; } // figure momentum add, even if the damage won't be taken if ( knockback && targ->client ) { vec3_t kvel; float mass; mass = 200; VectorScale (dir, g_knockback.value * (float)knockback / mass, kvel); VectorAdd (targ->client->ps.velocity, kvel, targ->client->ps.velocity); /*if( mod == MOD_GRENADE || mod == MOD_GRENADE_LAUNCHER || mod == MOD_DYNAMITE || mod == MOD_GPG40 || mod == MOD_M7 || mod == MOD_LANDMINE ) { targ->client->ps.velocity[2] *= 2.f; // gimme air baby! targ->client->ps.groundEntityNum = ENTITYNUM_NONE; // flying high! } else if( mod == MOD_ROCKET ) { targ->client->ps.velocity[2] *= .75f; // but not to the moon please! targ->client->ps.groundEntityNum = ENTITYNUM_NONE; // flying high! }*/ if (targ == attacker && !( mod != MOD_ROCKET && mod != MOD_GRENADE && mod != MOD_GRENADE_LAUNCHER && mod != MOD_DYNAMITE && mod != MOD_GPG40 && mod != MOD_M7 && mod != MOD_LANDMINE )) { targ->client->ps.velocity[2] *= 0.25; } // set the timer so that the other client can't cancel // out the movement immediately if ( !targ->client->ps.pm_time ) { int t; t = knockback * 2; if ( t < 50 ) { t = 50; } if ( t > 200 ) { t = 200; } targ->client->ps.pm_time = t; targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; } } // check for completely getting out of the damage if ( !(dflags & DAMAGE_NO_PROTECTION) ) { // if TF_NO_FRIENDLY_FIRE is set, don't do damage to the target // if the attacker was on the same team if ( targ != attacker && OnSameTeam (targ, attacker) ) { if ( (g_gamestate.integer != GS_PLAYING && match_warmupDamage.integer == 1)) { return; } else if (!g_friendlyFire.integer) { return; } } } // add to the attacker's hit counter if ( attacker->client && targ != attacker && targ->health > 0 ) { if ( OnSameTeam( targ, attacker ) ) { attacker->client->ps.persistant[PERS_HITS] -= damage; } else { attacker->client->ps.persistant[PERS_HITS] += damage; } } if ( damage < 1 ) { damage = 1; } take = damage; save = 0; // adrenaline junkie! if( targ->client && targ->client->ps.powerups[PW_ADRENALINE] ) { take *= .5f; } // save some from flak jacket if( targ->client && targ->client->sess.skill[SK_EXPLOSIVES_AND_CONSTRUCTION] >= 4 && targ->client->sess.playerType == PC_ENGINEER ) { if( mod == MOD_GRENADE || mod == MOD_GRENADE_LAUNCHER || mod == MOD_ROCKET || mod == MOD_GRENADE_PINEAPPLE || mod == MOD_MAPMORTAR || mod == MOD_MAPMORTAR_SPLASH || mod == MOD_EXPLOSIVE || mod == MOD_LANDMINE || mod == MOD_GPG40 || mod == MOD_M7 || mod == MOD_SATCHEL || mod == MOD_ARTY || mod == MOD_AIRSTRIKE || mod == MOD_DYNAMITE || mod == MOD_MORTAR || mod == MOD_PANZERFAUST || mod == MOD_MAPMORTAR ) { take -= take * .5f; } } headShot = IsHeadShot(targ, dir, point, mod); if ( headShot ) { if( take * 2 < 50 ) // head shots, all weapons, do minimum 50 points damage take = 50; else take *= 2; // sniper rifles can do full-kill (and knock into limbo) if( dflags & DAMAGE_DISTANCEFALLOFF ) { vec_t dist; vec3_t shotvec; VectorSubtract( point, muzzleTrace, shotvec ); dist = VectorLength( shotvec ); if( dist > 1500.f ) { if( dist > 2500.f ) { take *= 0.2f; } else { float scale = 1.f - 0.2f * (1000.f / (dist - 1000.f)); take *= scale; } } } if( !(targ->client->ps.eFlags & EF_HEADSHOT) ) { // only toss hat on first headshot G_AddEvent( targ, EV_LOSE_HAT, DirToByte(dir) ); if( mod != MOD_K43_SCOPE && mod != MOD_GARAND_SCOPE ) { take *= .8f; // helmet gives us some protection } } targ->client->ps.eFlags |= EF_HEADSHOT; // OSP - Record the headshot if(client && attacker && attacker->client #ifndef DEBUG_STATS && attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam #endif ) { G_addStatsHeadShot(attacker, mod); } if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Head Shot\n\"\n"); } G_LogRegionHit( attacker, HR_HEAD ); hr = HR_HEAD; } else if ( IsLegShot(targ, dir, point, mod) ) { G_LogRegionHit( attacker, HR_LEGS ); hr = HR_LEGS; if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Leg Shot\n\"\n"); } } else if ( IsArmShot(targ, attacker, point, mod) ) { G_LogRegionHit( attacker, HR_ARMS ); hr = HR_ARMS; if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Arm Shot\n\"\n"); } } else if (targ->client && targ->health > 0 && IsHeadShotWeapon( mod ) ) { G_LogRegionHit( attacker, HR_BODY ); hr = HR_BODY; if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Body Shot\n\"\n"); } } #ifndef DEBUG_STATS if ( g_debugDamage.integer ) #endif { G_Printf( "client:%i health:%i damage:%i mod:%s\n", targ->s.number, targ->health, take, modNames[mod] ); } // add to the damage inflicted on a player this frame // the total will be turned into screen blends and view angle kicks // at the end of the frame if ( client ) { if ( attacker ) { client->ps.persistant[PERS_ATTACKER] = attacker->s.number; } else { client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD; } client->damage_blood += take; client->damage_knockback += knockback; if ( dir ) { VectorCopy ( dir, client->damage_from ); client->damage_fromWorld = qfalse; } else { VectorCopy ( targ->r.currentOrigin, client->damage_from ); client->damage_fromWorld = qtrue; } } // See if it's the player hurting the emeny flag carrier // Team_CheckHurtCarrier(targ, attacker); if (targ->client) { // set the last client who damaged the target targ->client->lasthurt_client = attacker->s.number; targ->client->lasthurt_mod = mod; } // do the damage if( take ) { targ->health -= take; // Gordon: don't ever gib POWS if( ( targ->health <= 0 ) && ( targ->r.svFlags & SVF_POW ) ) { targ->health = -1; } // Ridah, can't gib with bullet weapons (except VENOM) // Arnout: attacker == inflictor can happen in other cases as well! (movers trying to gib things) //if ( attacker == inflictor && targ->health <= GIB_HEALTH) { if( targ->health <= GIB_HEALTH ) { if( !G_WeaponIsExplosive( mod ) ) { targ->health = GIB_HEALTH + 1; } } // JPW NERVE overcome previous chunk of code for making grenades work again // if ((take > 190)) // 190 is greater than 2x mauser headshot, so headshots don't gib // Arnout: only player entities! messes up ents like func_constructibles and func_explosives otherwise if( ( (targ->s.number < MAX_CLIENTS) && (take > 190) ) && !(targ->r.svFlags & SVF_POW) ) { targ->health = GIB_HEALTH - 1; } if( targ->s.eType == ET_MOVER && !Q_stricmp( targ->classname, "script_mover" ) ) { targ->s.dl_intensity = 255.f * (targ->health / (float)targ->count); // send it to the client } //G_Printf("health at: %d\n", targ->health); if( targ->health <= 0 ) { if( client && !wasAlive ) { targ->flags |= FL_NO_KNOCKBACK; // OSP - special hack to not count attempts for body gibbage if( targ->client->ps.pm_type == PM_DEAD ) { G_addStats(targ, attacker, take, mod); } if( (targ->health < FORCE_LIMBO_HEALTH) && (targ->health > GIB_HEALTH) ) { limbo(targ, qtrue); } // xkan, 1/13/2003 - record the time we died. if (!client->deathTime) client->deathTime = level.time; } else { targ->sound1to2 = hr; targ->sound2to1 = mod; targ->sound2to3 = (dflags & DAMAGE_RADIUS) ? 1 : 0; if( client ) { if( G_GetTeamFromEntity( inflictor ) != G_GetTeamFromEntity( targ ) ) { G_AddKillSkillPoints( attacker, mod, hr, (dflags & DAMAGE_RADIUS) ); } } if( targ->health < -999 ) { targ->health = -999; } targ->enemy = attacker; targ->deathType = mod; // Ridah, mg42 doesn't have die func (FIXME) if( targ->die ) { // Kill the entity. Note that this funtion can set ->die to another // function pointer, so that next time die is applied to the dead body. targ->die( targ, inflictor, attacker, take, mod ); // OSP - kill stats in player_die function } if( targ->s.eType == ET_MOVER && !Q_stricmp( targ->classname, "script_mover" ) && (targ->spawnflags & 8) ) { return; // reseructable script mover doesn't unlink itself but we don't want a second death script to be called } // if we freed ourselves in death function if (!targ->inuse) return; // RF, entity scripting if (targ->health <= 0) { // might have revived itself in death function if ((targ->s.eType != ET_CONSTRUCTIBLE && targ->s.eType != ET_EXPLOSIVE) || (targ->s.eType == ET_CONSTRUCTIBLE && !targ->desstages)) { // call manually if using desstages G_Script_ScriptEvent( targ, "death", "" ); } } } } else if ( targ->pain ) { if (dir) { // Ridah, had to add this to fix NULL dir crash VectorCopy (dir, targ->rotate); VectorCopy (point, targ->pos3); // this will pass loc of hit } else { VectorClear( targ->rotate ); VectorClear( targ->pos3 ); } targ->pain (targ, attacker, take, point); } else { // OSP - update weapon/dmg stats G_addStats(targ, attacker, take, mod); // OSP } // RF, entity scripting G_Script_ScriptEvent( targ, "pain", va("%d %d", targ->health, targ->health+take) ); // Ridah, this needs to be done last, incase the health is altered in one of the event calls if ( targ->client ) { targ->client->ps.stats[STAT_HEALTH] = targ->health; } } }
/* ================= ConsoleCommand ================= */ qboolean ConsoleCommand( void ) { char cmd[MAX_TOKEN_CHARS]; trap_Argv( 0, cmd, sizeof( cmd ) ); if ( Q_stricmp (cmd, "entitylist") == 0 ) { Svcmd_EntityList_f(); return qtrue; } if ( Q_stricmp (cmd, "forceteam") == 0 ) { Svcmd_ForceTeam_f(); return qtrue; } if (Q_stricmp (cmd, "game_memory") == 0) { Svcmd_GameMem_f(); return qtrue; } if (Q_stricmp (cmd, "addbot") == 0) { Svcmd_AddBot_f(); return qtrue; } if (Q_stricmp (cmd, "botlist") == 0) { Svcmd_BotList_f(); return qtrue; } if (Q_stricmp (cmd, "abort_podium") == 0) { Svcmd_AbortPodium_f(); return qtrue; } if (Q_stricmp (cmd, "addip") == 0) { Svcmd_AddIP_f(); return qtrue; } if (Q_stricmp (cmd, "removeip") == 0) { Svcmd_RemoveIP_f(); return qtrue; } if (Q_stricmp (cmd, "listip") == 0) { trap_SendConsoleCommand( EXEC_NOW, "g_banIPs\n" ); return qtrue; } if(Q_stricmp (cmd, "setGametype") == 0) { Svcmd_SetGameType_f(); return qtrue; } if(Q_stricmp (cmd, "startcam") == 0) { Svcmd_StartCam(); return qtrue; } if(Q_stricmp (cmd, "stopcam") == 0) { Svcmd_StopCam(); return qtrue; } if(Q_stricmp (cmd, "camcmd") == 0) { Svcmd_CamCmd(); return qtrue; } if( !Q_stricmp( cmd, "initwp") ) { WaypointInit(); return qtrue; } if ( Q_stricmp( cmd, "blibset") == 0 ) { char key[MAX_TOKEN_CHARS]; char value[MAX_TOKEN_CHARS]; trap_Argv(1, key, sizeof(key) ); trap_Argv(2, value, sizeof(value) ); if(!strlen(key)) { G_Printf("missing key\n"); return qtrue; } if( !strlen(value) ) // use "1" as default strcpy( value, "1" ); trap_BotLibVarSet( key, value ); return qtrue; } if(wopSP_cmdCheck(cmd)) return qtrue; if (g_dedicated.integer) { if ( Q_stricmp( cmd, "ssay" ) == 0 ) { Svcmd_Say_f(); return qtrue; } if ( Q_stricmp( cmd, "stell" ) == 0 ) { Svcmd_Tell_f(); return qtrue; } if ( Q_stricmp( cmd, "scp" ) == 0 ) { Svcmd_ClientCommand_f( CCMD_CP ); return qtrue; } if ( Q_stricmp( cmd, "smp" ) == 0 ) { Svcmd_ClientCommand_f( CCMD_MP ); return qtrue; } if ( Q_stricmp( cmd, "sprint" ) == 0 ) { Svcmd_ClientCommand_f( CCMD_PRINT ); return qtrue; } // everything else will also be printed to clients trap_SendServerCommand( -1, va("print \"server: %s\n\"", ConcatArgs(0) ) ); return qtrue; } return qfalse; }
void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { int contents = 0, i, killer = ENTITYNUM_WORLD; char *killerName = "<world>"; qboolean nogib = qtrue; gitem_t *item = NULL; gentity_t *ent; qboolean killedintank = qfalse; //float timeLived; weapon_t weap = BG_WeaponForMOD( meansOfDeath ); // G_Printf( "player_die\n" ); if(attacker == self) { if(self->client) { self->client->pers.playerStats.suicides++; trap_PbStat ( self - g_entities , "suicide" , va ( "%d %d %d" , self->client->sess.sessionTeam , self->client->sess.playerType , weap ) ) ; } } else if(OnSameTeam( self, attacker )) { G_LogTeamKill( attacker, weap ); } else { G_LogDeath( self, weap ); G_LogKill( attacker, weap ); if( g_gamestate.integer == GS_PLAYING ) { if( attacker->client ) { attacker->client->combatState |= (1<<COMBATSTATE_KILLEDPLAYER); } } } // RF, record this death in AAS system so that bots avoid areas which have high death rates if( !OnSameTeam( self, attacker ) ) { // LC - not needed // BotRecordTeamDeath( self->s.number ); self->isProp = qfalse; // were we teamkilled or not? } else { self->isProp = qtrue; } // if we got killed by a landmine, update our map if( self->client && meansOfDeath == MOD_LANDMINE ) { // if it's an enemy mine, update both teamlists /*int teamNum; mapEntityData_t *mEnt; mapEntityData_Team_t *teamList; teamNum = inflictor->s.teamNum % 4; teamList = self->client->sess.sessionTeam == TEAM_AXIS ? &mapEntityData[0] : &mapEntityData[1]; if((mEnt = G_FindMapEntityData(teamList, inflictor-g_entities)) != NULL) { G_FreeMapEntityData( teamList, mEnt ); } if( teamNum != self->client->sess.sessionTeam ) { teamList = self->client->sess.sessionTeam == TEAM_AXIS ? &mapEntityData[1] : &mapEntityData[0]; if((mEnt = G_FindMapEntityData(teamList, inflictor-g_entities)) != NULL) { G_FreeMapEntityData( teamList, mEnt ); } }*/ mapEntityData_t *mEnt; if((mEnt = G_FindMapEntityData(&mapEntityData[0], inflictor-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[0], mEnt ); } if((mEnt = G_FindMapEntityData(&mapEntityData[1], inflictor-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[1], mEnt ); } } { mapEntityData_t *mEnt; mapEntityData_Team_t *teamList = self->client->sess.sessionTeam == TEAM_AXIS ? &mapEntityData[1] : &mapEntityData[0]; // swapped, cause enemy team mEnt = G_FindMapEntityDataSingleClient( teamList, NULL, self->s.number, -1 ); while( mEnt ) { if( mEnt->type == ME_PLAYER_DISGUISED ) { mapEntityData_t* mEntFree = mEnt; mEnt = G_FindMapEntityDataSingleClient( teamList, mEnt, self->s.number, -1 ); G_FreeMapEntityData( teamList, mEntFree ); } else { mEnt = G_FindMapEntityDataSingleClient( teamList, mEnt, self->s.number, -1 ); } } } if( self->tankLink ) { G_LeaveTank( self, qfalse ); killedintank = qtrue; } if( self->client->ps.pm_type == PM_DEAD || g_gamestate.integer == GS_INTERMISSION ) { return; } // OSP - death stats handled out-of-band of G_Damage for external calls G_addStats(self, attacker, damage, meansOfDeath); // OSP self->client->ps.pm_type = PM_DEAD; G_AddEvent( self, EV_STOPSTREAMINGSOUND, 0); if(attacker) { killer = attacker->s.number; killerName = (attacker->client) ? attacker->client->pers.netname : "<non-client>"; } if(attacker == 0 || killer < 0 || killer >= MAX_CLIENTS) { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if(g_gamestate.integer == GS_PLAYING) { char *obit; if(meansOfDeath < 0 || meansOfDeath >= sizeof(modNames) / sizeof(modNames[0])) { obit = "<bad obituary>"; } else { obit = modNames[meansOfDeath]; } G_LogPrintf("Kill: %i %i %i: %s killed %s by %s\n", killer, self->s.number, meansOfDeath, killerName, self->client->pers.netname, obit ); } // broadcast the death event to everyone ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY ); ent->s.eventParm = meansOfDeath; ent->s.otherEntityNum = self->s.number; ent->s.otherEntityNum2 = killer; ent->r.svFlags = SVF_BROADCAST; // send to everyone self->enemy = attacker; self->client->ps.persistant[PERS_KILLED]++; // JPW NERVE -- if player is holding ticking grenade, drop it if ((self->client->ps.grenadeTimeLeft) && (self->s.weapon != WP_DYNAMITE) && (self->s.weapon != WP_LANDMINE) && (self->s.weapon != WP_SATCHEL) && (self->s.weapon != WP_TRIPMINE)) { vec3_t launchvel, launchspot; launchvel[0] = crandom(); launchvel[1] = crandom(); launchvel[2] = random(); VectorScale( launchvel, 160, launchvel ); VectorCopy(self->r.currentOrigin, launchspot); launchspot[2] += 40; { // Gordon: fixes premature grenade explosion, ta bani ;) gentity_t *m = fire_grenade(self, launchspot, launchvel, self->s.weapon); m->damage = 0; } } if (attacker && attacker->client) { if ( attacker == self || OnSameTeam (self, attacker ) ) { // DHM - Nerve :: Complaint lodging if( attacker != self && level.warmupTime <= 0 && g_gamestate.integer == GS_PLAYING) { if( attacker->client->pers.localClient ) { trap_SendServerCommand( self-g_entities, "complaint -4" ); } else { if( meansOfDeath != MOD_CRUSH_CONSTRUCTION && meansOfDeath != MOD_CRUSH_CONSTRUCTIONDEATH && meansOfDeath != MOD_CRUSH_CONSTRUCTIONDEATH_NOATTACKER ) { if( g_complaintlimit.integer ) { if( !(meansOfDeath == MOD_LANDMINE && g_disableComplaints.integer & TKFL_MINES ) && !((meansOfDeath == MOD_ARTY || meansOfDeath == MOD_AIRSTRIKE) && g_disableComplaints.integer & TKFL_AIRSTRIKE ) && !(meansOfDeath == MOD_MORTAR && g_disableComplaints.integer & TKFL_MORTAR ) ) { trap_SendServerCommand( self-g_entities, va( "complaint %i", attacker->s.number ) ); self->client->pers.complaintClient = attacker->s.clientNum; self->client->pers.complaintEndTime = level.time + 20500; } } } } } // high penalty to offset medic heal /* AddScore( attacker, WOLF_FRIENDLY_PENALTY ); */ if( g_gametype.integer == GT_WOLF_LMS ) { AddKillScore( attacker, WOLF_FRIENDLY_PENALTY ); } } else { //G_AddExperience( attacker, 1 ); // JPW NERVE -- mostly added as conveneience so we can tweak from the #defines all in one place AddScore(attacker, WOLF_FRAG_BONUS); if( g_gametype.integer == GT_WOLF_LMS ) { if( level.firstbloodTeam == -1 ) level.firstbloodTeam = attacker->client->sess.sessionTeam; AddKillScore( attacker, WOLF_FRAG_BONUS ); } attacker->client->lastKillTime = level.time; } } else { AddScore( self, -1 ); if( g_gametype.integer == GT_WOLF_LMS ) AddKillScore( self, -1 ); } // Add team bonuses Team_FragBonuses(self, inflictor, attacker); // drop flag regardless if (self->client->ps.powerups[PW_REDFLAG]) { item = BG_FindItem("Red Flag"); if (!item) item = BG_FindItem("Objective"); self->client->ps.powerups[PW_REDFLAG] = 0; } if (self->client->ps.powerups[PW_BLUEFLAG]) { item = BG_FindItem("Blue Flag"); if (!item) item = BG_FindItem("Objective"); self->client->ps.powerups[PW_BLUEFLAG] = 0; } if (item) { vec3_t launchvel = { 0, 0, 0 }; gentity_t *flag = LaunchItem(item, self->r.currentOrigin, launchvel, self->s.number); flag->s.modelindex2 = self->s.otherEntityNum2;// JPW NERVE FIXME set player->otherentitynum2 with old modelindex2 from flag and restore here flag->message = self->message; // DHM - Nerve :: also restore item name // Clear out player's temp copies self->s.otherEntityNum2 = 0; self->message = NULL; } // send a fancy "MEDIC!" scream. Sissies, ain' they? if (self->client != NULL) { if( self->health > GIB_HEALTH && meansOfDeath != MOD_SUICIDE && meansOfDeath != MOD_SWITCHTEAM ) { G_AddEvent( self, EV_MEDIC_CALL, 0 ); } } Cmd_Score_f( self ); // show scores // send updated scores to any clients that are following this one, // or they would get stale scoreboards for(i=0; i<level.numConnectedClients; i++) { gclient_t *client = &level.clients[level.sortedClients[i]]; if(client->pers.connected != CON_CONNECTED) continue; if(client->sess.sessionTeam != TEAM_SPECTATOR) continue; if(client->sess.spectatorClient == self->s.number) { Cmd_Score_f(g_entities + level.sortedClients[i]); } } self->takedamage = qtrue; // can still be gibbed self->r.contents = CONTENTS_CORPSE; //self->s.angles[2] = 0; self->s.powerups = 0; self->s.loopSound = 0; self->client->limboDropWeapon = self->s.weapon; // store this so it can be dropped in limbo LookAtKiller( self, inflictor, attacker ); self->client->ps.viewangles[0] = 0; self->client->ps.viewangles[2] = 0; //VectorCopy( self->s.angles, self->client->ps.viewangles ); // trap_UnlinkEntity( self ); self->r.maxs[2] = self->client->ps.crouchMaxZ; //% 0; // ydnar: so bodies don't clip into world self->client->ps.maxs[2] = self->client->ps.crouchMaxZ; //% 0; // ydnar: so bodies don't clip into world trap_LinkEntity( self ); // don't allow respawn until the death anim is done // g_forcerespawn may force spawning at some later time self->client->respawnTime = level.timeCurrent + 800; // remove powerups memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) ); // never gib in a nodrop // FIXME: contents is always 0 here if ( self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) ) { GibEntity( self, killer ); nogib = qfalse; } if(nogib){ // normal death // for the no-blood option, we need to prevent the health // from going to gib level if ( self->health <= GIB_HEALTH ) { self->health = GIB_HEALTH + 1; } // Arnout: re-enable this for flailing /* if( self->client->ps.groundEntityNum == ENTITYNUM_NONE ) { self->client->ps.pm_flags |= PMF_FLAILING; self->client->ps.pm_time = 750; BG_AnimScriptAnimation( &self->client->ps, ANIM_MT_FLAILING, qtrue ); // Face explosion directory { vec3_t angles; vectoangles( self->client->ps.velocity, angles ); self->client->ps.viewangles[YAW] = angles[YAW]; SetClientViewAngle( self, self->client->ps.viewangles ); } } else*/ // DHM - Play death animation, and set pm_time to delay 'fallen' animation //if( G_IsSinglePlayerGame() && self->client->sess.sessionTeam == TEAM_ALLIES ) { // // play "falldown" animation since allies bots won't ever die completely // self->client->ps.pm_time = BG_AnimScriptEvent( &self->client->ps, self->client->pers.character->animModelInfo, ANIM_ET_FALLDOWN, qfalse, qtrue ); // G_StartPlayerAppropriateSound(self, "death"); //} else { self->client->ps.pm_time = BG_AnimScriptEvent( &self->client->ps, self->client->pers.character->animModelInfo, ANIM_ET_DEATH, qfalse, qtrue ); // death animation script already contains sound //} // record the death animation to be used later on by the corpse self->client->torsoDeathAnim = self->client->ps.torsoAnim; self->client->legsDeathAnim = self->client->ps.legsAnim; G_AddEvent( self, EV_DEATH1 + 1, killer ); // the body can still be gibbed self->die = body_die; } if( meansOfDeath == MOD_MACHINEGUN ) { switch( self->client->sess.sessionTeam ) { case TEAM_AXIS: level.axisMG42Counter = level.time; break; case TEAM_ALLIES: level.alliesMG42Counter = level.time; break; } } G_FadeItems( self, MOD_SATCHEL ); CalculateRanks(); if( killedintank /*Gordon: automatically go to limbo from tank*/ ) { limbo( self, qfalse ); // but no corpse } else if ( (meansOfDeath == MOD_SUICIDE && g_gamestate.integer == GS_PLAYING) ) { limbo( self, qtrue ); } else if( g_gametype.integer == GT_WOLF_LMS ) { if( !G_CountTeamMedics( self->client->sess.sessionTeam, qtrue ) ) { limbo( self, qtrue ); } } }
/* ================= CheckExitRules There will be a delay between the time the exit is qualified for and the time everyone is moved to the intermission spot, so you can see the last frag. ================= */ void CheckExitRules( void ) { int i; gclient_t *cl; // if at the intermission, wait for all non-bots to // signal ready, then go to next level if ( level.intermissiontime ) { CheckIntermissionExit (); return; } if ( level.intermissionQueued ) { #ifdef MISSIONPACK int time = (g_singlePlayer.integer) ? SP_INTERMISSION_DELAY_TIME : INTERMISSION_DELAY_TIME; if ( level.time - level.intermissionQueued >= time ) { level.intermissionQueued = 0; BeginIntermission(); } #else if ( level.time - level.intermissionQueued >= INTERMISSION_DELAY_TIME ) { level.intermissionQueued = 0; BeginIntermission(); } #endif return; } // check for sudden death if ( ScoreIsTied() ) { // always wait for sudden death return; } if ( g_timelimit.integer && !level.warmupTime ) { if ( level.time - level.startTime >= g_timelimit.integer*60000 ) { trap_SendServerCommand( -1, "print \"Timelimit hit.\n\""); LogExit( "Timelimit hit." ); return; } } if ( g_gametype.integer < GT_CTF && g_fraglimit.integer ) { if ( level.teamScores[TEAM_RED] >= g_fraglimit.integer ) { trap_SendServerCommand( -1, "print \"Red hit the fraglimit.\n\"" ); LogExit( "Fraglimit hit." ); return; } if ( level.teamScores[TEAM_BLUE] >= g_fraglimit.integer ) { trap_SendServerCommand( -1, "print \"Blue hit the fraglimit.\n\"" ); LogExit( "Fraglimit hit." ); return; } for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } if ( cl->sess.sessionTeam != TEAM_FREE ) { continue; } if ( cl->ps.persistant[PERS_SCORE] >= g_fraglimit.integer ) { LogExit( "Fraglimit hit." ); trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " hit the fraglimit.\n\"", cl->pers.netname ) ); return; } } } if ( g_gametype.integer >= GT_CTF && g_capturelimit.integer ) { if ( level.teamScores[TEAM_RED] >= g_capturelimit.integer ) { trap_SendServerCommand( -1, "print \"Red hit the capturelimit.\n\"" ); LogExit( "Capturelimit hit." ); return; } if ( level.teamScores[TEAM_BLUE] >= g_capturelimit.integer ) { trap_SendServerCommand( -1, "print \"Blue hit the capturelimit.\n\"" ); LogExit( "Capturelimit hit." ); return; } } }
/* ================= ConsoleCommand ================= */ qboolean ConsoleCommand( void ) { char cmd[MAX_TOKEN_CHARS]; trap_Argv( 0, cmd, sizeof( cmd ) ); if ( Q_stricmp (cmd, "entitylist") == 0 ) { Svcmd_EntityList_f(); return qtrue; } if ( Q_stricmp (cmd, "forceteam") == 0 ) { Svcmd_ForceTeam_f(); return qtrue; } if (Q_stricmp (cmd, "game_memory") == 0) { Svcmd_GameMem_f(); return qtrue; } if (Q_stricmp (cmd, "addbot") == 0) { Svcmd_AddBot_f(); return qtrue; } if (Q_stricmp (cmd, "botlist") == 0) { Svcmd_BotList_f(); return qtrue; } /* if (Q_stricmp (cmd, "abort_podium") == 0) { Svcmd_AbortPodium_f(); return qtrue; } */ if (Q_stricmp (cmd, "addip") == 0) { Svcmd_AddIP_f(); return qtrue; } if (Q_stricmp (cmd, "removeip") == 0) { Svcmd_RemoveIP_f(); return qtrue; } if (Q_stricmp (cmd, "listip") == 0) { trap_SendConsoleCommand( EXEC_NOW, "g_banIPs\n" ); return qtrue; } if (g_dedicated.integer) { if (Q_stricmp (cmd, "say") == 0) { trap_SendServerCommand( -1, va("print \"server: %s\n\"", ConcatArgs(1) ) ); return qtrue; } // everything else will also be printed as a say command trap_SendServerCommand( -1, va("print \"server: %s\n\"", ConcatArgs(0) ) ); return qtrue; } return qfalse; }
/* ================ G_RankRunFrame ================ */ void G_RankRunFrame() { gentity_t* ent; gentity_t* ent2; grank_status_t old_status; grank_status_t status; int time; int i; int j; if( !trap_RankCheckInit() ) { trap_RankBegin( GR_GAMEKEY ); } trap_RankPoll(); if( trap_RankActive() ) { for( i = 0; i < level.maxclients; i++ ) { ent = &(g_entities[i]); if ( !ent->inuse ) continue; if ( ent->client == NULL ) continue; if ( ent->r.svFlags & SVF_BOT) { // no bots in ranked games trap_SendConsoleCommand( EXEC_INSERT, va("kick %s\n", ent->client->pers.netname) ); continue; } old_status = ent->client->client_status; status = trap_RankUserStatus( i ); if( ent->client->client_status != status ) { // inform client of current status // not needed for client side log in trap_SendServerCommand( i, va("rank_status %i\n",status) ); if ( i == 0 ) { int j = 0; } ent->client->client_status = status; } switch( status ) { case QGR_STATUS_NEW: case QGR_STATUS_SPECTATOR: if( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { ent->client->sess.sessionTeam = TEAM_SPECTATOR; ent->client->sess.spectatorState = SPECTATOR_FREE; ClientSpawn( ent ); // make sure by now CS_GRAND rankingsGameID is ready trap_SendServerCommand( i, va("rank_status %i\n",status) ); trap_SendServerCommand( i, "rank_menu\n" ); } break; case QGR_STATUS_NO_USER: case QGR_STATUS_BAD_PASSWORD: case QGR_STATUS_TIMEOUT: case QGR_STATUS_NO_MEMBERSHIP: case QGR_STATUS_INVALIDUSER: case QGR_STATUS_ERROR: if( (ent->r.svFlags & SVF_BOT) == 0 ) { trap_RankUserReset( ent->s.clientNum ); } break; case QGR_STATUS_ACTIVE: if( (ent->client->sess.sessionTeam == TEAM_SPECTATOR) && (g_gametype.integer < GT_TEAM) ) { SetTeam( ent, "free" ); } if( old_status != QGR_STATUS_ACTIVE ) { // player has just become active for( j = 0; j < level.maxclients; j++ ) { ent2 = &(g_entities[j]); if ( !ent2->inuse ) continue; if ( ent2->client == NULL ) continue; if ( ent2->r.svFlags & SVF_BOT) continue; if( (i != j) && (trap_RankUserStatus( j ) == QGR_STATUS_ACTIVE) ) { trap_RankReportInt( i, j, QGR_KEY_PLAYED_WITH, 1, 0 ); } // send current scores so the player's rank will show // up under the crosshair immediately DeathmatchScoreboardMessage( ent2 ); } } break; default: break; } } // don't let ranked games last forever if( ((g_fraglimit.integer == 0) || (g_fraglimit.integer > 100)) && ((g_timelimit.integer == 0) || (g_timelimit.integer > 1000)) ) { trap_Cvar_Set( "timelimit", "1000" ); } } // tell time to clients so they can show current match rating if( level.intermissiontime == 0 ) { for( i = 0; i < level.maxclients; i++ ) { ent = &(g_entities[i]); if( ent->client == NULL ) { continue; } time = (level.time - ent->client->pers.enterTime) / 1000; ent->client->ps.persistant[PERS_MATCH_TIME] = time; } } }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; // char *headmodel; char userinfo[MAX_INFO_STRING]; int preTeam = 0; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); 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" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "kyle/default"; } Info_SetValueForKey( userinfo, key, model ); /* key = "headmodel"; headmodel = Info_ValueForKey( botinfo, key ); if ( !*headmodel ) { headmodel = model; } Info_SetValueForKey( userinfo, key, headmodel ); key = "team_headmodel"; Info_SetValueForKey( userinfo, key, headmodel ); */ key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "saber1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "single_1"; } Info_SetValueForKey( userinfo, key, s ); key = "saber2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "none"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "personality"); if (!*s ) { Info_SetValueForKey( userinfo, "personality", "botfiles/default.jkb" ); } else { Info_SetValueForKey( userinfo, "personality", s ); } // have the server allocate a client slot clientNum = trap_BotAllocateClient(); 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.\n" ); trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "UNABLE_TO_ADD_BOT"))); return; } // initialize the bot settings if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM ) { if( PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "red"; } } // Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); if (g_gametype.integer >= GT_TEAM) { if (team && Q_stricmp(team, "red") == 0) { bot->client->sess.sessionTeam = TEAM_RED; } else if (team && Q_stricmp(team, "blue") == 0) { bot->client->sess.sessionTeam = TEAM_BLUE; } else { bot->client->sess.sessionTeam = PickTeam( -1 ); } } if (g_gametype.integer == GT_SIEGE) { bot->client->sess.siegeDesiredTeam = bot->client->sess.sessionTeam; bot->client->sess.sessionTeam = TEAM_SPECTATOR; } preTeam = bot->client->sess.sessionTeam; // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if (bot->client->sess.sessionTeam != preTeam) { trap_GetUserinfo(clientNum, userinfo, MAX_INFO_STRING); if (bot->client->sess.sessionTeam == TEAM_SPECTATOR) { bot->client->sess.sessionTeam = preTeam; } if (bot->client->sess.sessionTeam == TEAM_RED) { team = "Red"; } else { if (g_gametype.integer == GT_SIEGE) { if (bot->client->sess.sessionTeam == TEAM_BLUE) { team = "Blue"; } else { team = "s"; } } else { team = "Blue"; } } Info_SetValueForKey( userinfo, "team", team ); trap_SetUserinfo( clientNum, userinfo ); bot->client->ps.persistant[ PERS_TEAM ] = bot->client->sess.sessionTeam; G_ReadSessionData( bot->client ); ClientUserinfoChanged( clientNum ); } if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) { int loners = 0; int doubles = 0; bot->client->sess.duelTeam = 0; G_PowerDuelCount(&loners, &doubles, qtrue); if (!doubles || loners > (doubles/2)) { bot->client->sess.duelTeam = DUELTEAM_DOUBLE; } else { bot->client->sess.duelTeam = DUELTEAM_LONE; } bot->client->sess.sessionTeam = TEAM_SPECTATOR; SetTeam(bot, "s"); } else { if( delay == 0 ) { ClientBegin( clientNum, qfalse ); return; } AddBotToSpawnQueue( clientNum, delay ); } }
/* ============== G_Script_ScriptParse Parses the script for the given entity ============== */ void G_Script_ScriptParse(gentity_t *ent) { char *pScript; qboolean wantName; qboolean inScript; int eventNum; g_script_event_t events[G_MAX_SCRIPT_STACK_ITEMS]; int numEventItems; g_script_event_t *curEvent; char params[MAX_INFO_STRING]; 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)); for (;;) { char *token; 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 // Nico, added "create" & "delete" condition if (!Q_stricmp(action->actionString, "set") || !Q_stricmp(action->actionString, "create") || !Q_stricmp(action->actionString, "delete")) { token = COM_Parse(&pScript); if (token[0] != '{') { COM_ParseError("'{' expected, found: %s.\n", token); } while ((token = COM_Parse(&pScript)) != NULL && (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")) && strlen(token)) { trap_SendServerCommand(-1, va("addToBuild %s\n", token)); } } if ((i == 0 || i == 1) && !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] == '}' && !--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; } }
void UpdateTournamentInfo( void ) { int i = 0, j = 0, k = 0; gentity_t *player = NULL; int playerClientNum; int n; char msg[AWARDS_MSG_LENGTH], msg2[AWARDS_MSG_LENGTH]; int playerRank=level.numNonSpectatorClients-1, highestTiedRank = 0; gentity_t *MVP = NULL; int mvpNum = -1, mvpPoints = 0, winningCaptures = 0, winningPoints = 0; int winningTeam=0; int loseCaptures = 0, losePoints = 0; char *mvpName = ""; gclient_t *cl = NULL; gclient_t *cl2= NULL; int secondPlaceTied=0; memset(msg, 0, AWARDS_MSG_LENGTH); memset(msg2, 0, AWARDS_MSG_LENGTH); player = NULL; // Was there a tie for second place on the podium? cl = &level.clients[level.sortedClients[1]]; cl2= &level.clients[level.sortedClients[2]]; if (cl->ps.persistant[PERS_SCORE] == cl2->ps.persistant[PERS_SCORE]) secondPlaceTied=1; winningTeam = level.clients[0].ps.persistant[PERS_RANK]+1; if ( winningTeam != TEAM_BLUE && winningTeam != TEAM_RED ) {//tie or not a team game winningTeam = 0; } if (mvpNum < 0) {//ah, crap no MVP, pick the first player on the winning team for (i = 0; i < level.maxclients; i++ ) { if ( !&g_entities[i] ) continue; if ( !(&g_entities[i])->client) continue; if ( g_entities[i].client->ps.persistant[PERS_TEAM] == winningTeam ) { mvpNum = i; break; } } } if (mvpNum >= 0) {//still no MVP, so skip it MVP = &g_entities[mvpNum]; mvpName = MVP->client->pers.netname; mvpPoints = MVP->client->ps.persistant[PERS_SCORE]; winningTeam = MVP->client->ps.persistant[PERS_TEAM]; } if ( winningTeam ) {//one of the teams won winningCaptures = level.teamScores[winningTeam]; if (winningTeam == TEAM_RED) loseCaptures = level.teamScores[TEAM_BLUE]; else loseCaptures = level.teamScores[TEAM_RED]; for (i = 0; i < level.maxclients; i++ ) { if ( !&g_entities[i] ) continue; if ( !(&g_entities[i])->client ) continue; if ( g_entities[i].client->ps.persistant[PERS_TEAM] == winningTeam ) winningPoints += g_entities[i].client->ps.persistant[PERS_SCORE]; else losePoints += g_entities[i].client->ps.persistant[PERS_SCORE]; } } for (i = 0; i < level.maxclients; i++ ) { player = &g_entities[i]; if ( !player->inuse || (player->r.svFlags & SVF_BOT)) { continue; } playerClientNum = i; G_Client_CalculateRanks( qfalse ); // put info for the top three players into the msg Com_sprintf(msg, AWARDS_MSG_LENGTH, "awards %d", level.numNonSpectatorClients); for( j = 0; j < level.numNonSpectatorClients; j++ ) { if (j > 2) { break; } n = level.sortedClients[j]; strcpy(msg2, msg); Com_sprintf(msg, AWARDS_MSG_LENGTH, "%s %d", msg2, n); } // put this guy's awards into the msg if ( level.clients[playerClientNum].sess.sessionTeam == TEAM_SPECTATOR ) { strcpy(msg2, msg); Com_sprintf( msg, sizeof(msg), "%s 0", msg2); } // now supply... // // 1) winning team's MVP's name // 2) winning team's MVP's score // 3) winning team's total captures // 4) winning team's total points // 5) this player's rank // 6) the highest rank for which this player tied // 7) losing team's total captures // 8) losing team's total points // 9) if second place was tied // 10) intermission point // 11) intermission angles // for (k = 0; k < level.numNonSpectatorClients; k++) { if (level.sortedClients[k] == playerClientNum) { playerRank = k; break; } } highestTiedRank = 0; for (k = playerRank-1; k >= 0; k--) { cl = &level.clients[level.sortedClients[k]]; if (cl->ps.persistant[PERS_SCORE] > level.clients[level.sortedClients[playerRank]].ps.persistant[PERS_SCORE]) { break; } highestTiedRank = k+1; } strcpy(msg2, msg); Com_sprintf(msg, AWARDS_MSG_LENGTH, "%s \"%s\" %d %d %d %d %d %d %d %d %f %f %f %f %f %f", msg2, mvpName, mvpPoints, winningCaptures, winningPoints, playerRank, highestTiedRank, loseCaptures, losePoints, secondPlaceTied, level.intermission_origin[0], level.intermission_origin[1], level.intermission_origin[2], level.intermission_angle[0], level.intermission_angle[1], level.intermission_angle[2]); trap_SendServerCommand(player-g_entities, msg); } if (g_gametype.integer == GT_SINGLE_PLAYER) { Com_sprintf( msg, sizeof(msg), "postgame %i", playerRank); trap_SendConsoleCommand( EXEC_APPEND, msg); } }